Android使用ViewPager实现滚动广告解决OOM小技巧

现在基本每个Android App都会标配一个启动介绍的页面,或做产品介绍,或做app功能展示,既然起导航界面,基本思路就是用ViewPager实现。(图片引自UI中国一设计师的设计图片) 
这里写图片描述 
正好这几天在做这个导航界面,我的测试手机是魅族MX3,做完之后测试还行,没有明显卡顿的现象,但是当我把debug的apk装到米3和魅蓝上时,程序运行到这个导航界面会马上crash掉,偶尔没有crash也会出现明显的卡顿现象,体验效果非常差。 
打开Android Studio的内存管理器查看运行时分配内存,当时我就蒙逼了,180+M左右,就这个导航界面用了180M,还做什么安卓。。。 
改了很多地方,效果依旧不明显,查阅资料有说另外写一个Adapter继承自PagerAdapter,方法如下:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">class GuideAdapter extends PagerAdapter{
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> List<View> views;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> final LinkedList<View> recyleBin=<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> LinkedList<>();
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">GuideAdapter</span>(List<View>views){
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.views=views;
        }
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getCount</span>() {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> views.size();
        }   
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> boolean <span class="hljs-title" style="box-sizing: border-box;">isViewFromObject</span>(View view, Object <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">object</span>) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> view==<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">object</span>;
        }   
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">destroyItem</span>(ViewGroup container, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position, Object <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">object</span>) {
            container.removeView(views.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(position));
        }   
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getItemPosition</span>(Object <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">object</span>) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> super.getItemPosition(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">object</span>);
        }   
        @Override
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Object <span class="hljs-title" style="box-sizing: border-box;">instantiateItem</span>(ViewGroup container, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> position) {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//在此设置背景图片,提高加载速度,解决OOM问题</span>
            View view;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> count=getCount();
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(!recyleBin.isEmpty()) {
                view=recyleBin.pop();
            }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
                view=views.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(position);
                ViewGroup.LayoutParams <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">params</span> = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT);
                view.setBackgroundResource(images[position % count]);
                view.setLayoutParams(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">params</span>);
            }
            container.addView(view,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> views.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(position);
        }
}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li></ul>

初始化的时候让viewpager的adapter为上面的自定义的adapter即可,基本代码如下:

<code class="hljs r has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">private ViewPager mViewPager;
    private GuideAdapter mGuideAdapter;
    private int[] images;//显示介绍的图片的id值
    private ArrayList<View>views;
    private ImageView[] indicator=null;//下面的导航指示器,此处不做过多介绍,可忽略

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>
    //控件初始化业务
    mViewPager=(ViewPager)findViewById(R.id.viewpager);
    views=new ArrayList<>();
    //此处放入五张介绍的图片的id值
    images=new int[]{R.drawable.bg_1,R.drawable.bg_2,R.drawable.bg_3,R.drawable.bg_4,R.drawable.bg_5}
    //adapter实例化
    mGuideAdapter=new GuideAdapter(views);
    //循环加入图片的业务
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>(int i=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;i<images.length;i++){
        ImageView mImageView=new ImageView(this);
    //下面这一步会导致OOM,所以添加backgroundResource的步骤在自定义的adapter的
    //instantiateItem方法里面实现,此处已注释
    //mImageView.setBackgroundResource(images[i]);
    views.add(mImageView);//添加入动态数组里面,此处的ImageView里面均为没有背景的imageview
    //<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span>循环内还有指示器的添加,因不在讨论问题的重点内,忽略业务代码
    }
    //为mViewPager绑定适配器
    mViewPager.setAdapter(mGuideAdapter);
    mViewPager.setCurrentItem(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);
    mViewPager.setOffscreenPageLimit(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
    mViewPager.setOnPageChangeListener(this);
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul>

基本的思路就是让加载viewpager过多的没有显示的图片在自定义的Adapter的destroyItem里面销毁释放内存,防止造成OOM或者内存溢出crash的问题,这种方法每次加载viewpager的时候只会加载当前一页和前后两页共三页,所以内存占用不会很高。

但是,但是,问题来了。。。重新编写之后测试发现:te me还是占用160M+内存,当时我就傻了。。。怎么不见效果呢。。。于是好长时间都在查资料,找问题,debug。。。 
然而,半天过去了。。。 
我还是没找到问题到底出在哪里。。。 
后来,导师和我说看一下你的图片多少大,我说100k的样子,然后他看了一下,说让我把所有图片拖到PS里面转成PNG再导出来(之前一直都是JPG)。于是照做了,顺便看了一眼新导出的图片大小,看了一下都比JPG的要大,于是半信半疑的替换资源,重新编译运行后。终于惊喜的发现内存占用降到40-60M之间了。。。原来是图片格式的问题,欸,搞了半天头都大了,原来是这么简单的问题。。。 
这也横向提示我们以后Android编程里面的资源最好不要用JPG格式的,下载类的图片资源也是,因为32位的PNG颜色过渡平滑且支持透明,JPG是像素化压缩过的图片,质量已经下降了,PNG的压缩算法解压快很多,JPG的话可以有很高的压缩比(当然会有损失),所以综合考虑还是用PNG比较好(特殊需求除外)。

在此也感谢我的导师,在我开发当中教我好多,第一次写博客,即当作自己的记事本,日后还能看看,也希望能解决大家相似的问题,第一次写,一定会有表达不到位,表述不清楚的地方,希望留下你的评论和建议,我也可以改正,提高自己。

猜你喜欢

转载自blog.csdn.net/qinbin2015/article/details/46514787