本文主要内容

照片墙应用介绍

缓存使用思路

多线程使用

杂谈

记得本人工作之后,手头上第一个活就是做一个本地壁纸展示程序,当时正流行1080P的屏幕,为了展示屏幕效果,壁纸图片巨大无比,轻松突破10M,而且内置壁纸还有15张左右,当时面临的两个问题,一个就是oom问题,另一个就是流畅度问题,虽然后来都解决了,但之后就对这种巨大的图片有点虚。。

惭愧的是,5年以后的今天才写了这篇文章,不忘初心方得始终,这个道理虽然简单但今天才真正明白

照片墙应用介绍

应用中需要显示大量的图片,图片从网络获取,效果如下图:

应用有两个问题:

流畅地显示

省流量

流畅性是所有应用都必须要有的,图片要从网上下载,下载过程必须放到工作线程当中来解决。另外图片也有可以有一部分保存在内存当中,直接从内存当中获取并显示,这才是最快的方式。

图片有很多,用户理论上只用下载一遍图片就行了,不能重复去下载,缓存可以帮助用户节省流量。

缓存使用思路

Android缓存原理一文中说明了缓存的原理以及使用方式,如有不明白可参考此文。

其实缓存思路特别简单,在需要使用图片的时候,先查内存缓存,如果没有再查硬盘缓存,还没有则去网络下载,下载完毕后加入硬盘缓存以及内存缓存,以备下次再用。缓存是不是已满,该如何删除交给缓存工具类决定,但大体思路一定是这样。

本文中也是这么做的,而且硬盘缓存读取相当于IO过程,较缓慢,也可以放到工作线程当中,于是就可以把查内存缓存以外的所有操作都放到工作线程当中完成。

照片墙的核心代码如下:

private void loadBitmaps(ImageView imageView, String url) {

try {

Bitmap bitmap = getBitmapFromMemoryCache(url);

if (bitmap == null) {

Task task = new Task(imageView, url);

mPools.submit(task);

} else {

if (imageView != null && bitmap != null) {

imageView.setImageBitmap(bitmap);

}

}

} catch (Exception e) {

// TODO: handle exception

}

}

如果从内存缓存中无法获取到图片,则向线程池中提交一个任务,在线程池中完成图片的获取。

class Task implements Runnable {

ImageView iv;

String imageUrl;

Task(ImageView view, String url) {

iv = view;

imageUrl = url;

}

@Override

public void run() {

FileDescriptor fileDescriptor = null;

FileInputStream fileInputStream = null;

Snapshot snapshot = null;

try {

final String key = hashKeyForDisk(imageUrl);

snapshot = mDiskLruCache.get(key);

if (snapshot == null) {

Editor editor = mDiskLruCache.edit(key);

if (editor != null) {

OutputStream outputStream = editor.newOutputStream(0);

if (downloadUrlToStream(imageUrl, outputStream)) {

editor.commit();

} else {

editor.abort();

}

}

snapshot = mDiskLruCache.get(key);

}

if (snapshot != null) {

fileInputStream = (FileInputStream) snapshot

.getInputStream(0);

fileDescriptor = fileInputStream.getFD();

}

Bitmap bitmap = null;

if (fileDescriptor != null) {

bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);

}

if (bitmap != null) {

addBitmapToMemoryCache(key, bitmap);

Result result = new Result(iv, bitmap, imageUrl);

Message msg = Message.obtain(mHandler, MSG_SHOW_BITMAP, result);

msg.sendToTarget();

}

} catch (Exception e) {

Log.e("okunu", "run", e);

}

}

}

和前文所说思路一样,先从硬盘缓存中读取,如果没有再从网络中下载图片。一定不能忘记将图片添加进入硬盘缓存和内存缓存中来,这一步非常重要。

由于硬盘缓存的使用方法,在从网上下载图片的时候,是直接下载到缓存文件当中的,而不是先下载再复制一份到硬盘缓存当中,因为这样可以节省一次IO过程。

另外还有一些小细节,比如最后如何刷新界面,首先在工作线程中是无法刷新UI的,所以在此处用handler,将结果返回主线程中处理。

if (msg.what == MSG_SHOW_BITMAP) {

Object obj = msg.obj;

if (obj != null) {

Result result = (Result) obj;

if (result.iv.getTag().equals(result.url)) {

result.iv.setImageBitmap(result.bitmap);

}

}

}

