使用Glide 4.x 自定义GlideModel 和 conceal 2.x 对Android[图片]文件加密与解密

项目地址GlideV4

刚写完这篇文章突然想起来是不是可以使用接口来代替Picture这样可以更加通用,于是试了一下果然可以.所以读者们可以将Picture替换成IPicture接口.

public interface IPicture {
  String getFileName();
}

前面介绍了如何使用Glide3.x 进行图片文件加密解密.最近Glide更新到了4.x conceal也更新到了2.x , 所以决定把自己的项目更新到最新版本.
如果您使用的是 Glide 3.x请看这篇文章使用Glide 3.x 自定义GlideModel和 conceal 1.x 对Android[图片]文件加密与解密

Glide4.x 相对于Glide 3.x变化还是挺大的
在此我们先来了解一下Glide 4.x 在使用上的一些变化

一些变化

一. 引用库的变化,这个不用解释了吧! 升级版本....

  compile 'com.github.bumptech.glide:glide:4.0.0-RC1'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0-RC1'
  compile 'com.facebook.conceal:conceal:2.0.1@aar'

二. 接口上的变化

  • Glide.with() 4.x 新增了一个重载Glide.with(view) 是的你没有看错,可以使用View作为参数. 而且还会将你的图片加载与Activity或者是Fragment生命周期进行绑定. 当然使用这个接口会耗费那么多一点点的性能.但是为了更好的封装(在Adapter中再也不需要专门为Glide传一个Fragment或Activity参数了)这一点性能牺牲还是值得的.

  • 图片变换比如裁切,圆角等. 在Glide 3.x 时代我们使用类似如下代码

Glide.with(context)
        .load("")
        .bitmapTransform(new CenterCrop(context), new CropCircleTransformation(context))
        .into(image);

而我们在4.x中则应该以一种新的姿态来实现,centerCrop()placeholder()error()priority()
diskCacheStrategy()等大多数方法都被移动到了RequestOptions中,glide-transformations 4.x 中已经不能再使用了, 如果需要使用可以复制源代码进行修改. 或者等作者更新.

  • 总之一句话原来找不到的接口,就去RequestOptions中找. 找不到了那我也没辙啊. 比如动画接口..animate()

RequestOptions mOptions = RequestOptions.circleCropTransform();  
              
Glide.with(context)
     .load("")
     .apply(mOptions)
     .into(image);

Glide 4.x 自定义GlideModel

Glide 4.x 不再需要像 Glide 3.x 那样在主配置文件里面配置 GlideModel,只需要在GlideModel类加上注解即可.

为了不影响原来的URL类型图片处理所以这里注册的是Picture类型和文件类型
两者实现一个即可.具体的说明可以在注释中看到.就不再解释了

/**
 * Glide 4.x 自定义处理Picture类型和File类型的文件处理, GlideModel
 */
@GlideModule public class MyGlideModule extends AppGlideModule {

  @Override public void applyOptions(Context context, GlideBuilder builder) {
    // 设置别的get/set tag id,以免占用View默认的
    ViewTarget.setTagId(R.id.glide_tag_id);
    RequestOptions options = new RequestOptions().format(DecodeFormat.PREFER_RGB_565);
    builder.setDefaultRequestOptions(options);
  }

  @Override public void registerComponents(Context context, Registry registry) {
    // 指定Model类型为Picture的处理方式
    registry.append(Picture.class, InputStream.class, new MyModelLoader.LoaderFactory());

    // 指定Model类型为File的处理方式
    registry.append(File.class, InputStream.class,
        new FileLoader.Factory(new FileLoader.FileOpener() {

          @Override public InputStream open(File file) throws FileNotFoundException {
            // 可以在这里进行文件处理,比如解密等.
            Timber.e(file.getAbsolutePath());
            return ConcealUtil.getCipherInputStream( new FileInputStream(file));
          }

          @Override public void close(InputStream inputStream) throws IOException {
            inputStream.close();
          }

          @Override public Class getDataClass() {
            return InputStream.class;
          }
        }));
  }

  /**
   * 清单解析的开启
   *
   * 这里不开启,避免添加相同的modules两次
   *
   * @return
   */
  @Override public boolean isManifestParsingEnabled() {
    return false;
  }
}

图片文件的解密都在这里进行.


**
 * Glide 4.x 自定义GlideModel
 * 这里指定Picture类型的Model由用户处理
 */
public class MyModelLoader implements ModelLoader {

  public MyModelLoader() {
  }

  @Nullable @Override
  public LoadData buildLoadData(Picture model, int width, int height,
      Options options) {
    return new LoadData(new MyKey(model), new MyDataFetcher(model));
  }

  @Override public boolean handles(Picture s) {
    return true;
  }

  /**
   * 文件唯一ID
   * 这个类可以使用 {@link ObjectKey} 代替
   */
  public static class MyKey implements Key {
    Picture path;

    public MyKey(Picture path) {
      this.path = path;
    }

