ImageLoader 加密解密共存

本贴最后更新于 2342 天前,其中的信息可能已经水流花落

ImageLoader 加密解密共存

  • 问题提出
    Android 开发中异步图片显示应用特别广泛,我们常常会用到 imageloader、Glide、picasso 或者其他开源框架,各有好处,我们这边开发中经常使用的是 imageloader,但是在最近的项目开发中,我们图片的 URL 是加密的二进制流,出于项目的需要,我们需要对加密流进行解密。

  • 一个简单的解决方法
    出于快速开发的想法,想寻找一个特别简单的能够拿到图片 InputStream,很快的找到了 Glide 的 imageDecoder 回调,在这里面有一个 decode 方法,能够操作 InputStream 进行解密操作,然后倒入了 Glide 框架,这样一个项目中拥有了两个异步图片加载框架,怎么说也不是一个好方法。

  • 干掉一个异步加载框架
    上面提出了项目中因为解密加入了 Gilde 框架,这样的做法是简单(其实 imageloader 也很简单),但是我绝对不允许一个项目两个相同功能的框架,所以我要看看 imageloader 的源码,获取 inputstream,进而在 decode 的时候把加密的图片给解密了,然后再使用中只需要给参数加上 key,满足在使用中没有差别,首先看一下源码,理解一下 imageloader 的工作流程:

/** 这个方法是显示图片的时候调用的方法,存在一Imageloader.java里面我们看看这个传递的参数,options里面传递了图片显示的一些参数,我接下来会通过option传递图片解密的密码。 **/ public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { checkConfiguration(); if (imageAware == null) { throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); } if (listener == null) { listener = defaultListener; } if (options == null) { options = configuration.defaultDisplayImageOptions; } if (TextUtils.isEmpty(uri)) { engine.cancelDisplayTaskFor(imageAware); listener.onLoadingStarted(uri, imageAware.getWrappedView()); if (options.shouldShowImageForEmptyUri()) { imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources)); } else { imageAware.setImageDrawable(null); } listener.onLoadingComplete(uri, imageAware.getWrappedView(), null); return; } if (targetSize == null) { targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize()); } String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); engine.prepareDisplayTaskFor(imageAware, memoryCacheKey); listener.onLoadingStarted(uri, imageAware.getWrappedView()); // 上面都是一些判空等基本操作,可以从这儿开始看。 // 获取图片的软应用。 Bitmap bmp = configuration.memoryCache.get(memoryCacheKey); // 我们看else,确定下载图片时候的流程,忽略有缓存之后的图片显示 if (bmp != null && !bmp.isRecycled()) { L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey); if (options.shouldPostProcess()) { ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine.getLockForUri(uri)); ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo, defineHandler(options)); if (options.isSyncLoading()) { displayTask.run(); } else { engine.submit(displayTask); } } else { options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE); listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp); } } else { if (options.shouldShowImageOnLoading()) { imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources)); } else if (options.isResetViewBeforeLoading()) { imageAware.setImageDrawable(null); } ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine.getLockForUri(uri)); // 真正处理图片是在这个task的异步线程里面 LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo, defineHandler(options)); if (options.isSyncLoading()) { displayTask.run(); } else { engine.submit(displayTask); } } }

接下来我们在看看 LoadAndDisplayImageTask 这个类:

@Override public void run() { if (waitIfPaused()) return; if (delayIfNeed()) return; ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock; L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey); if (loadFromUriLock.isLocked()) { L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey); } loadFromUriLock.lock(); Bitmap bmp; try { checkTaskNotActual(); // 再次看看内存里面有没有该图片 bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp == null || bmp.isRecycled()) { // 下载图片的真正方法 bmp = tryLoadBitmap(); // ...... 这个线程下面的代码和我们要做的事无关,都是在缓存图片之类的 }

接下来看看 tryLoadBitmap:

private Bitmap tryLoadBitmap() throws TaskCancelledException { Bitmap bitmap = null; try { File imageFile = configuration.diskCache.get(uri); // 如果已经有了那不是我要干的事,所以跳过if if (imageFile != null && imageFile.exists() && imageFile.length() > 0) { L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey); loadedFrom = LoadedFrom.DISC_CACHE; checkTaskNotActual(); bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath())); } if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey); loadedFrom = LoadedFrom.NETWORK; String imageUriForDecoding = uri; if (options.isCacheOnDisk() && tryCacheImageOnDisk()) { imageFile = configuration.diskCache.get(uri); if (imageFile != null) { imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath()); } } checkTaskNotActual(); // 找到了真正decode的代码,当然decodeImage要干啥,我们还得去看看 bitmap = decodeImage(imageUriForDecoding); // ..... 下面的代码我又可以不看了 return bitmap; }

好吧,该看 decodeImage:

private Bitmap decodeImage(String imageUri) throws IOException { ViewScaleType viewScaleType = imageAware.getScaleType(); ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType, getDownloader(), options); // 这里应该去看decode,按理论这个方法应该到头了。 return decoder.decode(decodingInfo); }

我们看到 decode 是接口的方法,我们看看谁来实现了这个接口 ImageDecoder,很容易知道这个真正的方法在 BaseImageDecoder 里面,最后看一下真正处理 stream 的方法:

@Override public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException { Bitmap decodedBitmap; ImageFileInfo imageInfo; InputStream imageStream = getImageStream(decodingInfo); if (imageStream == null) { L.e(ERROR_NO_IMAGE_STREAM, decodingInfo.getImageKey()); return null; } try { imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo); imageStream = resetStream(imageStream, decodingInfo); Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo); // decodedBitmap 就是要显示的图片了,所以我们在这里严处理掉加密图片。 decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions); //这里贴一下修改之后的处理逻辑,把decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);改为下面的代码: if(decodingInfo.isEncryption()){ //加密了,这里解密方法就不贴了,私有方法,贴出来也没用。 decodedBitmap = decryptBitmap(imageStream, decodingOptions,decodingInfo); }else{ decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions); } // ...... 省略部分代码 return decodedBitmap; }

源码我们已经追溯完毕,接下来就是改。

  • 增加 Options 参数值,处理加密图片
    既然知其所以然了,那就干吧,我们给 Options 增加一个是否加密的标志位,然后再增加一个解密密码变量,这里就是传递参数的问题了.
private final boolean isEncryption; private final String password;

我们在上面的源码中可以看到这个 DisplayImageOptions 类的参数是一直传递的,所以在最外面调用的时候,给 options 加上这两个值吧,当然我给 isEncryption 默认值是 false,这样对非加密的图片就是跟以前一样的处理方式,贴一下加密的调用方式吧,然后 decode 的方法我就直接放到刚刚的源码里了。

ImageLoader.getInstance().displayImage(mPicturePaths.get(i).getmDataSource(), imageView, ImageLoaderUtils.getEncryptionOptions(password)); // 这里也贴一下getEncryptionOptions,放在ImageLoaderUtils类里面 public static DisplayImageOptions getEncryptionOptions(String key){ return new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.pre_setting_bg) //设置图片在下载期间显示的图片 .showImageForEmptyUri(R.mipmap.pre_setting_bg)//设置图片Uri为空或是错误的时候显示的图片 .showImageOnFail(R.mipmap.pre_setting_bg) //设置图片加载/解码过程中错误时候显示的图片 .cacheInMemory(true)//设置下载的图片是否缓存在内存中 .cacheOnDisk(true)//设置下载的图片是否缓存在SD卡中 .imageScaleType(ImageScaleType.IN_SAMPLE_INT)//设置图片以如何的编码方式显示 .bitmapConfig(Bitmap.Config.RGB_565)//设置图片的解码类型 .imageScaleType(ImageScaleType.EXACTLY) .setEncryption(true) .setPassword(key) .build();//构建完成 }
  • 最后
    代码完全是自己写的,如果有什么不对的地方,还望批评指正,我的邮箱:redzkh@gmail.com,发现记录一下,会有所作用,因为好记性不如烂笔头。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 34 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 451 关注
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    240 引用 • 224 回帖
  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    52 引用 • 228 回帖
  • 印象笔记
    3 引用 • 16 回帖 • 1 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 14 关注
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    32 引用 • 100 回帖 • 1 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    119 引用 • 323 回帖
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 184 关注
  • 创业

    你比 99% 的人都优秀么?

    81 引用 • 1395 回帖 • 2 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 808 关注
  • CodeMirror
    2 引用 • 17 回帖 • 177 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 89 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 618 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    89 引用 • 150 回帖
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 5 关注
  • Angular

    AngularAngularJS 的新版本。

    26 引用 • 66 回帖 • 563 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖 • 1 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 1 关注
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    70 引用 • 193 回帖 • 413 关注
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4601 回帖 • 709 关注
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖 • 3 关注
  • 七牛云

    七牛云是国内领先的企业级公有云服务商,致力于打造以数据为核心的场景化 PaaS 服务。围绕富媒体场景,七牛先后推出了对象存储,融合 CDN 加速,数据通用处理,内容反垃圾服务,以及直播云服务等。

    29 引用 • 230 回帖 • 124 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 832 关注
  • Anytype
    3 引用 • 31 回帖 • 33 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 2 关注
  • OneNote
    1 引用 • 3 回帖 • 2 关注