结果中包含图片、imageview以及url,为了防止图片显示错乱,还加以判定,只有imageview的tag等于此url的时候,才更新图片,如此则不会更新错乱。

多线程使用

在这种有大量耗时操作的时候,开启工作线程是非常必要的,但如何优雅地使用线程,其实仍然有门道。

android中有很多种开启工作线程的方式。

AsyncTask,封装地非常好,不同的回调函数还处于不同的线程当中,方便用户拿结果更新UI,但如果有多个AsyncTask实例在执行,它们是顺序执行,并不是想象中的多线程在多核CPU上同时执行。

Thread,原始的线程使用方式,如果构造太多,不优雅,线程不能得到复用,浪费资源,开启一个线程也是有开销的

HandlerThread,能够与handler结合,一种非常优雅的工作线程开启方式,不过不太适合大量任务的情况,这种只相当于线程池中只有一个线程在跑

线程池,重量级武器,适合大量任务的情况

android中开启工作线程包括但不限于以上4种,它们的优劣大致如上所述,需要我们根据不同的情形选用不同的方式,写出优雅的代码。

在照片墙应用中,明显是有大量任务需要工作线程来执行的,以上四种情况中,最适合的就是线程池了,它的速度效率是最高的,有兴趣的同学可以去做做实验,以四种不同方式来完成任务,看看哪个效率最高

杂谈

在文章一开始的时候,就聊到一个话题,OOM的问题,如果图片太大,如果防止OOM呢?

这个问题相信很多人都知道答案:

/**

* If set to true, the decoder will return null (no bitmap), but

* the out... fields will still be set, allowing the caller to

* query the bitmap without having to allocate the memory for its pixels.

*/

public boolean inJustDecodeBounds;

/**

* If set to a value > 1, requests the decoder to subsample the original

* image, returning a smaller image to save memory. The sample size is

* the number of pixels in either dimension that correspond to a single

* pixel in the decoded bitmap. For example, inSampleSize == 4 returns

* an image that is 1/4 the width/height of the original, and 1/16 the

* number of pixels. Any value <= 1 is treated the same as 1. Note: the

* decoder uses a final value based on powers of 2, any other value will

* be rounded down to the nearest power of 2.

*/

public int inSampleSize;

利用inJustDecodeBounds,计算出inSampleSize,相信这样的逻辑网上一找一大堆,本人在此不再复述。如果你的图片应用,图片都是大于5M以上的高清大图,那么一定要考虑这个方法了。

另外由url转化成hash key的时候,怎么这么费劲呢?

public String hashKeyForDisk(String key) {

String cacheKey;

try {

final MessageDigest mDigest = MessageDigest.getInstance("MD5");

mDigest.update(key.getBytes());

cacheKey = bytesToHexString(mDigest.digest());

Log.i("okunu", "cacheKey = " + cacheKey);

} catch (NoSuchAlgorithmException e) {

cacheKey = String.valueOf(key.hashCode());

}

return cacheKey;

}

private String bytesToHexString(byte[] bytes) {

StringBuilder sb = new StringBuilder();

for (int i = 0; i < bytes.length; i++) {

String hex = Integer.toHexString(0xFF & bytes[i]);

if (hex.length() == 1) {

sb.append('0');

}

sb.append(hex);

}

return sb.toString();

}

第一步,我们通常能理解,获取url的md5值,因为url中可能含有各种奇异字符,不适合作为key来使用,但bytesToHexString方法的作用是什么?

其实就是将byte数据转化成16进制,生成文件名而已。

代码已经上传到github当中,有需要的可以取用

