有很多人可能会跟我一样,有这样一个功能需求,(新闻或者商城)后台提供上滑加载更多的分页查询接口,每次上滑就需要加载更多的图片,当页数超过一定数量之后,手机就会卡顿,紧接着直接退出程序,打开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了