詳解Android_性能優(yōu)化之ViewPager加載成百上千高清大圖oom解決方案
一、背景
最近做項(xiàng)目需要用到選擇圖片上傳,類似于微信、微博那樣的圖片選擇器,ContentResolver讀取本地圖片資源并用RecyclerView+Glide加載圖片顯示就搞定列表的顯示,這個沒什么大問題,重點(diǎn)是,點(diǎn)擊圖片進(jìn)入大圖瀏覽,比如你相冊有幾百張圖片,也就意味著在ViewPager中需要加載幾百個view,況且手機(jī)拍出來的圖片都是1-2千萬左右像素的高清大圖(筆者手機(jī)2千萬像素 也就是拍照出來的照片3888*5152),大小也有5-7個兆,ViewPager滑動不了十幾張就oom了,即是對圖片做了壓縮處理,把圖片分辨率降低至1366*960,大小壓縮至150k以下,并且在ViewPager的destroyItem方法做了bitmap資源的回收,雖然效果好了點(diǎn),這也抵擋不了oom的降臨(網(wǎng)上查找的方案都是壓縮、使用第三方控件、回收,其實(shí)這都沒用,可能他們沒有真正體驗(yàn)過ViewPager加載幾百上千張大圖的感受),瀏覽到了第50-70張的時候就oom了 內(nèi)存一直暴漲,根本回收不了的,不信你們試試,壓縮和回收根本不能根治問題,那么怎么解決呢?研究了微信和微博,他們怎么也不會oom,最后我想到了一種解決方案。
二、方案實(shí)施
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); } }
/** * 判斷當(dāng)前圖片url對應(yīng)的壓縮過的緩存是否存在 ""表示不存在 * * @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庫使用 )做了圖片壓縮,并緩存,細(xì)心的朋友應(yīng)該有發(fā)現(xiàn)mViews集合添加的view個數(shù)是mDatas的size大小個數(shù),這樣就會導(dǎo)致一個問題ViewPager一直向下滑動的時候,內(nèi)存一直是增加的,即是做了資源回收,也是不能解決問題(況且筆者這里展示圖片的控件是SubsamplingScaleImageView 很不錯的大圖局部加載控件 能有效防止oom),大家可以試試,大量圖片的時候還是會oom,這得歸根于viewpager加載的圖片數(shù)量問題。
2、解決方案:
圖片壓縮也做了,資源回收也做了,但是ViewPager加載越來越多圖片的時候就會oom 你避免不了,不信你試試;
這里就要用到ViewPager的view的重用機(jī)制(自己理解的),也就是mViews我們固定給定個數(shù)量,如4,這樣ViewPager的i實(shí)際所需要的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個
看一直向下滑動的內(nèi)存走勢圖
內(nèi)存基本維持穩(wěn)定
三、demo演示
因?yàn)橐x取相冊就沒再模擬器運(yùn)行錄制gif ,直接截圖
demo下載:demo
四、總結(jié)
這個只是簡單的演示,實(shí)際項(xiàng)目中的相冊比這個復(fù)雜多了,簡單說就是要壓縮,要回收,View重用。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于flutter?sound插件實(shí)現(xiàn)錄音與播放功能
這篇文章主要介紹了基于flutter?sound插件實(shí)現(xiàn)錄音與播放功能,介紹了如何錄音,如何播放本地和遠(yuǎn)程音頻文件,以及如何實(shí)現(xiàn)動畫,在錄制完音頻文件后如何上傳,這些都是我們平常使用這個功能會遇到的問題。在使用的過程中遇到的問題也有列出,需要的朋友可以參考下2022-05-05Android學(xué)習(xí)之Span的使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android中各種Span類的使用方法,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Android有一定的幫助,需要的可以參考一下2022-06-06Android獲取手機(jī)系統(tǒng)版本等信息的方法
這篇文章主要介紹了Android獲取手機(jī)系統(tǒng)版本等信息的方法,涉及Android獲取手機(jī)版本中各種常見信息的技巧,非常具有實(shí)用價值,需要的朋友可以參考下2015-04-04基于Android SQLiteOpenHelper && CRUD 的使用
本篇文章小編為大家介紹,基于Android SQLiteOpenHelper && CRUD的使用。需要的朋友可以參考一下2013-04-04詳解Android中實(shí)現(xiàn)ListView左右滑動刪除條目的方法
這篇文章主要介紹了Android中實(shí)現(xiàn)ListView左右滑動刪除條目的方法,文中分別展示了通過Scroller和NineOldAndroids來實(shí)現(xiàn)的例子,需要的朋友可以參考下2016-04-04android:layout_gravity和android:gravity的區(qū)別
本篇文章主要介紹了android中g(shù)iavity和layout_gravity的區(qū)別。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04