因为项目有特殊的需求,从本地代码加载资源生成Bitmap,然后渲染到RecyclerView上,由于加载资源部分是耗时操作,需要在异步执行,在资源加载过程中,很容易因为资源多且大导致oom,因此考虑引入Glide,加资源加载部分新增本地资源加载的途径。
说白了就是要替换网络加载组件,为了不影响网络加载组件,需要在 ModelLoader 根据uri来做判断。
步骤:
1.创建数据获取类继承DataFetcher:
此时可以仿照HttpUrlFetcher:
public class MineFether implements DataFetcher<InputStream> { private static final int MAXIMUM_REDIRECTS = 5; private final static String TAG = "MineFether"; private GlideUrl glideUrl; private Context context; public static final String Uri_type = "mine:"; private static final String TAG_FILE = "file"; private static final String TAG_INDEX = "index"; private Bitmap bitmap; private ByteArrayInputStream byteArrayInputStream; private volatile boolean isCancelled; private ByteArrayOutputStream baos; private String tempFilePath; private Uri tempFileUri; public static String getUrl(String filePath, int index) { //这里这么写的原因是因为需要与Http请求区分开来,自己定义自己的Uri字符串类型, //另外这里是以json格式编写的,那么在loadData中还需要将得到的Uri通过同样的方式解析出 //加载数据时需要的数据,例如:文件路径,文件索引等等。 String urlFormat = "%s{\"%s\":\"%s\",\"%s\":\"%d\"}"; String url = String.format(urlFormat, Uri_type, TAG_FILE, filePath, TAG_INDEX, index); return url; } public MineFether(GlideUrl glideUrl, Context context) { this.glideUrl = glideUrl; this.context = context; } @Override public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { String str = glideUrl.getCacheKey(); long startTime = LogTime.getLogTime(); try { String json = str.substring(str.indexOf(":") + 1); JSONObject jsonObject = new JSONObject(json); String filePath = jsonObject.getString(TAG_FILE); int index = jsonObject.getInt(TAG_PAGEINDEX); InputStream result; if (filePath.startsWith(ContentResolver.SCHEME_CONTENT)) { result = loadDataWithRedirects(Uri.parse(filePath), pageIndex, width, showMode, drawAnnot == 1, 0); } else { result = loadDataWithRedirects(filePath, pageIndex, width, showMode, drawAnnot == 1, 0); } callback.onDataReady(result); } catch (JSONException e) { callback.onLoadFailed(e); } catch (Exception e) { callback.onLoadFailed(e); } finally { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)); } } } private InputStream loadDataWithRedirects(String filePath, int index, int redirects) throws Exception { if (redirects >= MAXIMUM_REDIRECTS) { throw new Exception("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!"); } //这里去获取Bitmap,当然也可以直接传入Inputstream,而且最终的目的还是要返回InputStream if (bitmap == null || bitmap.isRecycled()) { return loadDataWithRedirects(filePath, pageIndex, width, showMode, isDrawAnnot, redirects + 1); } baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); if (baos.size() <= 0) { cleanup(); return loadDataWithRedirects(filePath, pageIndex, width, showMode, isDrawAnnot, redirects + 1); } byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray()); if (isCancelled) { return null; } return byteArrayInputStream; } @Override public void cleanup() { if (byteArrayInputStream != null) { try { byteArrayInputStream.close(); } catch (IOException e) { // Ignore } } if (baos != null) { try { baos.close(); } catch (IOException e) { // Ignore } } if (bitmap != null) { if (!bitmap.isRecycled()) { bitmap.recycle(); } bitmap = null; } } @NonNull @Override public Class<InputStream> getDataClass() { return InputStream.class; } @NonNull @Override public DataSource getDataSource() { return DataSource.REMOTE; } @Override public void cancel() { isCancelled = true; } }2.创建模组加载类继承ModelLoader:
可以仿照HttpGlideUrlLoader
public class MineGlideUrlLoad implements ModelLoader<GlideUrl, InputStream> { public static final Option<Integer> TIMEOUT = Option.memory( "com.bumptech.glide.load.model.stream.HttpGlideUrlLoader.Timeout", 2500); private final ModelCache<GlideUrl, GlideUrl> modelCache; private Context context; public MineGlideUrlLoad(){ this(null, null); } public MineGlideUrlLoad(ModelCache<GlideUrl, GlideUrl> modelCache, Context context) { this.modelCache = modelCache; this.context = context; } @Nullable @Override public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height, @NonNull Options options) { GlideUrl url = model; if (modelCache != null) { url = modelCache.get(model, 0, 0); if (url == null) { modelCache.put(model, 0, 0, model); url = model; } } int timeout = options.get(TIMEOUT); DataFetcher dataFetcher; if(url.getCacheKey().startsWith(MineFether.Uri_type)){ dataFetcher = new MineFether(url, context); } else{ dataFetcher = new HttpUrlFetcher(url, timeout); } return new LoadData<>(url, dataFetcher); } @Override public boolean handles(@NonNull GlideUrl glideUrl) { return true; } /** * The default factory for {@link MineGlideUrlLoad}s. */ public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> { private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<>(500); private Context context; public Factory(Context context){ this.context = context; } @NonNull @Override public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) { return new MineGlideUrlLoad(modelCache, context); } @Override public void teardown() { // Do nothing. } } }3.注册修改好的资源加载组件:
由于使用的是InputStream.class来加载资源,而Glide本身有这个组件,因此需要将此组件替换。
@GlideModule public class MyAppGlideModule extends AppGlideModule { @Override public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) { /*内存缓存池*/ MemorySizeCalculator cache_calculator = new MemorySizeCalculator.Builder(context) .setMemoryCacheScreens(2) .build(); builder.setMemoryCache(new LruResourceCache(cache_calculator.getMemoryCacheSize())); /*磁盘缓存*/ int diskCacheSizeBytes = 1024 * 1024 * 100;//100 MB builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes)); /*Bitmap 池*/ MemorySizeCalculator bitmap_calculator = new MemorySizeCalculator.Builder(context) .setBitmapPoolScreens(3) .build(); builder.setBitmapPool(new LruBitmapPool(bitmap_calculator.getBitmapPoolSize())); builder.setDefaultRequestOptions( new RequestOptions() .format(DecodeFormat.PREFER_RGB_565) .disallowHardwareConfig()); } @Override public boolean isManifestParsingEnabled() { return false; } @Override public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { registry.replace(GlideUrl.class, InputStream.class, new MineGlideUrlLoad.Factory(context)); super.registerComponents(context, glide, registry); } }4.使用很简单:
GlideApp.with(context) .load(new GlideUrl(MineFether.getUrl(...)) .into(imageView);