listview、gridview上滑加载更多图片,内存溢出outofmemery的解决方案

来源:互联网 发布:samba源码下载 编辑:程序博客网 时间:2024/06/01 20:11

有很多人可能会跟我一样,有这样一个功能需求,(新闻或者商城)后台提供上滑加载更多的分页查询接口,每次上滑就需要加载更多的图片,当页数超过一定数量之后,手机就会卡顿,紧接着直接退出程序,打开logcat面板,提示的异常信息就是outofmemery,但是他不会告诉你问题出在哪行代码上。

该页面功能详细介绍:下拉刷新,上滑加载更多商品,商品图片和名称定高定宽。

我使用的框架:网络请求rxvolley、下拉和上滑UI使用xrefreshview、Glide图片加载。

错误的方案:由于商品高度固定,因此使用固定高度gridview,但是这样就不能滑动了,解决外部套用scrollview。

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.axin.testimgs.MainActivity">    <com.andview.refreshview.XRefreshView        android:id="@+id/refresh_main"        android:layout_width="match_parent"        android:layout_height="match_parent">        <com.andview.refreshview.XScrollView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="vertical">            <com.axin.testimgs.MyGridView                android:id="@+id/grid_main"                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:numColumns="2">            </com.axin.testimgs.MyGridView>        </com.andview.refreshview.XScrollView>    </com.andview.refreshview.XRefreshView></RelativeLayout>

package com.axin.testimgs;import android.content.Context;import android.util.AttributeSet;import android.widget.GridView;/** * Created by Administrator on 30/8/2017. */public class MyGridView extends GridView {    public MyGridView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public MyGridView(Context context) {        super(context);    }    public MyGridView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int expandSpec = MeasureSpec.makeMeasureSpec(                Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }}

xrefreshview大家可以自行百度。

这样就实现了下拉加载更多的目的。

但是,当加载图片至60多页的时候程序就崩溃了。

记住,这时错误的!

分析原因:adapter没有及时回收bitmap,因为scrollview变成了一个大容器,加载更多的图片就装进了大容器里,这样就会生成越来越多的bitmap,就会导致内存溢出。(这种方式也不是不能解决内存溢出的问题,这需要我们来计算scrollview的总长度,我们滑到了一定的位置,就要将其他位置的bitmap回收,本人认为复杂)

解决办法:查阅了资料看到了这样一句话


Android本身对ListView是做过优化的,查看源码我们可以发现AbsListView类当中有一个内部类——RecycleBin,
和一个接口RecyclerListener。
ListView中所包含的所有子View被分为“mActiveViews”“mScrapViews”两个部分,
前者是当前活动的,也就是屏幕上显示的Views,后者是不可见的Views。
如果实现了RecyclerListener接口,
当一个View由于ListView的滑动被系统回收到RecycleBin的mScrapViews数组时,
会调用RecyclerListener中的onMovedToScrapHeap(View view)方法。
RecycleBin相当于一个临时存储不需要显示的那部分Views的对象,
随着列表滑动,这些Views需要显示出来的时候,他们就被从RecycleBin中拿了出来,
RecycleBin本身并不对mScrapViews中的对象做回收操作。



因此,OOM的问题并不是gridview造成的,也不是glide造成的,而是scrollview和固定高度的gridview造成的

所以,将xml文件修改为

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.axin.testimgs.MainActivity">    <com.andview.refreshview.XRefreshView        android:id="@+id/refresh_main"        android:layout_width="match_parent"        android:layout_height="match_parent">            <GridView                android:id="@+id/grid_main"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:numColumns="2">            </GridView>    </com.andview.refreshview.XRefreshView></RelativeLayout>

附上MainActivity和Adapter的代码

