listview、gridview上滑加載更多圖片,內存溢出outofmemery的解決方案

有很多人可能會跟我一樣,有這樣一個功能需求,(新聞或者商城)後臺提供上滑加載更多的分頁查詢接口,每次上滑就需要加載更多的圖片,當頁數超過一定數量之後,手機就會卡頓,緊接着直接退出程序,打開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了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章