ViewPager的封装三:内存优化

    xiaoxiao2021-03-25  66

    1、Handler不销毁

    Handler可能出现内存泄漏问题(Activity的生命周期没有Handler的生命周期大),如果Handler不销毁,那么Handler就会一直执行,activity就不会销毁,可以在startScroll()方法中添加Log,然后退出当前Activity,查看该Log是否一直在打印。

    解决思路:当Activity退出时,会调用onDetachedFromWindow()方法,我们在这里将Handler移除即可。

    @Override protected void onDetachedFromWindow() { mHandler.removeMessages(SCROLL_MSG); mHandler = null; super.onDetachedFromWindow(); }

    在ViewPager源码也是这么做的:

    @Override protected void onDetachedFromWindow() { removeCallbacks(mEndScrollRunnable); // To be on the safe side, abort the scroller if ((mScroller != null) && !mScroller.isFinished()) { mScroller.abortAnimation(); } super.onDetachedFromWindow(); }

    2、界面复用

    mBannerView.setAdapter(new BannerAdapter() { @Override public View getView(int position) { String url = result.get(position).getBanner_url().getUrl_list().get(0).getUrl(); ImageView iv = new ImageView(MainActivity.this); Glide.with(MainActivity.this).load(url).placeholder(R.mipmap.ic_launcher).into(iv); return iv; } @Override public int getCount() { Log.e("TAg", "getCount()" + result.size()); return result.size(); } });

    如上代码,每次getView(),不管三七二十一,都会new ImageView,这肯定不是行的,既然用到了Adapter模式,我们也可以采用BaseAdapter.getView()方法那样,提供一个convertView的参数,当该参数为空时,才去创建ImageView。避免了创建不必要的对象。

    修改后的BannerAdapter.getView()方法:

    public abstract View getView(View convertView, int position);

    BannerViewPager中怎么处理呢?修改的自然是PagerAdapter中实例化和销毁Item这两处进行修改了:

    @Override public Object instantiateItem(ViewGroup container, int position) { View bannerItemView = mAdapter.getView(getConvertView(), position % mAdapter.getCount()); container.addView(bannerItemView); //container 就是ViewPager return bannerItemView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); mConvertViews.add((View) object); }

    instantiateItem()方法中,在getView()中添加参数,用于获取convertView,目前先不管它是如何实现的。再来看下,destroyItem()方法,这里将要移除的View添加到了mConvertViews的集合中,还是不懂,直接把这个待销毁的object,传给convertView不就行了吗?当然不行,下面是直接复用View的异常信息:

    java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:4417) at android.view.ViewGroup.addView(ViewGroup.java:4258) at android.support.v4.view.ViewPager.addView(ViewPager.java:1509) at android.view.ViewGroup.addView(ViewGroup.java:4198) at android.view.ViewGroup.addView(ViewGroup.java:4171)

    所以,我们不能直接复用销毁的View,需要先将它们放到一个集合中,然后判断一下,只有这个View的父View为空时,才返回这个View,否则创建新的ItemView。

    看来,获取复用View的getConvertViews()方法:

    private View getConvertView() { for (int i = 0; i < mConvertViews.size(); i++) { // 获取没有添加在ViewPager里面的View, // 如果没有被父View持有,则复用该View if (mConvertViews.get(i).getParent() == null) { return mConvertViews.get(i); } } return null; }

    添加convertView参数后的getView()方法:

    @Override public View getView(View convertView, int position) { String url = result.get(position).getBanner_url().getUrl_list().get(0).getUrl(); ImageView iv = null; if (convertView == null) { iv = new ImageView(BannerActivity.this); } else { iv = (ImageView) convertView; } Glide.with(MainActivity.this).load(url).placeholder(R.mipmap.ic_launcher).into(iv); return iv; }

    3、Activity生命周期管理

    这里是自定义轮播组件,那么就需要考虑到它的轮播时机,假如已经切换到其他界面了,那么也就没必要继续轮播了。

    private Application.ActivityLifecycleCallbacks mActivityLifecycleCallbacks = new SimpleActivityLifecycleCallback() { @Override public void onActivityResumed(Activity activity) { if (activity == mActivity) { startScroll(); } } @Override public void onActivityPaused(Activity activity) { if (activity == mActivity) { mHandler.removeMessages(SCROLL_MSG); } } };

    SimpleActivityLifecycleCallback是我们对Application.ActivityLifecycleCallbacks的默认实现,这样就没必要重写那么多不必要的接口了。 我们只管理当前Activity,所以要判断一下,只有是当前Activity时,才进行处理。

    注册:

    public void setAdapter(BannerAdapter adapter) { this.mAdapter = adapter; super.setAdapter(new BannerPagerAdapter()); mActivity.getApplication().registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks); }

    反注册:

    @Override protected void onDetachedFromWindow() { mHandler.removeMessages(SCROLL_MSG); mHandler = null; mActivity.getApplication().unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks); super.onDetachedFromWindow(); }

    4、结尾

    用了四篇小文来记录BannerView的开发过程,从泛读ViewPager源码,到BannerView从里到外封装,添加自定义属性,最后的内存优化,这些只是开发的套路,里面还有一些接口没实现,也存在内存优化的空间。

    5、补充一点

    这个自定义轮播图,刚进入界面,是无法左滑切换的,我们修复一下:

    BannerViewPager.setAdapter() public void setAdapter(BannerAdapter adapter) { setCurrentItem(Integer.MAX_VALUE >> 2 * mAdapter.getCount()); }

    如何获取代码?

    Git clone https://github.com/droid4j/anKataLite.git

    本篇对应的标签v0.6 git checkout v0.6

    转载请注明原文地址: https://ju.6miu.com/read-39327.html

    最新回复(0)