編輯:關於Android編程
一、背景
最近做項目需要用到選擇圖片上傳,類似於微信、微博那樣的圖片選擇器,ContentResolver讀取本地圖片資源並用RecyclerView+Glide加載圖片顯示就搞定列表的顯示,這個沒什麼大問題,重點是,點擊圖片進入大圖浏覽,比如你相冊有幾百張圖片,也就意味著在ViewPager中需要加載幾百個view,況且手機拍出來的圖片都是1-2千萬左右像素的高清大圖(筆者手機2千萬像素 也就是拍照出來的照片3888*5152),大小也有5-7個兆,ViewPager滑動不了十幾張就oom了,即是對圖片做了壓縮處理,把圖片分辨率降低至1366*960,大小壓縮至150k以下,並且在ViewPager的destroyItem方法做了bitmap資源的回收,雖然效果好了點,這也抵擋不了oom的降臨(網上查找的方案都是壓縮、使用第三方控件、回收,其實這都沒用,可能他們沒有真正體驗過ViewPager加載幾百上千張大圖的感受),浏覽到了第50-70張的時候就oom了 內存一直暴漲,根本回收不了的,不信你們試試,壓縮和回收根本不能根治問題,那麼怎麼解決呢?研究了微信和微博,他們怎麼也不會oom,最後我想到了一種解決方案。
二、方案實施
1、以往的普通做法
部分代碼:
List<SubsamplingScaleImageView> mViews = new ArrayList<>(); int size = mDatas.size(); for (int i = 0; i < size; i++) { SubsamplingScaleImageView view = new SubsamplingScaleImageView(this); mViews.add(view); } mBinding.viewpager.setAdapter(new MyAdapter());
class MyAdapter extends PagerAdapter { @Override public int getCount() { return mDatas.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, final int position) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewPager.LayoutParams.MATCH_PARENT,ViewPager.LayoutParams.MATCH_PARENT); final SubsamplingScaleImageView imageView = mViews.get(position); imageView.setLayoutParams(params); final String url = mDatas.get(position); String cacheExists = cacheExists(url); if(TextUtils.isEmpty(cacheExists)) {//沒緩存 需要壓縮(壓縮耗時 異步) new AsyncTask<Void, Void, String>() { @Override protected String doInBackground(Void... voids) { String cacheNoExistsPath = getCacheNoExistsPath(url); BitmapCompressUtils.compressBitmap(url, cacheNoExistsPath); File file = new File(cacheNoExistsPath); if (file.exists()) {//存在表示成功 return cacheNoExistsPath; } else { return url; } } @Override protected void onPostExecute(String s) { imageView.setImage(ImageSource.uri(s)); } }.execute(); } else {//有緩存 直接顯示 imageView.setImage(ImageSource.uri(cacheExists)); } container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { SubsamplingScaleImageView imageView = mViews.get(position); if(imageView != null) { imageView.recycle(); } container.removeView(imageView); } }
/** * 判斷當前圖片url對應的壓縮過的緩存是否存在 ""表示不存在 * * @param url 圖片路徑 * @return */ private String cacheExists(String url) { try { File fileDir = new File(mCacheRootPath); if(!fileDir.exists()) { fileDir.mkdirs(); } File file = new File(mCacheRootPath,new StringBuffer().append(MD5EncryptorUtils.md5Encryption(url)).toString()); if(file.exists()) { return file.getAbsolutePath(); } } catch (Exception e) { e.printStackTrace(); } return ""; } public String getCacheNoExistsPath(String url) { File fileDir = new File(mCacheRootPath); if(!fileDir.exists()) { fileDir.mkdirs(); } return new StringBuffer().append(mCacheRootPath) .append(MD5EncryptorUtils.md5Encryption(url)).toString(); }
可以看到,這裡筆者通過自己的壓縮算法(上一篇文章Android_NDK圖片壓縮之Libjpeg庫使用 )做了圖片壓縮,並緩存,細心的朋友應該有發現mViews集合添加的view個數是mDatas的size大小個數,這樣就會導致一個問題ViewPager一直向下滑動的時候,內存一直是增加的,即是做了資源回收,也是不能解決問題(況且筆者這裡展示圖片的控件是SubsamplingScaleImageView 很不錯的大圖局部加載控件 能有效防止oom),大家可以試試,大量圖片的時候還是會oom,這得歸根於viewpager加載的圖片數量問題。
2、解決方案:
圖片壓縮也做了,資源回收也做了,但是ViewPager加載越來越多圖片的時候就會oom 你避免不了,不信你試試;
這裡就要用到ViewPager的view的重用機制(自己理解的),也就是mViews我們固定給定個數量,如4,這樣ViewPager的i實際所需要的item也就只有4個。
修改後的部分代碼:
for (int i = 0; i < 4; i++) { SubsamplingScaleImageView view = new SubsamplingScaleImageView(this); mViews.add(view); } mBinding.viewpager.setAdapter(new MyAdapter());
class MyAdapter extends PagerAdapter { @Override public int getCount() { return mDatas.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, final int position) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewPager.LayoutParams.MATCH_PARENT,ViewPager.LayoutParams.MATCH_PARENT); int i = position % 4; final SubsamplingScaleImageView imageView = mViews.get(i); imageView.setLayoutParams(params); final String url = mDatas.get(position); String cacheExists = cacheExists(url); if(TextUtils.isEmpty(cacheExists)) {//沒緩存 需要壓縮(壓縮耗時 異步) new AsyncTask<Void, Void, String>() { @Override protected String doInBackground(Void... voids) { String cacheNoExistsPath = getCacheNoExistsPath(url); BitmapCompressUtils.compressBitmap(url, cacheNoExistsPath); File file = new File(cacheNoExistsPath); if (file.exists()) {//存在表示成功 return cacheNoExistsPath; } else { return url; } } @Override protected void onPostExecute(String s) { imageView.setImage(ImageSource.uri(s)); } }.execute(); } else {//有緩存 直接顯示 imageView.setImage(ImageSource.uri(cacheExists)); } container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { int i = position % 4; SubsamplingScaleImageView imageView = mViews.get(i); if(imageView != null) { imageView.recycle(); } container.removeView(imageView); }
很簡單的修改 就能有效防止oom 利用position%4拿到第幾個控件從mViews取值,保證了viewpager加載的mViews存儲的圖片為4個
看一直向下滑動的內存走勢圖
內存基本維持穩定
三、demo演示
因為要讀取相冊就沒再模擬器運行錄制gif ,直接截圖
demo下載:demo
四、總結
這個只是簡單的演示,實際項目中的相冊比這個復雜多了,簡單說就是要壓縮,要回收,View重用。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
每當viewpager上一個可見或依附的頁面發生了滾動事件就會調用PageTransformer,這讓應用可以使用自定義transformation讓viewpager某
Android性能優化是Android開發中經常遇見的一個問題,接下來將對Android性能優化方面的知識點做一個簡單的梳理和總結,將從工具和代碼兩方面進行梳理。所謂工欲
如果直接在TableVIewController上貼Button的話會導致這個會隨之滾動,下面解決在TableView上實現位置固定懸浮按鈕的兩種方法: 1.在view
public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode t