Android 解决加载图片过多出现oom--强大的Fresco

Fresco是Facebook推出的很好的解决加载图片过多而产生的oom现象。但是在我最先接触Fresco的时候,由于我用的eclipse开发,
在官网下载的Fresco库没办法用,不知道是怎么回事,后来自己找到了一个直接导入就可以用的Fresco库,[免费下载链接](http://download.csdn.net/detail/sunflower_cwy/9444387)

先新建一个ImagePipeline的配置工具类,里面做好Fresco使用前的基本配置

    /**
     * ImagePipeline配置工具类
     */

/*
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only.  Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.ycsoft.android.moviebar.comment;


import android.content.Context;
import android.os.Build;
import android.os.Environment;

import com.facebook.cache.disk.DiskCacheConfig;
import com.facebook.common.internal.Supplier;
import com.facebook.common.util.ByteConstants;
import com.facebook.imagepipeline.cache.MemoryCacheParams;
import com.facebook.imagepipeline.core.ImagePipelineConfig;

import java.io.File;

/**
 * Creates ImagePipeline configuration for the sample app
 */
public class ImagePipelineConfigFactory {
    private static final String IMAGE_PIPELINE_CACHE_DIR = "imagepipeline_cache";

    private static ImagePipelineConfig sImagePipelineConfig;
//    private static ImagePipelineConfig sOkHttpImagePipelineConfig;

    private static final int MAX_HEAP_SIZE = (int) Runtime.getRuntime().maxMemory();

    public static final int MAX_DISK_CACHE_SIZE = 300 * ByteConstants.MB;
    public static final int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 3;

    /**
     * Creates config using android http stack as network backend.
     */
    public static ImagePipelineConfig getImagePipelineConfig(Context context) {
        if (sImagePipelineConfig == null) {
            ImagePipelineConfig.Builder configBuilder = ImagePipelineConfig.newBuilder(context);
            configureCaches(configBuilder, context);
            sImagePipelineConfig = configBuilder.build();
        }
        return sImagePipelineConfig;
    }

    /**
     * Creates config using OkHttp as network backed.
     */
/*  public static ImagePipelineConfig getOkHttpImagePipelineConfig(Context context) {
    if (sOkHttpImagePipelineConfig == null) {
      OkHttpClient okHttpClient = new OkHttpClient();
      ImagePipelineConfig.Builder configBuilder =
        OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient);
      configureCaches(configBuilder, context);
      sOkHttpImagePipelineConfig = configBuilder.build();
    }
    return sOkHttpImagePipelineConfig;
  }*/

    /**
     * Configures disk and memory cache not to exceed common limits
     */
    private static void configureCaches(ImagePipelineConfig.Builder configBuilder, Context context) {
        final MemoryCacheParams bitmapCacheParams = new MemoryCacheParams(
                MAX_MEMORY_CACHE_SIZE, // Max total size of elements in the cache
                Integer.MAX_VALUE,                     // Max entries in the cache
                MAX_MEMORY_CACHE_SIZE, // Max total size of elements in eviction queue
                Integer.MAX_VALUE,                     // Max length of eviction queue
                Integer.MAX_VALUE);                    // Max cache entry size
        configBuilder
                .setBitmapMemoryCacheParamsSupplier(
                        new Supplier<MemoryCacheParams>() {
                            public MemoryCacheParams get() {
                                return bitmapCacheParams;
                            }
                        })
                .setMainDiskCacheConfig(DiskCacheConfig.newBuilder()
                                .setBaseDirectoryPath(getExternalCacheDir(context))
                                .setBaseDirectoryName(IMAGE_PIPELINE_CACHE_DIR)
                                .setMaxCacheSize(MAX_DISK_CACHE_SIZE)
                                .build());
    }

    public static File getExternalCacheDir(final Context context) {
        if (hasExternalCacheDir())
            return context.getExternalCacheDir();

        // Before Froyo we need to construct the external cache dir ourselves
        final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
        return createFile(Environment.getExternalStorageDirectory().getPath() + cacheDir, "");
    }

    public static boolean hasExternalCacheDir() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
    }

    public static File createFile(String folderPath, String fileName) {
        File destDir = new File(folderPath);
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        return new File(folderPath, fileName);
    }

在Application类里面做好Fresco使用前的声明

Fresco.initialize(this, ImagePipelineConfigFactory.getImagePipelineConfig(this));

上拉加载更多类

/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2015 All Rights Reserved.
 */
package com.ycsoft.android.moviebar.view;

import com.ycsoft.android.moviebar.R;

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * 支持上拉加载更多的
 *
 * @author 紫韶
 * @version $$Id: LoadMoreRecyclerView.java, v 0.1 11/17/15 10:07 alicx Exp $$
 */
public class LoadMoreRecyclerView extends RecyclerView {
    /**
     * item 类型
     */
    public final static int TYPE_NORMAL = 0;
    public final static int TYPE_HEADER = 1;//头部--支持头部增加一个headerView
    public final static int TYPE_FOOTER = 2;//底部--往往是loading_more
    public final static int TYPE_LIST = 3;//代表item展示的模式是list模式
    public final static int TYPE_STAGGER = 4;//代码item展示模式是网格模式

    private boolean mIsFooterEnable = false;//是否允许加载更多

    /**
     * 自定义实现了头部和底部加载更多的adapter
     */
    private AutoLoadAdapter mAutoLoadAdapter;
    /**
     * 标记是否正在加载更多,防止再次调用加载更多接口
     */
    private boolean mIsLoadingMore;
    /**
     * 标记加载更多的position
     */
    private int mLoadMorePosition;
    /**
     * 加载更多的监听-业务需要实现加载数据
     */
    private LoadMoreListener mListener;

    public LoadMoreRecyclerView(Context context) {
        super(context);
        init();
    }

    public LoadMoreRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LoadMoreRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * 初始化-添加滚动监听
     * <p/>
     * 回调加载更多方法,前提是
     * <pre>
     *    1、有监听并且支持加载更多:null != mListener && mIsFooterEnable
     *    2、目前没有在加载,正在上拉(dy>0),当前最后一条可见的view是否是当前数据列表的最好一条--及加载更多
     * </pre>
     */
    private void init() {
        super.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (null != mListener && mIsFooterEnable && !mIsLoadingMore && dy > 0) {
                    int lastVisiblePosition = getLastVisiblePosition();
                    if (lastVisiblePosition + 1 == mAutoLoadAdapter.getItemCount()) {
                        setLoadingMore(true);
                        mLoadMorePosition = lastVisiblePosition;
                        mListener.onLoadMore();
                        FooterViewHolder.getmFooterProgressBar().setVisibility(View.VISIBLE);
                        FooterViewHolder.getTextView().setVisibility(View.VISIBLE);
                    }
                }
            }
        });
    }

    /**
     * 设置加载更多的监听
     *
     * @param listener
     */
    public void setLoadMoreListener(LoadMoreListener listener) {
        mListener = listener;
    }

    /**
     * 设置正在加载更多
     *
     * @param loadingMore
     */
    public void setLoadingMore(boolean loadingMore) {
        this.mIsLoadingMore = loadingMore;
    }

    /**
     * 加载更多监听
     */
    public interface LoadMoreListener {
        /**
         * 加载更多
         */
        void onLoadMore();
    }

    /**
     *
     */
    public class AutoLoadAdapter extends Adapter<ViewHolder> {

        /**
         * 数据adapter
         */
        @SuppressWarnings("rawtypes")
        private Adapter mInternalAdapter;

        private boolean mIsHeaderEnable;
        private int mHeaderResId;

        @SuppressWarnings("rawtypes")
        public AutoLoadAdapter(Adapter adapter) {
            mInternalAdapter = adapter;
            mIsHeaderEnable = false;
        }

        @Override
        public int getItemViewType(int position) {
            int headerPosition = 0;
            int footerPosition = getItemCount() - 1;

            if (headerPosition == position && mIsHeaderEnable && mHeaderResId > 0) {
                return TYPE_HEADER;
            }
            if (footerPosition == position && mIsFooterEnable) {
                return TYPE_FOOTER;
            }
            /**
             * 这么做保证layoutManager切换之后能及时的刷新上对的布局
             */
            if (getLayoutManager() instanceof LinearLayoutManager) {
                return TYPE_LIST;
            } else if (getLayoutManager() instanceof StaggeredGridLayoutManager) {
                return TYPE_STAGGER;
            } else {
                return TYPE_NORMAL;
            }
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (viewType == TYPE_HEADER) {
                return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(
                        mHeaderResId, parent, false));
            }
            if (viewType == TYPE_FOOTER) {
                return new FooterViewHolder(
                        LayoutInflater.from(parent.getContext()).inflate(
                                R.layout.list_foot_loading, parent, false));
            } else { // type normal
                return mInternalAdapter.onCreateViewHolder(parent, viewType);
            }
        }

      /*  public class FooterViewHolder extends ViewHolder {

            private GifView mFooterProgressBar;
            private TextView textView;
            public FooterViewHolder(View itemView) {
                super(itemView);
                this.mFooterProgressBar = (GifView) itemView.findViewById(R.id.pull_to_load_progress);
                mFooterProgressBar.setGifImage(R.drawable.loading_gif);
                mFooterProgressBar.setShowDimension(50, 50); 
                this.textView = (TextView) itemView.findViewById(R.id.loading_text);
                mFooterProgressBar.setVisibility(View.VISIBLE);
                textView.setVisibility(View.VISIBLE);
            }

        }*/

        public class HeaderViewHolder extends ViewHolder {
            public HeaderViewHolder(View itemView) {
                super(itemView);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            int type = getItemViewType(position);
            if (type != TYPE_FOOTER && type != TYPE_HEADER) {
                mInternalAdapter.onBindViewHolder(holder, position);
            }
        }

        /**
         * 需要计算上加载更多和添加的头部俩个
         *
         * @return
         */
        @Override
        public int getItemCount() {
            int count = mInternalAdapter.getItemCount();
            if (mIsFooterEnable) count++;
            if (mIsHeaderEnable) count++;

            return count;
        }

        public void setHeaderEnable(boolean enable) {
            mIsHeaderEnable = enable;
        }

        public void addHeaderView(int resId) {
            mHeaderResId = resId;
        }
    }

    @SuppressWarnings("rawtypes")
    @Override
    public void setAdapter(Adapter adapter) {
        if (adapter != null) {
            mAutoLoadAdapter = new AutoLoadAdapter(adapter);
        }
        super.swapAdapter(mAutoLoadAdapter, true);
    }

    /**
     * 切换layoutManager
     *
     * 为了保证切换之后页面上还是停留在当前展示的位置,记录下切换之前的第一条展示位置,切换完成之后滚动到该位置
     * 另外切换之后必须要重新刷新下当前已经缓存的itemView,否则会出现布局错乱(俩种模式下的item布局不同),
     * RecyclerView提供了swapAdapter来进行切换adapter并清理老的itemView cache
     *
     * @param layoutManager
     */
    public void switchLayoutManager(LayoutManager layoutManager) {
        int firstVisiblePosition = getFirstVisiblePosition();
//        getLayoutManager().removeAllViews();
        setLayoutManager(layoutManager);
//        super.swapAdapter(mAutoLoadAdapter, true);
        getLayoutManager().scrollToPosition(firstVisiblePosition);
    }

    /**
     * 获取第一条展示的位置
     *
     * @return
     */
    private int getFirstVisiblePosition() {
        int position;
        if (getLayoutManager() instanceof LinearLayoutManager) {
            position = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
        } else if (getLayoutManager() instanceof GridLayoutManager) {
            position = ((GridLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
        } else if (getLayoutManager() instanceof StaggeredGridLayoutManager) {
            StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) getLayoutManager();
            int[] lastPositions = layoutManager.findFirstVisibleItemPositions(new int[layoutManager.getSpanCount()]);
            position = getMinPositions(lastPositions);
        } else {
            position = 0;
        }
        return position;
    }

    /**
     * 获得当前展示最小的position
     *
     * @param positions
     * @return
     */
    private int getMinPositions(int[] positions) {
        int size = positions.length;
        int minPosition = Integer.MAX_VALUE;
        for (int i = 0; i < size; i++) {
            minPosition = Math.min(minPosition, positions[i]);
        }
        return minPosition;
    }

    /**
     * 获取最后一条展示的位置
     *
     * @return
     */
    private int getLastVisiblePosition() {
        int position;
        if (getLayoutManager() instanceof LinearLayoutManager) {
            position = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
        } else if (getLayoutManager() instanceof GridLayoutManager) {
            position = ((GridLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
        } else if (getLayoutManager() instanceof StaggeredGridLayoutManager) {
            StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) getLayoutManager();
            int[] lastPositions = layoutManager.findLastVisibleItemPositions(new int[layoutManager.getSpanCount()]);
            position = getMaxPosition(lastPositions);
        } else {
            position = getLayoutManager().getItemCount() - 1;
        }
        return position;
    }

    /**
     * 获得最大的位置
     *
     * @param positions
     * @return
     */
    private int getMaxPosition(int[] positions) {
        int size = positions.length;
        int maxPosition = Integer.MIN_VALUE;
        for (int i = 0; i < size; i++) {
            maxPosition = Math.max(maxPosition, positions[i]);
        }
        return maxPosition;
    }

    /**
     * 添加头部view
     *
     * @param resId
     */
    public void addHeaderView(int resId) {
        mAutoLoadAdapter.addHeaderView(resId);
    }

    /**
     * 设置头部view是否展示
     * @param enable
     */
    public void setHeaderEnable(boolean enable) {
        mAutoLoadAdapter.setHeaderEnable(enable);
    }

    /**
     * 设置是否支持自动加载更多
     *
     * @param autoLoadMore
     */
    public void setAutoLoadMoreEnable(boolean autoLoadMore) {
        mIsFooterEnable = autoLoadMore;
    }

    /**
     * 通知更多的数据已经加载
     *
     * 每次加载完成之后添加了Data数据,用notifyItemRemoved来刷新列表展示,
     * 而不是用notifyDataSetChanged来刷新列表
     *
     * @param hasMore
     */
    public void notifyMoreFinish(boolean hasMore) {
        setAutoLoadMoreEnable(hasMore);
        getAdapter().notifyItemRemoved(mLoadMorePosition);
        mIsLoadingMore = false;
    }
}

在gridview中的显示
xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

     <com.ycsoft.android.moviebar.view.LoadMoreRecyclerView
        android:id="@+id/main_teleplay_gridview"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="45dp"
        android:layout_marginRight="45dp"
        android:layout_marginTop="80dp"
        android:scrollbars="none" />


</RelativeLayout>

gridview的适配器

package com.ycsoft.android.moviebar.adapter;

import java.util.List;

import android.app.FragmentManager;
import android.content.Context;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.controller.ControllerListener;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.SimpleDraweeView;
import com.ycsoft.android.moviebar.R;
import com.ycsoft.android.moviebar.entity.SelectResultEntity;
import com.ycsoft.android.moviebar.fragment.film.FilmListFragment;
import com.ycsoft.android.moviebar.view.RotateTextView;

public class FilmListAdapter extends
        RecyclerView.Adapter<FilmListAdapter.MyViewHolder> {

    private List<SelectResultEntity> _listEntities;
    private LayoutInflater _inflater = null;
    private String _name;
    private String _kind;
    private FragmentManager _fManager;
    private MyViewHolder myViewHolder;

    public FilmListAdapter(Context context, List<SelectResultEntity> entities,
            String name, String kind, FragmentManager fm) {
        this._listEntities = entities;
        this._inflater = LayoutInflater.from(context);
        this._name = name;
        this._kind = kind;
        this._fManager = fm;
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        RotateTextView text;
        TextView name;
        SimpleDraweeView image;
        RelativeLayout layout;

        public MyViewHolder(View view) {
            super(view);
            name = (TextView) view
                    .findViewById(R.id.fragment_gridview_item_moviename_text);
            text = (RotateTextView) view
                    .findViewById(R.id.fragment_gridview_item_average_text);
            image = (SimpleDraweeView) view
                    .findViewById(R.id.fragment_gridview_item_movieimg);
            layout = (RelativeLayout) view
                    .findViewById(R.id.main_select_fragment_gridview_item_layout);
        }

    }

    @Override
    public int getItemCount() {
        return _listEntities == null ? 0 : _listEntities.size();
    }

    private SelectResultEntity getItem(int position) {
        return _listEntities.get(position);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        final SelectResultEntity entity = getItem(position);
        myViewHolder = holder;
        holder.name.setText(entity.getLocalMovieName());

        if (entity.getYCAver() == 0.0f) {
            holder.text.setText("暂无");
            holder.text.setTextSize(10.0f);
        } else {
            holder.text.setText(String.valueOf(entity.getYCAver()));
            holder.text.setTextSize(17.0f);
        }

        Uri uri;
        if (entity.getYCMovieImg() == null) {
            uri = Uri.parse("res://drawable/"
                    + R.drawable.fragment_gridview_item_movieimg_deflut);
        } else {
            uri = Uri.parse(entity.getYCMovieImg());
        }
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setControllerListener(controllerListener).setUri(uri).build();
        holder.image.setController(controller);

        holder.layout.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                FilmListFragment.jumpFragment(entity.getLocalMovieName(),
                        _name, _kind, _fManager);
            }
        });
    }

    @SuppressWarnings("rawtypes")
    private ControllerListener controllerListener = new BaseControllerListener() {
        public void onFailure(String id, Throwable throwable) {
            myViewHolder.image.setImageURI(Uri.parse("res://drawable/"
                    + R.drawable.fragment_gridview_item_movieimg_deflut));
        };
    };

 /**
     * 当图片不显示的时候自动释放,防止oom
     *
     * @param holder
     */
    @Override
    public void onViewRecycled(MyViewHolder holder) {
        super.onViewRecycled(holder);
        if (holder.image.getController() != null) {
            holder.image.getController().onDetach();
        }
        if (holder.image.getTopLevelDrawable() != null) {
            holder.image.getTopLevelDrawable().setCallback(null);
        }
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = _inflater.inflate(
                R.layout.main_select_list_frament_gridview_item, parent, false);
        return new MyViewHolder(view);
    }

}

最后在onDestary中释放内存

ImagePipeline imagePipeline = Fresco.getImagePipeline();
imagePipeline.clearMemoryCaches();

Fresco是个很强大的图片处理框架,这只是我在项目中的基本用法,更多的用法在Fresco中文网有很好的说明

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值