android图片系统解决方案-从采集到显示

概述

Android上图片涉及到的要点:html

  • 基础
  • 自定义相机拍照
  • 调用系统相机拍照
  • 图片选择
  • 裁剪
  • 压缩
  • 上传
  • 服务端处理与下载
  • 显示与内存-普通图与超大图
  • 文件夹管理
  • 内置图片管理与包大小

基础

Android-Exif-Extended示例:
    public native static boolean nativeCompress(Bitmap bitmap,int quality,String outPath);
    
    public static boolean compressOringinal(String srcPath,int quality,String outPath){
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath);
        boolean success =  nativeCompress(bitmap,quality,outPath);//libjpeg压缩
        if(success){
            ExifInterface exif = new ExifInterface();
            try {
                exif.readExif( srcPath, ExifInterface.Options.OPTION_ALL );
                exif.writeExif( outPath );//所有exif信息拷到新文件那儿去
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                return true;
            }
        }
        return success;
    }
复制代码

自定义相机拍照

camera api是Android兼容性问题最大的地方,没有之一.因此,不要做死去用原生的,或者本身从头开始写一套. 直接用开源的.github

CameraView算法

CameraView-guide数据库

固然,原生api的使用仍是要掌握的,有几个比较好的博客供参考:api

Android相机开发系列:很全面数组

Android多媒体之Camera的相关操做:至关于一个概述

注意点:

  • preview和takepicture两套不一样的操做,不一样的supportedSize. size的选择须要与预览的surfaceview相配合,避免拉伸.
  • 前置摄像头的takepicture的自拍镜像问题
  • 图像矩阵横屏90°.方向旋转能够用纯java数组转换,也能够用opencv 的api,或者renderscript,或者利用bitmap中转,性能最好的是renderscript.
  • 最终图像质量:若是对图片质量(清晰度,亮度)有所要求,可使用preview的api来采集多帧,取评价最高的一帧做为最终的结果. 评价标准方面,能够基于opencv开发一套对清晰度和亮度判断的算法. 更牛的就是多帧合成,顶级实践就是谷歌,华为,小米的超级夜景算法.

调用系统相机拍照

注意兼容性:

  • 构建intent时指定文件保存位置,避免有些机型拿不到默认存储位置的返回
  • Uri 在7.0以上系统的兼容
  • 8.0以上系统,即便是调用系统相机拍照,也须要请求权限,不然crash

标准的intent

Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 Uri photoUri = getMediaFileUri(TYPE_TAKE_PHOTO);
 takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
 startActivityForResult(takeIntent, CODE_TAKE_PHOTO);
复制代码

Uri兼容性

intent读写权限都加上,无论任何版本:

调用系统拍照,Intent须要加上读和写权限. 若是没有写权限,部分4.4如下手机会crash:
```
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

或者这种实现:
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
```
复制代码

7.0以上兼容性的实现

抄官方demo谁都会,但后果就是致使容易跟第三方库内部实现冲突. 这里的重点在于与其余已内部实现Uri兼容性的第三方库的兼容:

//官方demo:原封不动搬过来,就可能会有跟第三方库冲突.由于别人通常也是原封不动搬过来的
<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>

//正确示范:

<provider
            android:name="org.devio.takephoto.permission.TFileProvider"//name要本身继承v4里的fileprovider
            android:authorities="${applicationId}.takephoto.fileprovider"//本身附加字符串,后面要提供解析方法
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths_takephoto" />//记得加后缀.尤为做为库提供时,防止被同名覆盖.
</provider>

//xml里: 
<paths>
<!--外置SD卡-->
        <root-path
            path=""
            name="camera_photos" />
//若是是库,通常只提供上面一个就够了,下面的由app本身的module去提供.
<!--Context.getFilesDir()-->
        <files-path
            name="files"
            path=""/>
<!--Context.getCacheDir()-->
        <cache-path
            name="cache"
            path=""/>
 <!--Environment.getExternalStorageDirectory()-->
        <external-path
            name="external"
            path=""/>
<!--Context.getExternalFilesDir(null)-->
        <external-files-path
            name="external_file_path"
            path=""/>
<!--Context.getExternalCacheDir()--> <!--直到support-v4:25.0.0才支持-->
        <external-cache-path
            name="external_cache_path"
            path=""/>
    </paths>
    
//而后提供解析这个uri的方式(sd卡):

