使用Glide 3.x 自定義GlideModel和 conceal 1.x 對(duì)Android[圖片]文件加密與解密

項(xiàng)目地址GlideV3

剛寫(xiě)完這篇文章突然想起來(lái)是不是可以使用接口來(lái)代替Picture這樣可以更加通用,于是試了一下果然可以.所以讀者們可以將Picture替換成IPicture接口.

public interface IPicture {
  String getFileName();
}

本項(xiàng)目使用的Model是Picture,使用Glide.with().from(Picture.class).load()加載圖片的話,網(wǎng)絡(luò)圖片等都將通過(guò)這個(gè)自定義GlideModel加載,所以加載網(wǎng)絡(luò)圖片時(shí)請(qǐng)繼續(xù)使用Glide.with().load()
在寫(xiě)本文章的時(shí)候Glide版本還是3.7.x,Glide 4.x 實(shí)現(xiàn)同樣的功能.將使用另一篇博文.

發(fā)現(xiàn)問(wèn)題

由于Glide相對(duì)于picasso有更多的優(yōu)勢(shì),所以最近把項(xiàng)目圖片加載庫(kù)切換到了Glide,本地圖片存放在外置SD卡中,使用的是Facebookconceal進(jìn)行加密. 接下來(lái)就悲劇了,Glide 只能加載File,byte[],url,等類型的數(shù)據(jù),但是conceal 只能的到解密數(shù)據(jù)流, 所以必須將InputStream解析成byte[]才能被Glide加載.
解析字節(jié)流在UI線程(當(dāng)然你也可以將它放到非UI線程)中執(zhí)行,每次刷新數(shù)據(jù)(如選中操作,上下滑動(dòng)等)都會(huì)導(dǎo)致所有圖片的數(shù)據(jù)重新從文件系統(tǒng)(加載-解密-解析-顯示)整個(gè)過(guò)程,導(dǎo)致用戶界面卡頓.

代碼如下 :

InputStream inputStream = new FileInputStream(file);
// 解密并轉(zhuǎn)化為字節(jié)數(shù)組,由于這一段在UI線程中所以卡頓很嚴(yán)重,而且每次刷新數(shù)據(jù)都會(huì)重新加載-解密-解析-顯示
byte[] data = input2byte(crypto.getCipherInputStream(inputStream, entity));
// 加載字節(jié)數(shù)組到ImageView
Glide.with(Context).load(data).into(image);

解決問(wèn)題

怎么樣才能做到像Glide加載普通文件一樣,在異步的線程里加載圖片,而且只需要一次加載解析,其他情況在緩存中查找呢? 查找了相關(guān)資料發(fā)現(xiàn)Glide 還能夠加載ModelType類型數(shù)據(jù),ModelType類型的加載需要自定義GlideModule并在清單文件中注冊(cè),首先來(lái)定義我們的GlideModule并注冊(cè).

  1. 自定義GlideModule,由于我的數(shù)據(jù)的存儲(chǔ)路徑是可以直接拿到的,因此我的modelClass類型為Picture類型,也可以定義為其他類型 比如Picture等等
public class CustomGlideModule implements GlideModule {

  @Override public void applyOptions(Context context, GlideBuilder builder) {
    // 設(shè)置別的get/set tag id,以免占用View默認(rèn)的
    ViewTarget.setTagId(R.id.glide_tag_id);
    
   // 圖片質(zhì)量低,夠用就行
   builder.setDecodeFormat(DecodeFormat.PREFER_RGB_565);
  }

  @Override public void registerComponents(Context context, Glide glide) {
    // 注冊(cè)我們的ImageLoader,定義modelClass類型為Picture因?yàn)榧用芪募穆窂侥軌蛑苯幽玫?也可以定義為其他類型 比如Picture等等
    // 第二個(gè)參數(shù)是我們要將第一個(gè)參數(shù)數(shù)據(jù)映射到的類型,這里的意思是(將字符串類型的文件地址,轉(zhuǎn)化為輸入流)
    // 第三個(gè)參數(shù)是我們的Loader(加載器)的工廠方法,決定我們使用什么加載器來(lái)加載文件
    glide.register(Picture.class, InputStream.class, new ImageLoader.Factory());
  }
}
  1. 在清單文件中注冊(cè)我們自定義GlideModule
<meta-data
    android:name="xxx.glide.CustomGlideModule"
        android:value="GlideModule" />
  1. 自定義的加密文件加載類,用于加載加密文件,這樣自定義的數(shù)據(jù)加載類我們不需要管理緩存問(wèn)題,Glide會(huì)為我們處理好.
public class ImageDataFetcher implements DataFetcher<InputStream> {

