您的位置:首页 > 移动开发 > Android开发

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

2016-02-24 11:29 459 查看
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

e5c6
*
* @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中文网有很好的说明
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息