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中文网有很好的说明
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories