ImageLoader 加密解密共存

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

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,发现记录一下,会有所作用,因为好记性不如烂笔头。

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    79 引用 • 431 回帖
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1062 引用 • 3455 回帖 • 150 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖 • 1 关注
  • OneDrive
    2 引用 • 5 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 266 关注
  • Word
    13 引用 • 41 回帖 • 2 关注
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 265 关注
  • sts
    2 引用 • 2 回帖 • 243 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 111 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 559 关注
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 519 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    53 引用 • 190 回帖 • 2 关注
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    7 引用 • 28 回帖 • 1 关注
  • 导航

    各种网址链接、内容导航。

    45 引用 • 177 回帖 • 2 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖 • 1 关注
  • 阿里巴巴

    阿里巴巴网络技术有限公司(简称:阿里巴巴集团)是以曾担任英语教师的马云为首的 18 人,于 1999 年在中国杭州创立,他们相信互联网能够创造公平的竞争环境,让小企业通过创新与科技扩展业务,并在参与国内或全球市场竞争时处于更有利的位置。

    43 引用 • 221 回帖 • 54 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 9 关注
  • 分享

    有什么新发现就分享给大家吧!

    248 引用 • 1794 回帖
  • Kafka

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

    36 引用 • 35 回帖 • 5 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 367 回帖 • 1 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 496 关注
  • 尊园地产

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

    1 引用 • 22 回帖 • 799 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 233 回帖
  • Office

    Office 现已更名为 Microsoft 365. Microsoft 365 将高级 Office 应用(如 Word、Excel 和 PowerPoint)与 1 TB 的 OneDrive 云存储空间、高级安全性等结合在一起,可帮助你在任何设备上完成操作。

    5 引用 • 34 回帖
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    7 引用 • 69 回帖 • 5 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