Android-缓存网络图片(MVP模式)学习笔记
2016-08-18 13:41
441 查看
Android之缓存网络图片到内存中学习笔记
访问网络图片是很普遍的事了,在前面的学习中,我也写过了几次异步网上请求网络图片,但是没有缓存图片,那么我们也都知道,有时候访问一些经常访问的网络图片,如果不采取缓存的形式,那么对流量的消耗会非常大,所以,有必要的时候我们可以采取缓存图片的方式来解决流量消耗问题,下面就通过一个MVP模式的简单设计来这里写代码片讲解一下缓存网络图片。
整体的结构如下:
首先对于缓存图片来说,我们需要掌握一下几个小点:
1. 当图片没有缓存的时候,我们就要去通过网络异步加载图片
2. 当存在缓存的时候,我们就不能去通过网络异步加载图片,直接获取缓存中的图片
3. 当保存缓存图片时如何确定唯一命名规则,由于网络下载图片地址是不同的,所以可根据地址命名
4. 考虑到有些模拟器保存到内存卡的缓存中时,出现在cache中找到不缓存的图片时,我们将图片地址
中的非单词字符(除了a-z A-Z 0 - 9) 以外,全部去掉
5. 考虑缓存的内存是否够用,当缓存不够用时我们就要删除部分(这里我的代码是删除全部)已缓存的图片
6. 考虑在GridView中复用控件时,出现图片的闪烁已经图片错乱问题
model层:
ModelInter.java//所有的获取数据功能都抽取到接口中,让其子类实现 public interface ModelInter { void getData(OnCompleteListener listener, String path); public interface OnCompleteListener { void onComplete(byte[] bytes, String path); } }
ModelImp.java
//实现获取数据 public class ModelImp implements ModelInter { @Override public void getData(OnCompleteListener listener, String path) { new MyAsyncTask(listener).execute(path); } }
view层:
ViewInter.java//所有更新界面,以及得到数据的功能 public interface ViewInter<T> { void getAdapterData(List<T> data); }
MainActivity.java
//主界面启动,展示数据等。 public class MainActivity extends AppCompatActivity implements ViewInter<Bean.NewslistBean> { private String path = "http://apis.baidu.com/txapi/mvtp/meinv?num=10"; private GridView mGrid; private MyAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); new Presenter(this).load(path); } private void init() { mGrid = (GridView) findViewById(R.id.mGrid); } private void showListData(List<Bean.NewslistBean> data) { if (adapter == null) { adapter = new MyAdapter(data, R.layout.list_item); mGrid.setAdapter(adapter); } else { } } //通过presenter代理类为我们提供数据 @Override public void getAdapterData(List<Bean.NewslistBean> data) { showListData(data); } }
presenter层:
presenter.java//为视图层提供所需要的数据 public class Presenter { private ViewInter viewInter; private ModelInter modelInter; public Presenter(ViewInter viewInter){ this.viewInter = viewInter; modelInter = new ModelImp(); } //当view需要数据时,调用该方法就可以获取数据了 public void load(final String path){ //传递了匿名内部类,实现获取数据的方法 modelInter.getData(new ModelInter.OnCompleteListener() { @Override public void onComplete(byte[] bytes, String path) { //通过gson解析数据 Bean bean = new Gson().fromJson(new String(bytes), Bean.class); if(bean != null) viewInter.getAdapterData(bean.getNewslist()); } },path); } }
adapter:
MyAdapter.java//自定义适配器类,不详细解说,需要了解的查看我相关知识点即可 public class MyAdapter extends MyBaseAdapter<Bean.NewslistBean> { public MyAdapter(List<Bean.NewslistBean> data, int layout) { super(data,layout); } @Override public void setData(ViewHolder holder, Bean.NewslistBean t) { //初始化上下文 if(MyUtils.context == null) MyUtils.context = holder.getContext(); //如果不存在缓存,就加载网络图片 if(!MyUtils.isCacheImage(t.getPicUrl())){ if(MyUtils.isEnoughForCache())//判断缓存空间是否足够 //加载请求网络图片 holder.setImageURL(R.id.mImg,t.getPicUrl()); else{ Toast.makeText(MyUtils.context,"空间不足了,缓存不了图片了,正在请求删除图片...",Toast.LENGTH_SHORT).show(); //删除已经缓存的图片 MyUtils.deletePic(MyUtils.context.getExternalCacheDir()); } }else { //这里可以通过log日志判断数据是怎样获取的 Log.i("IT_Real", "setData: 直接拿数据了,没用通过缓存拿数据"); //得到缓存路径下的文件 String fileName = t.getPicUrl().replaceAll("\\W",""); fileName = fileName + t.getPicUrl().substring(t.getPicUrl().lastIndexOf("."),t.getPicUrl().length()); //通过文件位置获取一个Bitmap Bitmap bm = BitmapFactory.decodeFile(holder.getContext().getExternalCacheDir() + "/" + fileName); //设置图片 holder.setImageBitmap(R.id.mImg,bm); } } }
MyBaseAdapter.java
public abstract class MyBaseAdapter<T> extends BaseAdapter { protected List<T> data; protected int layout; public MyBaseAdapter(List<T> data,int layout){ this.data = data; this.layout = layout; } @Override public int getCount() { return data == null ? 0 : data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = ViewHolder.getHolder(convertView,parent,position, R.layout.list_item); setData(holder,data.get(position)); return holder.getConvertView(); } public abstract void setData(ViewHolder holder,T t); }
ViewHolder.java
public class ViewHolder { private int position; private SparseArray<View> array; private View convertView; private Context context; private ViewHolder(ViewGroup parent, int position, int layout) { this.position = position; this.context = parent.getContext(); convertView = LayoutInflater.from(parent.getContext()).inflate(layout, null); convertView.setTag(this); array = new SparseArray<>(); } public static ViewHolder getHolder(View convertView, ViewGroup parent, int position, int layout) { if (convertView == null) { return new ViewHolder(parent, position, layout); } else { ViewHolder holder = (ViewHolder) convertView.getTag(); holder.position = position; return holder; } } public <T extends View> T getView(int viewId) { View view = array.get(viewId); if (view == null) { view = convertView.findViewById(viewId); array.put(viewId, view); } return (T) view; } public Context getContext() { return context; } public View getConvertView() { return convertView; } public ViewHolder setText(int viewId, String data) { TextView tv = getView(viewId); tv.setText(data); return this; } public ViewHolder setImageResource(int viewId, int resId) { ImageView img = getView(viewId); img.setImageResource(resId); return this; } public ViewHolder setImageURL(int viewId, String url){ final ImageView image = getView(viewId); //防止图片闪烁,每次加载GridView时,设置为空白界面 image.setImageBitmap(null); new MyAsyncTask(new ImageCallBack(image,url,this.context)).execute(url); Log.i("IT_Real", "setImageURL: 网络请求了"); return this; } public ViewHolder setImageBitmap(int viewId, Bitmap bm) { ImageView img = getView(viewId); img.setImageBitmap(bm); return this; } }
utils:
MyAsyncTask.java//异步网络请求数据 public class MyAsyncTask extends AsyncTask<String,Void,byte[]> { private String path; private ModelInter.OnCompleteListener listener; public MyAsyncTask(ModelInter.OnCompleteListener listener){ this.listener = listener; } /** * 异步请求网络数据 */ @Override protected byte[] doInBackground(String... params) { path = params[0]; return getBytes(path); } /** * 获取到数据,然后接口回调传递数据 */ @Override protected void onPostExecute(byte[] bytes) { super.onPostExecute(bytes); if(listener != null) { listener.onComplete(bytes,path); } } /** * 网络请求的数据 */ private byte[] getBytes(String path){ try { URL url = new URL(path); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setRequestProperty("apikey","百度APIStore的个人apikey,没有的可以自己去注册一个,然后获取美女图片json数据"); connection.setConnectTimeout(5000); connection.setReadTimeout(5000); connection.connect(); if(connection.getResponseCode() == HttpURLConnection.HTTP_OK){ BufferedInputStream bis = new BufferedInputStream(connection.getInputStream()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len = -1; byte[] bytes = new byte[1024 * 8]; while(-1 != (len = bis.read(bytes))){ baos.write(bytes,0,len); } byte[] result = baos.toByteArray(); bis.close(); baos.close(); return result; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }
MyUtils.java
//所有自己需要实现的功能工具类
public class MyUtils { public static Context context; private static StatFs statFs; /** * 判断是否存在缓存图片 * @param path 图片路径 * @return */ public static boolean isCacheImage(String path){ String fileName = path.replaceAll("\\W",""); fileName = fileName + path.substring(path.lastIndexOf("."),path.length()); File file = new File(context.getExternalCacheDir(),fileName); Log.i("IT_Real", "isCacheImage: " + "file is exists" + file.exists() + " ->" + isMount()); return isMount() && file.exists(); } /** * 判断内存卡是否具有读写权限 * @return true有,false没有 */ public static boolean isMount(){ return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } /** * 判断缓存空间是否足够 * @return */ public static boolean isEnoughForCache(){ if(statFs == null) statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); int availableBlocks = statFs.getAvailableBlocks(); int blockSize = statFs.getBlockSize(); //如果可用空间小于10兆,就提示用户内存不足 return availableBlocks * blockSize>= 10 * 1024 * 1024; } /** * 递归删除缓存的文件 * @param file 文件路径 */ public static void deletePic(File file){ if(file == null) return; if(!file.isFile()){//如果不是文件就遍历 File[] files = file.listFiles(); if(file != null){ for (File file1 : files) { deletePic(file1); } } }else { file.delete(); Log.i("IT_Real", "deletePic: 删除了.."); } } }
callback:
ImageCallBack.java//图片回调接口实现类
public class ImageCallBack implements ModelInter.OnCompleteListener { private ImageView image; private String path; private Context context; public ImageCallBack(ImageView image, String path, Context context) { this.image = image; this.path = path; this.context = context; } /** * 得到网络请求图片后的图片数据 * @param bytes * @param path */ @Override public void onComplete(byte[] bytes, String path) { //判断每次加载的网络数据是否和当前图片地址的加载数据,不相同则不设置此图片,防止图片数据错乱 if (bytes != null && this.path.equals(path)) { Bitmap bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); image.setImageBitmap(bm); //开始保存图片 saveCache(context.getExternalCacheDir(), bm, path); } } /** * 保存缓存的图片 * @param cacheDir 缓存的路径 * @param bm * @param path 文件的名字 */ private void saveCache(File cacheDir, Bitmap bm, String path) { //去掉所有非单词的字符 String fileName = path.replaceAll("\\W",""); fileName = fileName + path.substring(path.lastIndexOf("."),path.length()); try { BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(cacheDir, fileName))); bm.compress(Bitmap.CompressFormat.JPEG, 100, bos); bos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
整体的功能已经实现了,这个小案例中,我使用了MVP模式、万能通用适配器,关于MVP模式和万能适配器,这里都不做介绍了,不了解的可用查看我博客中的相关知识点,所有涉及到的主要问题都已经注释了,如果有不懂的可用留言下方,我会在第一时间回复。
相关文章推荐
- Android之okHttpClient+handler+LruCache缓存网络图片学习笔记(通用MVP模式)
- Android异步载入学习笔记之四:利用缓存优化网络载入图片及ListView载入优化
- Android网络图片加载缓存处理库的使用---第三方库学习笔记(五)
- Android网络图片加载缓存处理库的使用---第三方库学习笔记(五)
- Android异步加载学习笔记之四:利用缓存优化网络加载图片及ListView加载优化
- Android加载网络图片学习过程,涉及到本地缓存
- Android(java)学习笔记201:网络图片浏览器的实现(ANR)
- Android 学习笔记之Volley(八)实现网络图片的数据加载
- Android 异步加载网络图片并缓存到本地 软引用 学习分享
- android图片的异步加载和双缓存学习笔记——DisplayImageOptions
- android图片的异步加载和双缓存学习笔记——DisplayImageOptions
- 学习笔记:Android里面的MVC模式与MVP模式
- android图片的异步加载和双缓存学习笔记——DisplayImageOptions
- Android 异步加载网络图片并缓存到本地 软引用 学习分享
- Android 异步加载网络图片并缓存到本地 软引用 学习分享
- Android 异步加载网络图片并缓存到本地 软引用 学习分享
- Android(java)学习笔记204:自定义SmartImageView(继承自ImageView,扩展功能为自动获取网络路径图片)
- Android学习(34) -- 带缓存的网络图片下载
- Android 异步加载网络图片并缓存到本地 软引用 学习分享
- android图片的异步加载和双缓存学习笔记——DisplayImageOptions