Open fred-ye opened 9 years ago
首先直接看这么一段代码:
public class TestViewPagerActivity extends Activity { private ViewPager viewPager; private ImageView imageView; private int res[] = {R.drawable.image_1, R.drawable.image_2, R.drawable.image_3, R.drawable.image_4}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_view_pager); viewPager = (ViewPager) findViewById(R.id.viewpager); viewPager.setAdapter(adapter); } private PagerAdapter adapter = new PagerAdapter() { @Override public int getCount() { return 4; } @Override public boolean isViewFromObject(View view, Object o) { return view == o; } @Override public Object instantiateItem(ViewGroup container, int position) { LayoutInflater inflater = (LayoutInflater) container.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.view_pager_item, null); imageView = (ImageView) view.findViewById(R.id.image); imageView.setImageResource(res[position]); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } }; }
activity_test_view_pager.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context="com.example.fred.testas.TestViewPagerActivity"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </RelativeLayout>
view_pager_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
在我们的项目中,有一个功能就是采用这种方式来做的。但是从收集到的Crash report中反现这地方出现过几次Crash,于是想着看能不能优化这一块。于是我便采有Android Studio中的Memory Monitor工具进行分析。当采用上面的代码跑在Device上时,App起动时Memory Monitor上显示 Allocated(20.92M), Free(4.15M), 几次滑动后,发现迅速的增长到Allocated(23.25M), Free(15.47M)。接下来再不断的滑动图片,内存的占用会不断的增长,但增长很缓慢。 试着进行如下优化:
1.从网上查询得知当我们在使用imageView.setBackgroundResource,imageView.setImageResource, 或者 BitmapFactory.decodeResource 这样的方法来设置图片,这些方法最终都是通过java层的createBitmap来完成,会消耗更多内存。 改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。 此时,我将instantiateItem方法的实现改为:
instantiateItem
public Object instantiateItem(ViewGroup container, int position) { LayoutInflater inflater = (LayoutInflater) container.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.view_pager_item, null); imageView = (ImageView) view.findViewById(R.id.image); InputStream is = getResources().openRawResource(res[position]); BitmapFactory.Options options = new BitmapFactory.Options(); //修改图片属性 options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap btp = BitmapFactory.decodeStream(is, null, options); imageView.setImageBitmap(btp); container.addView(view); return view; }
发现,App启动时Memory Monitor显示 Allocated(16.06M), Free(9.01M)。不断滑动,发现内存也增长的比较快,大概每滑动两次,会增长0.15M左右。当Memory Monitor上显示 Allocated(25.12M), Free(0.13M)时,再滑动一次,发现GC触发了,内存回收。于是,显示Allocated(16.01M), Free(11.03M)。整体上看这种方式占用的内存是要少一些。看来这种方式是有效的。
针对内存不断上升的这种情况,看能不能从destroyItem方法上找找思路,于是想着在destroyItem方法中强制回收Bitmap,进行如下代码改动。
destroyItem
public void destroyItem(ViewGroup container, int position, Object object) { View view = (View)object; imageView = (ImageView) view.findViewById(R.id.image); BitmapDrawable bitmapDrawable = ((BitmapDrawable)imageView.getDrawable()); if (bitmapDrawable != null && bitmapDrawable.getBitmap() != null) { bitmapDrawable.getBitmap().recycle(); } container.removeView((View) object); }
结果发现,效果并不明显,和没有改动之前的效果基本上是一样的。此处暂且放一下,等会再说。
2.另外一种思路是这样,首先就先需要用到的Bitmap全部加载进来,这样就会保证操作的只会是这么几个bitmap.
public class TestViewPagerActivity extends Activity { private ViewPager viewPager; private ImageView imageView; private int res[] = {R.drawable.image_1, R.drawable.image_2, R.drawable.image_3, R.drawable.image_4}; private List<Bitmap> bitmapList = new ArrayList<Bitmap>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_view_pager); viewPager = (ViewPager) findViewById(R.id.viewpager); viewPager.setAdapter(adapter); for (int resId : res) { InputStream is = getResources().openRawResource(resId); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap btp = BitmapFactory.decodeStream(is, null, options); bitmapList.add(btp); } } private PagerAdapter adapter = new PagerAdapter() { @Override public int getCount() { return 4; } @Override public boolean isViewFromObject(View view, Object o) { return view == o; } @Override public Object instantiateItem(ViewGroup container, int position) { LayoutInflater inflater = (LayoutInflater) container.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.view_pager_item, null); imageView = (ImageView) view.findViewById(R.id.image); imageView.setImageBitmap(bitmapList.get(position)); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } }; }
程序一启动时, Allocated(16.35M), Free(8.72M). 左右滑动,每滑动两次,内存增长0.01M。整体感觉第二种思路内存的消耗是要小一些。
关于图片性能处理的总结可以看这里
ViewPager中图片显示的优化
首先直接看这么一段代码:
Activity
布局文件
activity_test_view_pager.xml
view_pager_item.xml
在我们的项目中,有一个功能就是采用这种方式来做的。但是从收集到的Crash report中反现这地方出现过几次Crash,于是想着看能不能优化这一块。于是我便采有Android Studio中的Memory Monitor工具进行分析。当采用上面的代码跑在Device上时,App起动时Memory Monitor上显示 Allocated(20.92M), Free(4.15M), 几次滑动后,发现迅速的增长到Allocated(23.25M), Free(15.47M)。接下来再不断的滑动图片,内存的占用会不断的增长,但增长很缓慢。 试着进行如下优化:
1.从网上查询得知当我们在使用imageView.setBackgroundResource,imageView.setImageResource, 或者 BitmapFactory.decodeResource 这样的方法来设置图片,这些方法最终都是通过java层的createBitmap来完成,会消耗更多内存。 改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。 此时,我将
instantiateItem
方法的实现改为:发现,App启动时Memory Monitor显示 Allocated(16.06M), Free(9.01M)。不断滑动,发现内存也增长的比较快,大概每滑动两次,会增长0.15M左右。当Memory Monitor上显示 Allocated(25.12M), Free(0.13M)时,再滑动一次,发现GC触发了,内存回收。于是,显示Allocated(16.01M), Free(11.03M)。整体上看这种方式占用的内存是要少一些。看来这种方式是有效的。
针对内存不断上升的这种情况,看能不能从
destroyItem
方法上找找思路,于是想着在destroyItem
方法中强制回收Bitmap,进行如下代码改动。结果发现,效果并不明显,和没有改动之前的效果基本上是一样的。此处暂且放一下,等会再说。
2.另外一种思路是这样,首先就先需要用到的Bitmap全部加载进来,这样就会保证操作的只会是这么几个bitmap.
程序一启动时, Allocated(16.35M), Free(8.72M). 左右滑动,每滑动两次,内存增长0.01M。整体感觉第二种思路内存的消耗是要小一些。
关于图片性能处理的总结可以看这里