package com.axin.testimgs;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.widget.GridView;import com.alibaba.fastjson.JSON;import com.andview.refreshview.XRefreshView;import com.kymjs.rxvolley.RxVolley;import com.kymjs.rxvolley.client.HttpCallback;import com.kymjs.rxvolley.client.HttpParams;import java.util.ArrayList;import java.util.List;import butterknife.Bind;import butterknife.ButterKnife;public class MainActivity extends AppCompatActivity {    @Bind(R.id.grid_main)    GridView gridMain;    @Bind(R.id.refresh_main)    XRefreshView refreshMain;    private List<TestBean.DataBean.NewTeaMarketGoodsBean.GoodsJsonListBean>  list = new ArrayList<>();    private MainGridAdapter adapter;    private int page=1;    private boolean mySwitch=false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        adapter=new MainGridAdapter(this,list);        gridMain.setAdapter(adapter);        refreshMain.setPullRefreshEnable(false);        refreshMain.setPullLoadEnable(true);        refreshMain.setXRefreshViewListener(new XRefreshView.SimpleXRefreshListener(){            @Override            public void onLoadMore(boolean isSilence) {                page++;                getHttpData();                mySwitch=true;            }        });        getHttpData();    }    private void getHttpData(){        HttpParams params=new HttpParams();        params.putHeaders("clientOs", "android");        params.putHeaders("clientVersion", "1.0");        params.put("tabsId","3c40e335c9294870b020d2f15a73f02e");        params.put("max", "10");        Log.e("page",page+"");        params.put("page", page+"");        new RxVolley.Builder()                .url("http://www.teaoo.cn/api/newTeaMarketGoodsList.do") //接口地址                //请求类型,如果不加,默认为 GET 可选项:                //POST/PUT/DELETE/HEAD/OPTIONS/TRACE/PATCH                .httpMethod(RxVolley.Method.POST)                //设置缓存时间: 默认是 get 请求 5 分钟, post 请求不缓存                .cacheTime(6)                //内容参数传递形式,如果不加,默认为 FORM 表单提交,可选项 JSON 内容                .contentType(RxVolley.ContentType.FORM)                .params(params) //上文创建的HttpParams请求参数集                //是否缓存,默认是 get 请求 5 缓存分钟, post 请求不缓存                .shouldCache(true)                .callback(new HttpCallback() {                    @Override                    public void onSuccess(String t) {                        Log.e("t",t);                        TestBean bean = JSON.parseObject(t, TestBean.class);                        List<TestBean.DataBean.NewTeaMarketGoodsBean.GoodsJsonListBean> goodsJsonList = bean.getData().getNewTeaMarketGoods().getGoodsJsonList();                        list.addAll(goodsJsonList);                        adapter.notifyDataSetChanged();                        if (mySwitch){                            refreshMain.stopLoadMore();                            mySwitch=false;                        }                    }                    @Override                    public void onFailure(int errorNo, String strMsg) {                        super.onFailure(errorNo, strMsg);                    }                }) //响应回调                .encoding("UTF-8") //编码格式,默认为utf-8                .doTask();  //执行请求操作    }}

package com.axin.testimgs;import android.content.Context;import android.util.DisplayMetrics;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import com.bumptech.glide.Glide;import java.util.List;/** * Created by Administrator on 30/8/2017. */public class MainGridAdapter extends BaseAdapter {    private Context context;    private List<TestBean.DataBean.NewTeaMarketGoodsBean.GoodsJsonListBean>  list;    private LayoutInflater inflater;    public MainGridAdapter(Context context, List<TestBean.DataBean.NewTeaMarketGoodsBean.GoodsJsonListBean> list) {        this.context = context;        this.list = list;        inflater=LayoutInflater.from(context);    }    @Override    public int getCount() {        return list.size();    }    @Override    public Object getItem(int i) {        return list.get(i);    }    @Override    public long getItemId(int i) {        return i;    }    @Override    public View getView(int i, View view, ViewGroup viewGroup) {        ViewHolder holder;        if (view==null){            holder=new ViewHolder();            view=inflater.inflate(R.layout.item_main_grid,null);            holder.img_item_main= (ImageView) view.findViewById(R.id.img_item_main);            ViewGroup.LayoutParams params = holder.img_item_main.getLayoutParams();            params.width=getScreenWidth(context)/2;            params.height=getScreenWidth(context)/2;            holder.img_item_main.setLayoutParams(params);            holder.tv_item_main= (TextView) view.findViewById(R.id.tv_item_main);            ViewGroup.LayoutParams params1 = holder.tv_item_main.getLayoutParams();            params1.width=getScreenWidth(context)/2;            params1.height=getScreenWidth(context)/8;            holder.tv_item_main.setLayoutParams(params1);            view.setTag(holder);        }else {            holder= (ViewHolder) view.getTag();        }        Glide.with(context).load(list.get(i).getPic()).into(holder.img_item_main);        holder.tv_item_main.setText("第"+i+"个图片");        return view;    }    private class ViewHolder {        ImageView img_item_main;        TextView tv_item_main;    }    private int getScreenWidth(Context context) {        //首先获取窗口管理者        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);        //创建显示尺寸类        DisplayMetrics metrics = new DisplayMetrics();        //将窗口(屏幕)的尺寸设置给 显示尺寸类        windowManager.getDefaultDisplay().getMetrics(metrics);        //返回屏幕宽度        return metrics.widthPixels;    }}




这样就OK了

阅读全文
1 0