    @Override public void updateDiskCacheKey(MessageDigest messageDigest) {
      messageDigest.update(path.getFilePath().getBytes(CHARSET));
    }

    @Override public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      MyKey myKey = (MyKey) o;
      return path != null ? path.equals(myKey.path) : myKey.path == null;
    }

    @Override public int hashCode() {
      return path != null ? path.hashCode() : 0;
    }
  }

  /**
   * 如何加载数据
   */
  public static class MyDataFetcher implements DataFetcher {

    private Picture file;
    private boolean isCanceled;
    InputStream mInputStream = null;

    public MyDataFetcher(Picture file) {
      this.file = file;
    }

    @Override public void loadData(Priority priority, DataCallback callback) {
      // 可以在这里进行一些文件处理,比如根据文件路径处理,文件解密等
      try {
        Timber.e(file.getFilePath());
        if (!isCanceled) {
        mInputStream =  ConcealUtil.getCipherInputStream(new FileInputStream(new File(file.getFilePath())));
        }
      } catch (FileNotFoundException e) {
        callback.onLoadFailed(e);
        Timber.e(e);
      }
      callback.onDataReady(mInputStream);
    }

    @Override public void cleanup() {
      if (mInputStream != null) {
        try {
          mInputStream.close();
        } catch (IOException e) {
          Timber.e(e);
        }
      }
    }

    @Override public void cancel() {
      isCanceled = true;
    }

    @Override public Class getDataClass() {
      return InputStream.class;
    }

    @Override public DataSource getDataSource() {
      //return LOCAL;
      return REMOTE;
      //return DATA_DISK_CACHE;
      //return RESOURCE_DISK_CACHE;
      //return MEMORY_CACHE;
    }
  }

  /**
   * 构造工厂类
   */
  public static class LoaderFactory implements ModelLoaderFactory {

    public LoaderFactory() {
    }

    @Override public ModelLoader build(MultiModelLoaderFactory multiFactory) {
      return new MyModelLoader();
    }

    @Override public void teardown() {

    }
  }
  

祭出我们的Util代码
特别留意一下这一句代码,与3.x是有区别的.当然3.x也可以使用这段代码来初始化crypto.

  crypto = AndroidConceal.get()
        .createDefaultCrypto(new SharedPrefsBackedKeyChain(context, CryptoConfig.KEY_256));
        
public class ConcealUtil {

  private static Crypto crypto = null;
  private static Entity entity = null;

  /**
   * 初始化
   *
   * @param context context
   * @param e 密码
   */
  public static void init(Context context, String e) {
    entity = Entity.create(e);
    crypto = new Crypto(new SharedPrefsBackedKeyChain(context, CryptoConfig.KEY_256),
        new SystemNativeCryptoLibrary(), CryptoConfig.KEY_256);
    if (!crypto.isAvailable()) {
      destroy();
    }
  }

  public static void destroy() {
    crypto = null;
    entity = null;
  }

  private static void check() {
    if (crypto == null || entity == null) {
      throw new RuntimeException("请初始化.....");
    }
  }

  public static OutputStream getCipherOutputStream(File file) {
    if (file.exists()) return null;
    try {
      OutputStream fileStream = new FileOutputStream(file);
      return getCipherOutputStream(fileStream);
    } catch (IOException e) {
      Timber.e(e);
    }
    return null;
  }

  public static OutputStream getCipherOutputStream(OutputStream fileStream) {
    check();
    try {
      return crypto.getCipherOutputStream(fileStream, entity);
    } catch (IOException e) {
      Timber.e(e);
    } catch (CryptoInitializationException e) {
      Timber.e(e);
    } catch (KeyChainException e) {
      Timber.e(e);
    }
    return null;
  }

  public static InputStream getCipherInputStream(String file) {
    return getCipherInputStream(new File(file));
  }

  public static InputStream getCipherInputStream(File file) {
    check();
    if (!file.exists()) return null;
    try {
      InputStream inputStream = new FileInputStream(file);
      return crypto.getCipherInputStream(inputStream, entity);
    } catch (FileNotFoundException e) {
      Timber.e(e);
    } catch (KeyChainException e) {
      Timber.e(e);
    } catch (CryptoInitializationException e) {
      Timber.e(e);
    } catch (IOException e) {
      Timber.e(e);
    }
    return null;
  }

  /**
   * 保存字节流
   *
   * @param data 数据
   */
  public static void saveFile(byte data[], String path) {
    String fileName = System.currentTimeMillis() + ".jpg";
    File image = new File(path, fileName);
    try {
      OutputStream outStream = getCipherOutputStream(image);
      if (outStream != null) {
        outStream.write(data);
        outStream.flush();
        outStream.close();
      }
    } catch (IOException e) {
      Timber.e(e);
    }
  }
}

使用方式,注意一定只能load(picture)

GlideApp.with(this).load(new Picture()).into(mImageView);

项目地址GlideV4

你可能感兴趣的:(使用Glide 4.x 自定义GlideModel 和 conceal 2.x 对Android[图片]文件加密与解密)