android 照片墙程序,Android照片墙应用相关推荐

  1. android连接程序,android程序如何连接真机进行测试

    android 如何连真机测试 个人认为第三种方法更易理解 1. 设置android手机为USB调试模式.步骤: menu---> 设置 ---> 应用程序 ---> 开发 , 选择 ...

  2. android hook 程序,Android hook框架之Xposed插件开发

    上一篇讲了Android hook框架Cydia,这一篇是Android hook的另一个框架Xposed,Xposed是一款广泛应用于安卓领域的开源框架. 其原理是Xposed框架主要通过替换/sy ...

  3. 用自己的手机调试Android应用程序——Android Studio

    最近在学习Android应用开发,一般来说都是用AVD建立的虚拟手机来调试和运行Android应用程序.不过自己的手机也是Android的,所以就是尝试用自己的手机来调试程序.不过在调试之前先做好手机 ...

  4. android 获取程序,Android获取桌面应用程序

    转载请注明出处,谢谢:http://blog.csdn.net/harryweasley/article/details/50057029 首先在看这个博客之前, 你可以先看下这个博客,http:// ...

  5. 调用android邮件程序,Android开发中怎样调用系统Email发送邮件(多种调用方式)

    我们都知道,在Android中调用其他程序进行相关处理,几乎都是使用的Intent,所以,Email也不例外. 在Android中,调用Email有三种类型的Intent: Intent.ACTION ...

  6. Android冻结程序,Android Studio 3.0和应用程序冻结

    我已经将我的 android工作室从2.x更新到android 3.0 stable. 从那时起,当我尝试使用cyanogenmod 12.1(android 5.1.1)将应用程序推送到我的Nexu ...

  7. 如何导出android studio程序,Android Studio 如何导出 Jar 给 Unity 使用

    大致步骤如下:1.创建新的 Android Studio 工程2.为此 Android Studio 工程创建 Android Library 类库(也就是一个 Module)(后面就是用它生成 ja ...

  8. android联网程序,android 联网 HttpClient

    可以使用 Android 集成进来的 apache 中关于联网的API. HttpParams : 保存Http请求设定的参数对象 HttpConnectionParams :提供对Http连接参数进 ...

  9. android su 程序,android 开发 制作自己的su

    所需材料: ①.su.c ②.Android.mk 以上玩意可以从源码中获取,或者从网上下载! 如果是从android源码中提取的su,请自行注释掉权限检查部分哈~~~ /// 1. Ubuntu l ...

最新文章

  1. 改善C#程序的建议10:用Parallel简化Task
  2. Centos6 升级glibc-2.17,解决Requires: libc.so.6(GLIBC_2.14)(64bit)错误解决方法
  3. 解决WebStorm中git出现的 Could not read from remote repository问题
  4. 执行srvctl报错 :error while loading shared libraries: libpthread.so.0:
  5. 计算机网络知识点2——数据交换、码分多路复用
  6. CF--思维练习-- CodeForces - 215C - Crosses(思维题)
  7. LeetCode 1700. 无法吃午餐的学生数量(队列模拟 / 不模拟)
  8. 安装electron-react-boilerplate遇到的问题
  9. python xpath定位元素方法_python--通过xpath相对节点位置查找元素(续)
  10. mysql 5.7参数配置_MySQL 5.7-新增配置参数
  11. 5.Chrome开发者工具不完全指南:(三、性能篇)
  12. 关于python注释下面选项描述错误的是_关于Python的列表,以下选项中描述错误的是______...
  13. Hadoop开发环境的搭建与配置(基于Linux)
  14. hdu2553解题报告
  15. shufflenetv1详解
  16. c语言线性链表的插入,线性链表的创建_插入_删除_操作_C语言
  17. Python面向对象:小明和小美爱跑步
  18. 【工程应用七】接着折腾模板匹配算法 (Optimization选项 + no_pregeneration模拟 + 3D亚像素插值)...
  19. 4412 SPI驱动
  20. 不关闭Selinux\Firewalld的情况下OpenSSH 9.0/9.1/9.2升级说明——筑梦之路

热门文章

  1. T大计算机科学本科参考书目
  2. python模拟鼠标键盘点击,简单自动化动物餐厅
  3. 计算机专业退休有退休金,我参加工作42年,国家公务员退休,二级警督,退休工资为什么按2014年10月份的工资计算机退休费...
  4. 十大项目告诉你:室内定位将是重塑零售世界的下一场革命
  5. 山东省济宁市谷歌高清卫星地图下载
  6. WinXW_android
  7. stm32驱动sh36730x的驱动代码
  8. Star CCM+ 案例 - 旋风分离器 (cyclone separator)-1几何的构建
  9. 华为校招有多难?千军万马过独木桥!
  10. 【Apple Studio Display】-苹果5K显示器黑屏问题处理