最新更新:添加对android 6.0权限动态申请机制适配:github地址

仿微信相册选择图片,查看大图,写的不太好,希望评论指出不足,谅解,先介绍一下我的基本思路

第一步获取手机上的所有图片路径:

                Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;                ContentResolver contentResolver = getContentResolver();                //获取jpeg和png格式的文件,并且按照时间进行倒序                Cursor cursor = contentResolver.query(uri, null, MediaStore.Images.Media.MIME_TYPE + "=\"image/jpeg\" or " +                        MediaStore.Images.Media.MIME_TYPE + "=\"image/png\"", null, MediaStore.Images.Media.DATE_MODIFIED+" desc");                if (cursor != null){                    while (cursor.moveToNext()){                        //do something                    }                    handler.sendEmptyMessage(0);                }

我的存储格式

    /** 按时间排序的所有图片list */    private ArrayList<SingleImageModel> allImages;    /** 按目录排序的所有图片list */    private ArrayList<SingleImageDirectories> imageDirectories;     /**     * 一个文件夹中的图片数据实体     */    private class SingleImageDirectories{        /** 父目录的路径 */        public String directoryPath;        /** 目录下的所有图片实体 */        public ImageDirectoryModel images;    }

一个是全部图片的存储顺序,第二个是按照目录的图片存储顺序

第二步,获取到图片之后,放入到gridview中进行显示,但是BitmapFactory.decodeFile()函数会非常耗时,所以为了使得非常流畅的显示图片,创建一个类AlbumBitmapCacheHelper.class,用来异步加载图片,

该类使用LruCache<String,Bitmap> cache来缓存Bitmap,使得存储图片不会造成oom,我这里设置cache的初始大小为1/4的运行时内存

然后使用ThreadPoolExecutor线程池来处理图片的显示,线程池大小应该设置适中

做完这两件事情之后就可以用来加载图片了,方法getBitmap用来返回图片

        Bitmap bitmap = getBitmapFromCache(path, width, height);        //如果能够从缓存中获取符合要求的图片,则直接回调        if (bitmap != null) {        } else {            //新建线程放入线程池去处理该图片的显示        }        return bitmap;

如果cache中找不到该图片,则调用BitmapFactory.decodeFile()去加载图片,加载图片不能够直接加载原图,会造成OOM,所以要去计算压缩比

                        BitmapFactory.Options options = new BitmapFactory.Options();                        options.inJustDecodeBounds = true;                        BitmapFactory.decodeFile(path, options);                        options.inSampleSize = computeScale(options, width, height);                        options.inJustDecodeBounds = false;                        bitmap = BitmapFactory.decodeFile(path, options);                        //获取之后,放入缓存,以便下次继续使用                        if (bitmap != null && cache!=null) {                            cache.put(path, bitmap);                        }

方法computeScale()主要是计算图片最小的压缩比,

这样在gridview中的getview方法中去调用AlbumBitmapCacheHelper.class的getBitmap()方法即可,但是这样会有很多的问题:
一个问题就是图片显示会闪,这主要是由于getview的view的复用导致一个imageview被设置多次background,解决方法就是使用settag方法holder.iv_content.setTag(path);将要显示的imageview的tag设置为需要显示的图片路径,这样在回调的时候使用方法gridView.findViewWithTag(path),找到这个imageview进行显示,闪的问题就解决了
第二个问题就是加载速度很慢,拉的速度很快的情况下,图片要很久才会加载出来,特别是很大的图片,比如拍照和截图的照片,
解决方法:第一方案就是在AlbumBitmapCacheHelper类中维护一个ArrayList<String> currentShowString,在getview方法中,如果该图片要显示,则直接将path加入到该list中,同时如果这个view的tag不为空,说明该view的原来的path是不需要显示的,所以需要将这个path从list中删除:
            //优化显示效果            if(holder.iv_content.getTag() != null) {                String remove = (String) holder.iv_content.getTag();                AlbumBitmapCacheHelper.getInstance().removePathFromShowlist(remove);            }            AlbumBitmapCacheHelper.getInstance().addPathToShowlist(path);
这样在线程池中的处理方式就是先查看需要显示的path是否在list中,如果没有在list中,则该线程直接关闭,如果在list中,则显示该图片
if (!currentShowString.contains(path)||cache==null) {         return;}


