图片、缓存-Android仿人人客户端(v5.7.1)——对从服务器端(网络)获取的图片进行本地双缓存处理(编码实现)-by小雨
废话就不多说了,开始。。。
转载请表明出处:http://blog.csdn.net/android_ls/article/details/8797740
明声:常有人说,能把杂复的情事,用最简略艰深式方向大家描述清晰,此乃“牛人”或称“师大别级”。我自知离那个别级还差很远。因此,我宣布的仿人人Android客户端系列博文,是与有定一Andoird基础知识的友人来分享的。
这篇是基于上一篇Android仿人人客户端(v5.7.1)——对从服务器端(络网)得获的图片行进地本双缓存理处(流程图或活动图)来行进讲授,没看过的可以先浏览下上一篇博文,其实我个人得觉图片双缓存理处这块,一张流程图已足以明说一切。至于码编现实,不同的人有不同的现实式方,面下我就和大家聊一聊我的现实式方:
一、图片双缓存理处,类图如下:
二、络网图片地本双缓存的码编现实(以得获户用图像为例):
1、出发要需表现户用图像的请求
String headUrl = user.getHeadurl(); LogUtil.i(TAG, "headUrl = " + user.getHeadurl()); // 户用图像的巨细48x48,单位为dip,转换为px int widthPx = DensityUtil.dip2px(mContext, 48); // 要一张角圆高量质的图片 ImageInfo imgInfo = new ImageInfo(mLeftPanelLayout.ivUserIcon, headUrl, widthPx, widthPx, true, false); mImageLoader.displayImage(imgInfo);
注:mLeftPanelLayout.ivUserIcon为ImageView;ImageInfo对象封装了图片请求参数。
2、根据URL从存内缓存中得获Bitmap对象,找到了Bitmap对象,用ImageView对象表现图像,到这里止终。
Bitmap bitmap = memoryCache.get(url); if (bitmap != null) { imageView.setImageBitmap(bitmap); }
注:memoryCache是MemoryCache(存内缓存类)的对象引用。
3 、没有从缓存中找到了Bitmap对象,则根据URL从文件缓存中得获File对象,将File对象解码(剖析)成Bitmap对象,用ImageView对象表现户用图像,到这里止终。
final File file = fileCache.getFile(url); if(file.exists()){ String pathName = file.getAbsolutePath(); System.out.println("pathName = " + pathName); System.out.println("file.length() = " + file.length()); bitmap = BitmapFactory.decodeFile(pathName); imageView.setImageBitmap(bitmap); }
注:fileCache为文件缓存类的引用
4、没有从文件缓存中找到File对象,则开启络网请求务业线程。
// 开启线程加载图片 try { AsyncBaseRequest asyncRequest = new AsyncHttpGet(url, null, null, new ResultCallback() { @Override public void onSuccess(Object obj) { } @Override public void onFail(int errorCode) { System.out.println("Loading image error. errorCode = " + errorCode); } }); mDefaultThreadPool.execute(asyncRequest); mAsyncRequests.add(asyncRequest); } catch (IOException e) { e.printStackTrace(); }
5、络网请求返回的图片数据流可能会很大,直接解码成生Bitmap对象,可能会形成OOM。因此,要根据指定的缩压比例,得获适合的Bitmap
Bitmap bitmap = BitmapUtil.decodeStream((InputStream) obj, imgInfo.getWidth(), imgInfo.getHeight());
6、上一步理处后过,可能解码成生的Bitmap对象还会很大,可能还会形成OOM,因此,对Bitmap对象再次行进量质缩压。
if (imgInfo.isCompress()) { // 对Bitmap行进量质缩压 bitmap = BitmapUtil.compressBitmap(bitmap); }
7、行进地本文件缓存
try { fileCache.writeToFile(inStream, file); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
8、行进地本存内缓存
// 将数据流将其转换成Bitmap bitmap = BitmapFactory.decodeStream(inStream); // 存入存内缓存中 memoryCache.put(url, bitmap);
9、用ImageView对象表现户用图像,到这里止终。
// 用ImageView对象表现图片 final Bitmap btm = bitmap; mHandler.post(new Runnable() { @Override public void run() { imageView.setImageBitmap(btm); } });
加载图片的整完方法,码代如下:
/** * 加载图片 * @param imgInfo 图片信息 */ public void displayImage(final ImageInfo imgInfo) { final ImageView imageView = imgInfo.getImageView(); final String url = imgInfo.getUrl(); imageViews.put(imageView, url); // 从存内缓存中找查 Bitmap bitmap = memoryCache.get(url); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { // 从文件缓存中找查 final File file = fileCache.getFile(url); if (file.exists()) { String pathName = file.getAbsolutePath(); System.out.println("pathName = " + pathName); System.out.println("file.length() = " + file.length()); bitmap = BitmapFactory.decodeFile(pathName); imageView.setImageBitmap(bitmap); } else { // 开启线程加载图片 try { AsyncBaseRequest asyncRequest = new AsyncHttpGet(url, null, null, new ResultCallback() { @Override public void onSuccess(Object obj) { if (obj == null || !(obj instanceof InputStream)) { System.out.println("Loading image return Object is null or not is InputStream."); return; } try { // 根据指定的缩压比例,得获适合的Bitmap Bitmap bitmap = BitmapUtil.decodeStream((InputStream) obj, imgInfo.getWidth(), imgInfo.getHeight()); if (imgInfo.isRounded()) { // 将图片酿成角圆 // bitmap = BitmapUtil.drawRoundCorner(bitmap, 8); bitmap = BitmapUtil.drawRoundBitmap(bitmap, 8); } if (imgInfo.isCompress()) { // 对Bitmap行进量质缩压 bitmap = BitmapUtil.compressBitmap(bitmap); } // 将Bitmap转换成ByteArrayInputStream ByteArrayOutputStream outStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); // 将行进量质缩压后的数据写入文件(文件缓存) fileCache.writeToFile(inStream, file); // 存入存内缓存中 memoryCache.put(url, bitmap); // 止防图片错位 String tag = imageViews.get(imageView); if (tag == null || !tag.equals(url)) { System.out.println("tag is null or url and ImageView disaccord."); return; } // 用ImageView对象表现图片 final Bitmap btm = bitmap; mHandler.post(new Runnable() { @Override public void run() { imageView.setImageBitmap(btm); } }); } catch (IOException e) { // 这里不做理处,因为默许表现的图片在xml组件配置里已设置 e.printStackTrace(); } } @Override public void onFail(int errorCode) { System.out.println("Loading image error. errorCode = " + errorCode); } }); mDefaultThreadPool.execute(asyncRequest); mAsyncRequests.add(asyncRequest); } catch (IOException e) { e.printStackTrace(); } } } }
三、在上述务业理处中程过,碰到的问题及处理思绪(记载理处进程)
1、根据指定的缩压比例,得获适合的Bitmap,浏览如下码代:
/** * 根据指定的缩压比例,得获适合的Bitmap * @param inStream InputStream * @param width 指定的宽度 * @param height 指定的度高 */ public static Bitmap decodeStream(InputStream inStream, int width, int height) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(inStream, null, options); int w = options.outWidth; int h = options.outHeight; // 从服务器端得获的图片巨细为:80x120 // 我们想要的图片巨细为:40x40 // 缩放比:120/40 = 3,也就是说我们要的图片巨细为原图的1/3 // 缩放比。由于是定固比例缩放,只用高或者宽其中一个数据行进盘算可即 int ratio = 1; // 默以为不缩放 if (w >= h && w > width) { ratio = (int) (w / width); } else if (w < h && h > height) { ratio = (int) (h / height); } if (ratio <= 0) { ratio = 1; } System.out.println("图片的缩放比例值ratio = " + ratio); options.inJustDecodeBounds = false; // 属性值inSampleSize表示缩略图巨细为原始图片巨细的几分之一,即如果这个值为2, // 则掏出的缩略图的宽和高都是原始图片的1/2,图片巨细就为原始巨细的1/4。 options.inSampleSize = ratio; return BitmapFactory.decodeStream(inStream, null, options); }
注:inStream为从络网得获后,直接传进来的。
行运面下的后,返回的Bitmap对象为null。究其原因,在设置 options.inJustDecodeBounds = true后,我们调用了BitmapFactory.decodeStream(inStream, null, options)方法得获图片的巨细,但是该方法在执行完后,该应在内部把传进去的InputStream关闭掉了。第二次的时候就读不到数据了。处理思绪,将从络网得获到的数据流先存保起来。处理方法一:
/** * 根据指定的缩压比例,得获适合的Bitmap(方法一) * @param file File * @param width 指定的宽度 * @param height 指定的度高 * @return Bitmap */ public static Bitmap decodeStream(File file, int width, int height) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getAbsolutePath(), options); int w = options.outWidth; int h = options.outHeight; // 从服务器端得获的图片巨细为:80x120 // 我们想要的图片巨细为:40x40 // 缩放比:120/40 = 3,也就是说我们要的图片巨细为原图的1/3 // 缩放比。由于是定固比例缩放,只用高或者宽其中一个数据行进盘算可即 int ratio = 1; // 默以为不缩放 if (w >= h && w > width) { ratio = (int) (w / width); } else if (w < h && h > height) { ratio = (int) (h / height); } if (ratio <= 0) { ratio = 1; } System.out.println("图片的缩放比例值ratio = " + ratio); options = new BitmapFactory.Options(); options.inJustDecodeBounds = false; // 属性值inSampleSize表示缩略图巨细为原始图片巨细的几分之一,即如果这个值为2, // 则掏出的缩略图的宽和高都是原始图片的1/2,图片巨细就为原始巨细的1/4。 options.inSampleSize = ratio; return BitmapFactory.decodeFile(file.getAbsolutePath(), options); }
处理方法二:
/** * 根据指定的缩压比例,得获适合的Bitmap(方法二) * @param inStream InputStream * @param width 指定的宽度 * @param height 指定的度高 * @return Bitmap * @throws IOException */ public static Bitmap decodeStream(InputStream inStream, int width, int height) throws IOException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 从输入流读取数据 byte[] data = StreamTool.read(inStream); BitmapFactory.decodeByteArray(data, 0, data.length, options); int w = options.outWidth; int h = options.outHeight; // 从服务器端得获的图片巨细为:80x120 // 我们想要的图片巨细为:40x40 // 缩放比:120/40 = 3,也就是说我们要的图片巨细为原图的1/3 // 缩放比。由于是定固比例缩放,只用高或者宽其中一个数据行进盘算可即 int ratio = 1; // 默以为不缩放 if (w >= h && w > width) { ratio = (int) (w / width); } else if (w < h && h > height) { ratio = (int) (h / height); } if (ratio <= 0) { ratio = 1; } System.out.println("图片的缩放比例值ratio = " + ratio); options.inJustDecodeBounds = false; // 属性值inSampleSize表示缩略图巨细为原始图片巨细的几分之一,即如果这个值为2, // 则掏出的缩略图的宽和高都是原始图片的1/2,图片巨细就为原始巨细的1/4。 options.inSampleSize = ratio; return BitmapFactory.decodeByteArray(data, 0, data.length); }
处理方法三:从络网返回的数据流中只读取图片的信息(宽度和度高),盘算缩压比例,后之再次从络网读取数据按第一次盘算出的缩压比例,得获适合的Bitmap。(这个是下下策,要问访两次络网)
2、对Bitmap行进量质缩压,浏览如下码代:
/** * 对Bitmap行进量质缩压 * @param bitmap Bitmap * @return ByteArrayInputStream */ public static Bitmap compressBitmap(Bitmap bitmap) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); // 图片量质默许值为100,表示不缩压 int quality = 100; // PNG是无损的,将会略忽量质设置。因此,这里设置为JPEG bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream); // 判断缩压后图片的巨细否是大于100KB,大于则继承缩压 while (outStream.toByteArray().length / 1024 > 100) { outStream.reset(); // 缩压quality%,把缩压后的数据寄存到baos中 bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream); quality -= 10; } System.out.println("quality = " + quality); byte[] data = outStream.toByteArray(); return BitmapFactory.decodeByteArray(data, 0, data.length); }
注意: bitmap.compress(Bitmap.CompressFormat.PNG, quality, outStream);如果这么写,是没有缩压效果的。因为PNG是无损的,将会略忽量质设置。
四、上述讲授中涉及到的类,整完的源文件如下:
加载(装载)图片类
package com.everyone.android.bitmap; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.widget.ImageView; import com.everyone.android.AppBaseActivity; import com.everyone.android.callback.ResultCallback; import com.everyone.android.entity.ImageInfo; import com.everyone.android.net.AsyncBaseRequest; import com.everyone.android.net.AsyncHttpGet; import com.everyone.android.net.DefaultThreadPool; import com.everyone.android.utils.BitmapUtil; /** * 能功描述:加载(装载)图片 * * 在前以,一个非常行流的存内缓存的现实是用使SoftReference or WeakReference ,但是种这方法当初其实不荐推。 * 从Android 2.3开始,垃圾收回器会更加踊跃的去收回软引用和弱引用引用的对象,这样致导种这做法相称的无效。 * 另外,在Android 3.0之前,图片数据存保在地本存内中,它们不是以一种可见预的式方来放释的, * 这样可能会致导用应存内的消耗量现出暂短的超限,用应程序溃崩 。 * * @author android_ls */ public class ImageLoader { /** * 存内缓存 */ private MemoryCache memoryCache; /** * 文件缓存 */ private FileCache fileCache; /** * 寄存图片的表现视图ImageView和图片的URL */ private Map<ImageView, String> imageViews = Collections.synchronizedMap(new LinkedHashMap<ImageView, String>()); private List<AsyncBaseRequest> mAsyncRequests; private DefaultThreadPool mDefaultThreadPool; private Handler mHandler; public ImageLoader(AppBaseActivity activity) { this.memoryCache = new MemoryCache(); this.fileCache = new FileCache(activity.getContext()); this.mAsyncRequests = activity.getAsyncRequests(); this.mDefaultThreadPool = activity.getDefaultThreadPool(); this.mHandler = activity.getHandler(); } /** * 加载图片 * @param imgInfo 图片信息 */ public void displayImage(final ImageInfo imgInfo) { final ImageView imageView = imgInfo.getImageView(); final String url = imgInfo.getUrl(); imageViews.put(imageView, url); // 从存内缓存中找查 Bitmap bitmap = memoryCache.get(url); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { // 从文件缓存中找查 final File file = fileCache.getFile(url); if (file.exists()) { String pathName = file.getAbsolutePath(); System.out.println("pathName = " + pathName); System.out.println("file.length() = " + file.length()); SimpleDateFormat mDateFormat = new SimpleDateFormat ("yyyy年MM月dd日 HH:mm:ss"); System.out.println("file.lastModified() = " + mDateFormat.format(file.lastModified())); bitmap = BitmapFactory.decodeFile(pathName); imageView.setImageBitmap(bitmap); } else { // 开启线程加载图片 try { AsyncBaseRequest asyncRequest = new AsyncHttpGet(url, null, null, new ResultCallback() { @Override public void onSuccess(Object obj) { if (obj == null || !(obj instanceof InputStream)) { System.out.println("Loading image return Object is null or not is InputStream."); return; } try { // 根据指定的缩压比例,得获适合的Bitmap Bitmap bitmap = BitmapUtil.decodeStream((InputStream) obj, imgInfo.getWidth(), imgInfo.getHeight()); if (imgInfo.isRounded()) { // 将图片酿成角圆 // bitmap = BitmapUtil.drawRoundCorner(bitmap, 8); bitmap = BitmapUtil.drawRoundBitmap(bitmap, 8); } if (imgInfo.isCompress()) { // 对Bitmap行进量质缩压 bitmap = BitmapUtil.compressBitmap(bitmap); } // 将Bitmap转换成ByteArrayInputStream ByteArrayOutputStream outStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); // 将行进量质缩压后的数据写入文件(文件缓存) fileCache.writeToFile(inStream, file); // 存入存内缓存中 memoryCache.put(url, bitmap); // 止防图片错位 String tag = imageViews.get(imageView); if (tag == null || !tag.equals(url)) { System.out.println("tag is null or url and ImageView disaccord."); return; } // 用ImageView对象表现图片 final Bitmap btm = bitmap; mHandler.post(new Runnable() { @Override public void run() { imageView.setImageBitmap(btm); } }); } catch (IOException e) { // 这里不做理处,因为默许表现的图片在xml组件配置里已设置 e.printStackTrace(); } } @Override public void onFail(int errorCode) { System.out.println("Loading image error. errorCode = " + errorCode); } }); mDefaultThreadPool.execute(asyncRequest); mAsyncRequests.add(asyncRequest); } catch (IOException e) { e.printStackTrace(); } } } } }
存内缓存类
package com.everyone.android.bitmap; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import android.graphics.Bitmap; import android.util.Log; /** * 能功描述:存内缓存类 * * @author android_ls */ public class MemoryCache { /** * 打印LOG的TAG */ private static final String TAG = "MemoryCache"; /** * 放入缓存时是个同步操作 * LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近用使次数由少到多排列, * 这样的利益是如果要将缓存中的元素替换,则先遍历出最近最少用使的元来素替换以进步率效 */ private Map<String, Bitmap> cacheMap = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true)); // 缓存只能占用的最大堆存内 private long maxMemory; public MemoryCache() { // 用使25%的可用的堆巨细 maxMemory = Runtime.getRuntime().maxMemory() / 4; Log.i(TAG, "MemoryCache will use up to " + (maxMemory / 1024 / 1024) + "MB"); } /** * 根据key得获响应的图片 * @param key * @return Bitmap */ public Bitmap get(String key) { if (!cacheMap.containsKey(key)){ return null; } return cacheMap.get(key); } /** * 添加图片到缓存 * @param key * @param bitmap */ public synchronized void put(String key, Bitmap bitmap) { checkSize(); cacheMap.put(key, bitmap); Log.i(TAG, "cache size=" + cacheMap.size() + " bitmap size = " + getBitmapSize(bitmap)); } /** * 严格控制堆存内,如果过超将首先替换最近最少用使的那个图片缓存 */ private void checkSize() { long count = 0; Iterator<Entry<String, Bitmap>> iterator = cacheMap.entrySet().iterator(); while (iterator.hasNext()) { Entry<String, Bitmap> entry = iterator.next(); count += getBitmapSize(entry.getValue()); } Log.i(TAG, "cache size=" + count + " length=" + cacheMap.size()); if (count > maxMemory) { while (iterator.hasNext()) { Entry<String, Bitmap> entry = iterator.next(); count -= getBitmapSize(entry.getValue()); iterator.remove(); if (count <= maxMemory) { System.out.println("够用了,不用在删除了"); break; } } Log.i(TAG, "Clean cache. New size " + cacheMap.size()); } } /** * 得获bitmap的字节巨细 * @param bitmap * @return */ private long getBitmapSize(Bitmap bitmap) { if (bitmap == null) { return 0; } return bitmap.getRowBytes() * bitmap.getHeight(); } /** * 空清缓存 */ public void clear() { cacheMap.clear(); } }
络网下载文件地本缓存类
package com.everyone.android.bitmap; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Comparator; import android.content.Context; import android.os.StatFs; /** * 能功描述:络网下载文件地本缓存类 * * @author android_ls */ public class FileCache { /** * 地本与我们用应程序关相文件寄存的根目录 */ private static final String ROOT_DIR_PATH = "CopyEveryone"; /** * 下载文件寄存的目录 */ private static final String IMAGE_DOWNLOAD_CACHE_PATH = ROOT_DIR_PATH + "/Download/cache"; /** * 默许的盘磁缓存巨细(20MB) */ private static final int DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 20; /** * 缓存文件寄存目录 */ private File cacheDir; /** * 缓存根目录 */ private String cacheRootDir; private Context mContext; public FileCache(Context context) { mContext = context; if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { cacheRootDir = android.os.Environment.getExternalStorageDirectory().getAbsolutePath(); } else { cacheRootDir = mContext.getCacheDir().getAbsolutePath(); } cacheDir = new File(cacheRootDir + File.separator + IMAGE_DOWNLOAD_CACHE_PATH); // 检测文件缓存目录否是存在,不存在则创立 if (!cacheDir.exists()) { cacheDir.mkdirs(); } } /** * 得获下载的文件要寄存的缓存目录 * /mnt/sdcard/CopyEveryone/Download/cache * @return 缓存目录的全路径 */ public String getCacheDirPath() { return cacheDir.getAbsolutePath(); } /** * 根据URL从文件缓存中得获文件 * @param url url的hashCode为缓存的文件名 */ public File getFile(String url) { if (!cacheDir.exists()) { cacheDir.mkdirs(); } String filename = String.valueOf(url.hashCode()); File file = new File(cacheDir, filename); return file; } /** * 盘算存储可用的巨细 * @return */ public long getAvailableMemorySize() { StatFs stat = new StatFs(cacheRootDir); long blockSize = stat.getBlockSize(); long availableBlocks = stat.getAvailableBlocks(); return availableBlocks * blockSize; } /** * 将指定的数据写入文件 * @param inputStream InputStream * @param outputStream OutputStream * @throws IOException */ public synchronized void writeToFile(InputStream inputStream, File file) throws IOException { int fileSize = inputStream.available(); System.out.println("fileSize = " + fileSize); long enabledMemory = getAvailableMemorySize(); System.out.println("前当可用盘硬: " + (enabledMemory/1024/1024)); // 单位:MB // 前当可用存储空间缺乏20M if(DEFAULT_DISK_CACHE_SIZE > enabledMemory){ if (fileSize > enabledMemory) { // 检测可用空间巨细,若不够用则删除最早的文件 File[] files = cacheDir.listFiles(); Arrays.sort(files, new FileLastModifSort()); int length = files.length; for (int i = 0; i < length; i++) { files[i].delete(); length = files.length; enabledMemory = getAvailableMemorySize(); System.out.println("前当可用存内: " + enabledMemory); if (fileSize <= enabledMemory) { System.out.println("够用了,不用在删除了"); break; } } } } else { int count = 0; File[] files = cacheDir.listFiles(); for (int i = 0; i < files.length; i++) { count += files[i].length(); } System.out.println("file cache size = " + count); // 用使的空间大于下限 enabledMemory = DEFAULT_DISK_CACHE_SIZE - count; if(fileSize > enabledMemory){ Arrays.sort(files, new FileLastModifSort()); int length = files.length; for (int i = 0; i < length; i++) { count -= files[i].length(); files[i].delete(); length = files.length; enabledMemory = DEFAULT_DISK_CACHE_SIZE - count; if (fileSize <= enabledMemory) { System.out.println("够用了,不用在删除了"); break; } } } } if(enabledMemory == 0){ return; } // 将数据写入文件存保 FileOutputStream outStream = new FileOutputStream(file); byte[] buffer = new byte[1024]; int len = 0; while ((len = inputStream.read(buffer)) != -1) { outStream.write(buffer, 0, len); } outStream.flush(); outStream.close(); inputStream.close(); // 设置最后改修的间时 long newModifiedTime = System.currentTimeMillis(); file.setLastModified(newModifiedTime); System.out.println("file.length() = " + file.length()); SimpleDateFormat mDateFormat = new SimpleDateFormat ("yyyy年MM月dd日 HH:mm:ss"); System.out.println("writeToFile file.lastModified() = " + mDateFormat.format(file.lastModified())); } /** * 根据文件的最后改修间时行进排序 * @author android_ls * */ class FileLastModifSort implements Comparator<File> { public int compare(File file1, File file2) { if (file1.lastModified() > file2.lastModified()) { return 1; } else if (file1.lastModified() == file2.lastModified()) { return 0; } else { return -1; } } } /** * 空清缓存的文件 */ public void clear() { if (!cacheDir.exists()) { return; } File[] files = cacheDir.listFiles(); if (files != null) { for (File f : files) { f.delete(); } } } }
图片信息实体类
package com.everyone.android.entity; import android.widget.ImageView; /** * 能功描述:图片信息实体类 * * @author android_ls */ public class ImageInfo { private int id; // 独一标识 private ImageView imageView; // 用于表现的组件 private String url; // 络网URL private int width; // 宽度 private int height; // 度高 private boolean rounded; // 否是要转换成角圆 private boolean compress; // 否是要行进量质缩压 public ImageInfo(ImageView imageView, String url) { this.imageView = imageView; this.url = url; } public ImageInfo() { } public ImageInfo(ImageView imageView, String url, int width, int height, boolean rounded, boolean compress) { this.imageView = imageView; this.url = url; this.width = width; this.height = height; this.rounded = rounded; this.compress = compress; } public ImageInfo(ImageView imageView, String url, boolean rounded) { this.imageView = imageView; this.url = url; this.rounded = rounded; } public ImageInfo(ImageView imageView, String url, int width, int height) { this.imageView = imageView; this.url = url; this.width = width; this.height = height; } public ImageInfo(ImageView imageView, String url, int width, int height, boolean rounded) { this.imageView = imageView; this.url = url; this.width = width; this.height = height; this.rounded = rounded; } public boolean isCompress() { return compress; } public void setCompress(boolean compress) { this.compress = compress; } public int getId() { return id; } public void setId(int id) { this.id = id; } public ImageView getImageView() { return imageView; } public void setImageView(ImageView imageView) { this.imageView = imageView; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public boolean isRounded() { return rounded; } public void setRounded(boolean rounded) { this.rounded = rounded; } }
Bitmap加工理处具工类
package com.everyone.android.utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; /** * 能功描述:Bitmap加工理处具工类 * @author android_ls * */ public class BitmapUtil { /** * 将图片酿成角圆(方法一) * @param bitmap Bitmap * @param pixels 角圆的弧度 * @return 角圆图片 */ public static Bitmap drawRoundBitmap(Bitmap bitmap, float pixels) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); // paint.setColor()的参数,除不能为Color.TRANSPARENT外,可以意任写 paint.setColor(Color.RED); canvas.drawRoundRect(rectF, pixels, pixels, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } /** * 将图片酿成角圆(方法二) * @param bitmap Bitmap * @param pixels 角圆的弧度 * @return 角圆图片 */ public static Bitmap drawRoundCorner(Bitmap bitmap, float pixels) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); RectF outerRect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // paint.setColor()的参数,除不能为Color.TRANSPARENT外,可以意任写 paint.setColor(Color.WHITE); canvas.drawRoundRect(outerRect, pixels, pixels, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); Drawable drawable = new BitmapDrawable(bitmap); drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); canvas.saveLayer(outerRect, paint, Canvas.ALL_SAVE_FLAG); drawable.draw(canvas); canvas.restore(); return output; } /** * 对Bitmap行进量质缩压 * @param bitmap Bitmap * @return ByteArrayInputStream */ public static Bitmap compressBitmap(Bitmap bitmap) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); // 图片量质默许值为100,表示不缩压 int quality = 100; // PNG是无损的,将会略忽量质设置。因此,这里设置为JPEG bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream); // 判断缩压后图片的巨细否是大于100KB,大于则继承缩压 while (outStream.toByteArray().length / 1024 > 100) { outStream.reset(); // 缩压quality%,把缩压后的数据寄存到baos中 bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream); quality -= 10; } System.out.println("quality = " + quality); byte[] data = outStream.toByteArray(); return BitmapFactory.decodeByteArray(data, 0, data.length); } /** * 根据指定的缩压比例,得获适合的Bitmap(方法一) * @param file File * @param width 指定的宽度 * @param height 指定的度高 * @return Bitmap */ public static Bitmap decodeStream(File file, int width, int height) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getAbsolutePath(), options); int w = options.outWidth; int h = options.outHeight; // 从服务器端得获的图片巨细为:80x120 // 我们想要的图片巨细为:40x40 // 缩放比:120/40 = 3,也就是说我们要的图片巨细为原图的1/3 // 缩放比。由于是定固比例缩放,只用高或者宽其中一个数据行进盘算可即 int ratio = 1; // 默以为不缩放 if (w >= h && w > width) { ratio = (int) (w / width); } else if (w < h && h > height) { ratio = (int) (h / height); } if (ratio <= 0) { ratio = 1; } System.out.println("图片的缩放比例值ratio = " + ratio); options = new BitmapFactory.Options(); options.inJustDecodeBounds = false; // 属性值inSampleSize表示缩略图巨细为原始图片巨细的几分之一,即如果这个值为2, // 则掏出的缩略图的宽和高都是原始图片的1/2,图片巨细就为原始巨细的1/4。 options.inSampleSize = ratio; return BitmapFactory.decodeFile(file.getAbsolutePath(), options); } /** * 根据指定的缩压比例,得获适合的Bitmap(方法二) * @param inStream InputStream * @param width 指定的宽度 * @param height 指定的度高 * @return Bitmap * @throws IOException */ public static Bitmap decodeStream(InputStream inStream, int width, int height) throws IOException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 从输入流读取数据 byte[] data = StreamTool.read(inStream); BitmapFactory.decodeByteArray(data, 0, data.length, options); int w = options.outWidth; int h = options.outHeight; // 从服务器端得获的图片巨细为:80x120 // 我们想要的图片巨细为:40x40 // 缩放比:120/40 = 3,也就是说我们要的图片巨细为原图的1/3 // 缩放比。由于是定固比例缩放,只用高或者宽其中一个数据行进盘算可即 int ratio = 1; // 默以为不缩放 if (w >= h && w > width) { ratio = (int) (w / width); } else if (w < h && h > height) { ratio = (int) (h / height); } if (ratio <= 0) { ratio = 1; } System.out.println("图片的缩放比例值ratio = " + ratio); options.inJustDecodeBounds = false; // 属性值inSampleSize表示缩略图巨细为原始图片巨细的几分之一,即如果这个值为2, // 则掏出的缩略图的宽和高都是原始图片的1/2,图片巨细就为原始巨细的1/4。 options.inSampleSize = ratio; return BitmapFactory.decodeByteArray(data, 0, data.length); } /** * 根据指定的缩压比例,得获适合的Bitmap(会错出的方法,仅用于测试) * @param inStream * @param width * @param height * @return * @throws IOException */ public static Bitmap decodeStreamError(InputStream inStream, int width, int height) throws IOException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(inStream, null, options); int w = options.outWidth; int h = options.outHeight; // 从服务器端得获的图片巨细为:80x120 // 我们想要的图片巨细为:40x40 // 缩放比:120/40 = 3,也就是说我们要的图片巨细为原图的1/3 // 缩放比。由于是定固比例缩放,只用高或者宽其中一个数据行进盘算可即 int ratio = 1; // 默以为不缩放 if (w >= h && w > width) { ratio = (int) (w / width); } else if (w < h && h > height) { ratio = (int) (h / height); } if (ratio <= 0) { ratio = 1; } System.out.println("图片的缩放比例值ratio = " + ratio); options.inJustDecodeBounds = false; // 属性值inSampleSize表示缩略图巨细为原始图片巨细的几分之一,即如果这个值为2, // 则掏出的缩略图的宽和高都是原始图片的1/2,图片巨细就为原始巨细的1/4。 options.inSampleSize = ratio; return BitmapFactory.decodeStream(inStream, null, options); } }
单位转换具工类
package com.everyone.android.utils; import android.content.Context; /** * 能功描述:单位转换具工类 * @author android_ls * */ public class DensityUtil { /** * 将单位为dip的值转换成单位为px的值 * @param context Context * @param dipValue dip值 * @return px值 */ public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } /** * 将单位为px的值转换成单位为dip的值 * @param context Context * @param pxValue 像素值 * @return dip值 */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 将px值转换为sp值,保障文字巨细稳定 * * @param pxValue * @param fontScale(DisplayMetrics类中属性scaledDensity) * @return */ public static int px2sp(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 将sp值转换为px值,保障文字巨细稳定 * * @param spValue * @param fontScale(DisplayMetrics类中属性scaledDensity) * @return */ public static int sp2px(Context context, float spValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (spValue * scale + 0.5f); } }
数据流理处具工类数据流理处具工类数据流理处具工类
package com.everyone.android.utils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; /** * 能功描述:数据流理处具工类 * @author android_ls */ public final class StreamTool { /** * 从输入流读取数据 * * @param inStream * @return * @throws IOException * @throws Exception */ public static byte[] read(InputStream inStream) throws IOException { ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); return outSteam.toByteArray(); } }
五、络网模块改修的文件源码:
络网请求线程基类
package com.everyone.android.net; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.HttpURLConnection; import java.util.Map; import org.json.JSONException; import com.everyone.android.callback.ParseCallback; import com.everyone.android.callback.ResultCallback; import com.everyone.android.utils.Constant; import com.everyone.android.utils.LogUtil; import com.everyone.android.utils.StreamTool; /** * 能功描述:络网请求线程基类 * @author android_ls * */ public abstract class AsyncBaseRequest implements Runnable, Serializable { /** * */ private static final long serialVersionUID = 1L; /** * LOG打印标签 */ private static final String TAG = "AsyncBaseRequest"; /** * 络网连接超时,默许值为5秒 */ protected int connectTimeout = 5 * 1000; /** * 络网数据读取超时,默许值为5秒 */ protected int readTimeout = 5 * 1000; private boolean interrupted; public boolean isInterrupted() { return interrupted; } public void setInterrupted(boolean interrupted) { this.interrupted = interrupted; } protected void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; } protected void setReadTimeout(int readTimeout) { this.readTimeout = readTimeout; } protected String requestUrl; protected Map<String, String> parameter; private ParseCallback parseHandler; private ResultCallback requestCallback; protected HttpURLConnection mHttpURLConn; protected InputStream mInStream; public AsyncBaseRequest(String url, Map<String, String> parameter, ParseCallback handler, ResultCallback requestCallback) { this.parseHandler = handler; this.requestUrl = url; this.parameter = parameter; this.requestCallback = requestCallback; } /** * 发送络网请求 * * @return 络网请求返回的InputStream数据流 * @throws IOException */ protected abstract InputStream getRequestResult() throws IOException; @Override public void run() { if (interrupted) { LogUtil.i(TAG, "问访络网前中断务业理处线程(止终)"); return; } try { mInStream = getRequestResult(); if (mInStream != null) { if (interrupted) { LogUtil.i(TAG, "读取数据前中断务业理处线程(止终)"); return; } Object obj = null; if(parseHandler != null){ byte[] data = StreamTool.read(mInStream); if (interrupted) { LogUtil.i(TAG, "剖析数据前中断务业理处线程(止终)"); return; } String result = new String(data); obj = parseHandler.parse(result); } if (interrupted) { LogUtil.i(TAG, "刷新UI前中断务业理处线程(止终)"); return; } if(obj != null){ requestCallback.onSuccess(obj); } else { requestCallback.onSuccess(mInStream); } } else { LogUtil.i(TAG, "get InputStream By HttpURLConnection return result is NULL."); requestCallback.onFail(Constant.NETWORK_REQUEST_RETUN_NULL); // 络网请求返回NULL } } catch (JSONException e) { requestCallback.onFail(Constant.NETWORK_REQUEST_RESULT_PARSE_ERROR); // 络网请求返回结果剖析错出 e.printStackTrace(); } catch (IOException e) { requestCallback.onFail(Constant.NETWORK_REQUEST_IOEXCEPTION_CODE); // IO常异标识 e.printStackTrace(); } } public HttpURLConnection getRequestConn() { return mHttpURLConn; } }
通过HTTP议协发送GET请求
package com.everyone.android.net; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.Map; import org.apache.http.protocol.HTTP; import com.everyone.android.callback.ParseCallback; import com.everyone.android.callback.ResultCallback; /** * 能功描述:通过HTTP议协发送GET请求 * @author android_ls * */ public class AsyncHttpGet extends AsyncBaseRequest { /** * */ private static final long serialVersionUID = 2L; public AsyncHttpGet(String url, Map<String, String> parameter, ParseCallback handler, ResultCallback requestCallback) throws IOException { super(url, parameter, handler, requestCallback); } @Override protected InputStream getRequestResult() throws IOException { StringBuilder sb = new StringBuilder(requestUrl); if (parameter != null && !parameter.isEmpty()) { sb.append('?'); for (Map.Entry<String, String> entry : parameter.entrySet()) { sb.append(entry.getKey()).append('=').append(URLEncoder.encode(entry.getValue(), HTTP.UTF_8)).append('&'); } sb.deleteCharAt(sb.length() - 1); } URL url = new URL(sb.toString()); mHttpURLConn = (HttpURLConnection) url.openConnection(); mHttpURLConn.setConnectTimeout(connectTimeout); mHttpURLConn.setRequestMethod("GET"); if (mHttpURLConn.getResponseCode() == HttpURLConnection.HTTP_OK) { return mHttpURLConn.getInputStream(); } return null; } }
六、行运后的效果图:
图片双缓存这块,拖了良久,这一篇博文我花了一晚上间时,搞了一个宵通,于终写完了。天亮了,说:友人们,晚安!
文章结束给大家分享下程序员的一些笑话语录:
人工智能今天的发展水平:8乘8的国际象棋盘其实是一个体现思维与创意的强大媒介。象棋里蕴含了天文数字般的变化。卡斯帕罗夫指出,国际象棋的合法棋步共有1040。在棋局里每算度八步棋,里面蕴含的变化就已经超过银河系里的繁星总数。而地球上很少有任何数量达到这个级别。在金融危机之前,全世界的财富总和大约是1014人民币,而地球人口只有1010。棋盘上,所有可能的棋局总数达到10120,这超过了宇宙里所有原子的总数!经典语录网