  private volatile boolean mIsCanceled;
  private final Picture mFilePath;
  private InputStream mInputStream;

  public ImageDataFetcher(Picture filePath) {
    mFilePath = filePath;
  }

  /**
   * 這個(gè)方法是在非UI線程中執(zhí)行,我們利用此方法來(lái)加載我們的加密數(shù)據(jù)
   *
   * @param priority
   * @throws Exception
   */
  @Override public InputStream loadData(Priority priority) throws Exception {
    if (mIsCanceled) {
      return null;
    }
    mInputStream = fetchStream(mFilePath.getFileName());
    return mInputStream;
  }

  /**
   * 返回解密后的數(shù)據(jù)流
   *
   * @param file 文件名
   * @return inputStream
   */
  private InputStream fetchStream(String file) {
    InputStream inputStream = new FileInputStream(new File(file));
    // 返回解密數(shù)據(jù)流
    return ConcealUtil.getCipherInputStream(inputStream);
  }

  /**
   * 處理完成之后清理工作
   */
  @Override public void cleanup() {
    if (mInputStream != null) {
      try {
        mInputStream.close();
      } catch (IOException e) {
        Log.e("Glide", "Glide", e);
      } finally {
        mInputStream = null;
      }
    }
  }

  /**
   * 該文件的唯一ID
   */
  @Override public String getId() {
    return mFilePath.getFileName();
  }

  /**
   * 在UI線程中調(diào)用,取消加載任務(wù)
   */
  @Override public void cancel() {
    mIsCanceled = true;
  }
}
  1. 圖片加載器,重載getResourceFetcher方法,返回我們剛剛定義好的圖片加載類.
public class ImageLoader implements ModelLoader<Picture, InputStream> {

  public ImageLoader() {
  }

  @Override
  public DataFetcher<InputStream> getResourceFetcher(Picture model, int width, int height) {
    return new ImageDataFetcher(model);
  }

  /**
   * ModelLoader工廠,在向Glide注冊(cè)自定義ModelLoader時(shí)使用到
   */
  public static class Factory implements ModelLoaderFactory<Picture, InputStream> {

    @Override
    public ModelLoader<Picture, InputStream> build(Context context, GenericLoaderFactory factories) {
      // 返回ImageLoader對(duì)象
      return new ImageLoader();
    }

    @Override public void teardown() {

    }
  }
}
  1. 最后我們可以這樣使用了
Glide.with(mActivity)
        .from(Picture.class) // 設(shè)置數(shù)據(jù)源類型為我們的ImageFid
        .fitCenter()
        .diskCacheStrategy(DiskCacheStrategy.RESULT) // 設(shè)置本地緩存,緩存源文件和目標(biāo)圖像
        .placeholder(R.drawable.ic_default_image)
        .load(pictures.get(position).getFilePath())
        .into(photoView);
  1. 祭出我們的Util代碼
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("請(qǐng)初始化.....");
    }
  }

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

  /**
   * 保存字節(jié)流
   *
   * @param data 數(shù)據(jù)
   */
  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);
    }
  }
}
注意緩存策略如果對(duì)數(shù)據(jù)保密要求嚴(yán)格的話請(qǐng)?jiān)O(shè)置為NONE

別忘了引入響應(yīng)的庫(kù)文件

dependencies {
...
    compile 'com.facebook.conceal:conceal:1.1.2@aar'
    compile 'com.github.bumptech.glide:glide:+'
}

項(xiàng)目地址GlideV3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評(píng)論 25 708
  • 一、簡(jiǎn)介 在泰國(guó)舉行的谷歌開(kāi)發(fā)者論壇上,谷歌為我們介紹了一個(gè)名叫Glide的圖片加載庫(kù),作者是bumptech。這...
    天天大保建閱讀 7,786評(píng)論 2 28
  • 做生意一忌:沒(méi)膽量 俗話說(shuō),只要有七分把握便可行動(dòng),余下的三分把握靠你爭(zhēng)取遇事下不了決心,錯(cuò)過(guò)時(shí)機(jī)不得利,要知道經(jīng)...
    時(shí)間軸的猜想閱讀 1,287評(píng)論 0 2
  • 杭州銀行成立于1996年9月,總部位于杭州。目前,全行擁有189家分支機(jī)構(gòu),網(wǎng)點(diǎn)覆蓋長(zhǎng)三角、珠三角、環(huán)渤海灣等...
    HZbank閱讀 2,344評(píng)論 0 0
  • 到底什么才是對(duì)媽媽最好的?媽媽到底想要什么?是呆在老家看孫子嗎?我的想法是什么?我的想法是一:八寶留在我們身邊。二...
    八寶媽閱讀 141評(píng)論 0 0

友情鏈接更多精彩內(nèi)容