第二个方案就是如果显示的图片很大,特别是拍照,截图的图片,decode有时会耗时几秒中,微信显示效果非常好,我自己想出来的处理的方式就是
***第一步,从应用的缓存temp目录下取,如果取不到,***第二步,计算图片的压缩比例samplesize,如果samplesize < 4,图片的BitmapFactory.decodeFile()时间短,直接返回图片,但是如果 samplesize > 4,执行第三步***第三步则将压缩后的图片存入temp目录下,以便下次快速取出这样显示图片的效果就出来了,显示的速度除了和微信一样第一次大图加载慢之外,之后的显示就能很快了,
  

                     if (!new File(CommonUtil.getDataPath()).exists())                        new File(CommonUtil.getDataPath()).mkdirs();                    //临时文件的文件名                    String tempPath = CommonUtil.getDataPath() + hash + ".temp";                    //如果该文件存在                    if (new File(tempPath).exists())                        bitmap = BitmapFactory.decodeFile(tempPath);                    ......                    //第三步,如果缩放比例大于4,该图的加载会非常慢,所以将该图保存到临时目录下以便下次的快速加载                        if (options.inSampleSize >= 4) {                            try {                                File file = new File(tempPath);                                if (!file.exists())                                    file.createNewFile();                                FileOutputStream fos = new FileOutputStream(file);                                ByteArrayOutputStream baos = new ByteArrayOutputStream();                                bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);                                fos.write(baos.toByteArray());                                fos.flush();                                fos.close();                            } catch (FileNotFoundException e) {                                e.printStackTrace();                            } catch (IOException e) {                                e.printStackTrace();                            }                        }

问题就差不多解决了

第三步大图的查看,大图只要是使用的网上找的ZoomImageView+viewpagger的组合,但是使用这个出现的问题就是很容易OOM,没办法,我的处理方式就是在点进去大图的时候

public void releaseHalfSizeCache() {    cache.resize((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));}

直接将cache的大小变成原来的一半,因为查看大图页,加载一张大图占用的内存就很大,这样显示效果页还凑合,有别的方法,一定要留言告诉我

注意:大图的查看由于需要通过intent传递数据,但是intent传递的数据大小不能太大,如果手机上有几千张图片,则数据量大小可能会超过intent所能传递的最大量,所以可以写入一个公共的地方,内存,数据库,文件都可以,

//TODO 这里由于涉及到intent传递的数据不能太大的问题,所以如果需要,这里要进行另外的处理,写入到内存或者写入到文件中  intent.putExtra(PickBigImagesActivity.EXTRA_DATA, getAllImagesFromCurrentDirectory());

我暂时没有处理~~


第四步就是图片选择完成之后,完成善后工作,将AlbumBitmapCacheHelper类中cache清空,差不多就这样了,还有很多的小问题,比如图片时间的显示,具体大家看源码


最新发现的问题:3.0以前GC操作需要很长时间,以常大于100ms,在执行GC时,程序就会出现卡的现象,3.0以后GC执行的时间通常在5ms以内,在以3.0以前的版本中,加载图片时,系统把bitmaps加载到Native中,并不受GC管理,需要手机释放,不然会遇到莫名奇妙的内存问题。3.0以后Bitmaps直接放到内存中在执行GC时,会及时清理无用的Bitmaps所占的内存,在初始化图片时把图片放到内存中,当加载完后,系统会把图片从内存转移到显存中,当你用内存测试工具时,会发现,在加载图片时,内存占用率很高,当加载完成后,内存使用量突然下来,当加载大量图片时会发现这种情况。

总而言之就是2.x版本的时候,就算你使用的是lru cache,bitmap还是不会被GC主动回收,必须要手动释放,所以如果需要适配2.x版本,该demo需要加入手动释放bitmap的操作



github地址

由于源码是android studio的工程,所以不能直接导入eclipse,必须要手动拷贝文件,在这里放出所有的文件,方便eclipse


更多相关文章

  1. android显示单位
  2. Android(安卓)Http访问网络 学习(慕课网学习)
  3. android中的LaunchMode详解----四种加载模式
  4. Android(安卓)RecyclerView之ListView显示(二)
  5. android图库竖屏不显示status bar的解决方法
  6. Android实战简易教程-第六十四枪(Android(安卓)APP 引导页实现-第
  7. Android百度地图开发之显示当前位置地图
  8. 【Android】ProgressBar圆形加载样式、进度条样式以及代码控制显
  9. android键盘谈起和收起监听

随机推荐

  1. 如何使用流星进行API调用并将header传递
  2. JavaScript数据类型的一些注意点(2)
  3. JavaScript初探系列之面向对象
  4. javascript的offset、client、scroll使用
  5. PHP的网站安全问题? (也可能适用于ASP / Ra
  6. 在javascript中调用带有双参数()的函数
  7. 汤姆大叔的深入理解JavaScript读后感一(1
  8. 奈何两字拉成桥、不得不双击两次才能激活
  9. 使用node.js托管一个asp.net或jsp网站。
  10. 用原生js实现数字自相加