public static String parseOwnUri(Context context, Uri uri) {
        if (uri == null) {
            return null;
        }
        String path;
        if (TextUtils.equals(uri.getAuthority(), context.getPackageName() + ".takephoto.fileprovider") {
            path = new File(uri.getPath().replace("camera_photos/", "")).getAbsolutePath();
        } else {
            path = uri.getPath();
        }
        return path;
}
   
复制代码

规避

本质上仍是由于从7.0开始强制执行sctrickmode,也能够写代码让它不执行:只是不建议这样作罢了.

/过滤URI检查
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());

复制代码

图片选择

能够调用系统intent去图库里选一张图,可是不一样手机的图库选择千差万别,返回的uri依据手机厂商和版本的不一样而状况不一样,有的是contentprovider形式,有的是file uri形式,Android 7.0以上如下还不一致. 另外,系统intent不支持多选. 搞来搞去,不如本身查media center数据库,本身作ui. 基本上成熟的app都是经过这种形式实现,网上开源库也一大把.

github.com/zhihu/Matis…

github.com/donglua/Pho…

图片裁剪

有系统intent,可是不一样手机千差万别,大多数不好劲.果断用开源的. 开源库中,ucrop秒杀其余裁剪库: github.com/Yalantis/uC…

固然,也仍有不足之处: 选择区域后裁剪该区域,不是基于原图来裁,而是基于界面上预览图来裁剪,因此会自带压缩效果.

图片压缩

  • 尺寸压缩与质量压缩:

对于手机图片来讲,80%质量和100%质量并没有肉眼可见的明显区别,而文件大小相差近一倍.彻底没有必要设为100%的质量,而若是是相机拍摄的原图而非进行尺寸压缩后的图,70%的质量已经足够.

因此,通常来讲: 通过尺寸压缩的图,可设置质量80%. 原图大小,可将质量设置为70%. 二者均可用下面的libjpeg来压缩.

  • libjpeg-turbo库在Android上的压缩优化

Bitmap.compress->skia->libjpeg->file,file->libjpeg->skia->bitmap 全部的问题均可以从这里找到答案.

  • 一样分辨率的图片,Android图片文件比ios大: Android上skia库调用libjpeg时使用的是霍夫曼定长编码而非动态的霍夫曼编码.
  • 屡次压缩的图片发绿问题: Android skia库的bug: RGB转YUV时是int转float操做,google工程师为了加快运算速度在进行了位移操做,会致使细微精度丢失,屡次压缩,解压再压缩操做会致使图片渐渐发绿. 参考
  • inSampleSize 只接受2的n次幂: 由于libjpeg解压时就只支持这样的传入

参考:www.voidcn.com/article/p-w…

Android图片压缩终结篇

彩蛋: 本身写的图片压缩工具app

  • 模仿微信压缩效果的库: Luban

拍照/图片选择-图片裁剪-压缩 的整合

github.com/crazycodebo…

github.com/LuckSiege/P…

结合app业务的二次封装技巧: 透明fragment接收onactivityResult,达到最终静态方法+回调的一行代码调用的效果:

TakePhotoUtil.startPickOne(fragmentActivity, isForCamera, new TakeOnePhotoListener() {
           @Override
           public void onSuccess(String usableFilePath) {
               showImg(usableFilePath);
           }

           @Override
           public void onFail(String filePath, String msg) {
               MyToast.errorBigL(msg);
               //toTakePhotoMode();
           }

           @Override
           public void onCancel() {
              // toTakePhotoMode();
           }
       });

复制代码

ps. 透明fragment的封装技巧参考: RxPermissionsFragment

图片上传

  • 控制图片源,尽可能小
  • 多图并发,结合rxjava
  • 顶层封装,api友好

服务端处理与下载

  • CDN缓存
  • 提供服务端处理能力:resize,裁剪,模糊.七牛和阿里云的图片存储服务均提供了此类功能.客户端经过url后添加参数,得到比原图小的图片,节省流量.

示例: 78re52.com1.z0.glb.clouddn.com/resource/go…

代码示例:QiniuUtils

客户端显示

文件夹管理

  • 项目中提供统一的获取方法,删除方法
  • cache使用的注意事项:存储不足时会被系统优先清空

内置图片管理与包大小

  • tinypng/tinyjpg先压缩一遍
  • 用不到透明通道的png统一转换成jpg
  • 大一些的图,能够放到服务端,首次进入app时下载到files文件夹.
相关文章
相关标签/搜索