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

ImageLoader 加密解密共存

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;
	}

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

	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();// 构建完成
    }
                            

感谢    关注    收藏    赞同    反对    举报    分享
回帖    
请输入回帖内容...