使用 Image Loader 和自定义 schema 加载特殊来源的图片

DennisNWQY 9年前
   <p>首先,我们问一个问题。</p>    <h3><strong>Image Loader 一般拿来加载哪些图片?</strong></h3>    <p>不管是早期的 Universal-Image-Loader ,还是后来的Picasso,Glide 以及Fresco,它们都是很优秀的图片加载库,我们可以很轻松地通过它们来加载各种网络图片( http:// , https:// ),甚至是从</p>    <ul>     <li>应用 Resource( android.resource:// )</li>     <li>应用 Asset( file://android_asset/ )</li>     <li>本地文件( file:/// )</li>     <li>本地多媒体库( content:// )</li>    </ul>    <p>加载图片。</p>    <p>但是,Image Loader 的作用绝不止于此。</p>    <h3><strong>Image Loader 还可以用来加载哪些图片?</strong></h3>    <ul>     <li>系统已安装动态壁纸的缩略图</li>     <li>系统已安装应用的图标</li>     <li>系统已安装应用里的图片资源</li>     <li>未安装 apk 文件的图标</li>     <li>未安装 apk 文件里的图片资源</li>     <li>图片缩略图</li>     <li>视频缩略图</li>     <li>通讯录联系人头像</li>    </ul>    <p>可以这么说,只要输出是图片的地方,都可以用 Image Loader 进行加载。</p>    <h3><strong>如何利用 Image Loader 加载这些特殊图片?</strong></h3>    <p>一种通用思路是,既然 Image Loader 根据 URL 来加载各种网络图片或本地图片,而它们的 schema 一般都不一样,如网络图片的 schema 是 http 和 https ,本地文件的 schema 是 file ,那我们可以通过构造自定义 schema 的 URL,来指代不同类型(来源)的图片,只要配置 Image Loader 使其能从这些自定义 schema 的 URL 中识别并加载图片,后续就可以像加载普通图片一样方便地加载这些特殊图片了。</p>    <p>具体步骤如下。</p>    <p><strong>1. 设计图片 URL 的格式</strong></p>    <p>举个例子,比如我们在开发一个应用管理软件,需要在列表中显示系统已安装应用的图标,我们可以用 "application://com.elvishew.sampleapp/10" 表示包名为 com.elvishew.sampleapp 、版本号为 10 的应用的缩略图。</p>    <p>又比如我们在开发一个文件管理器,需要显示 apk 文件的应用图标,我们可以用 "apk:///sdcard/sample.apk#1470000000000" 表示存放在 /sdcard 路径下、名为 sample.apk 、最后修改时间戳为 1470000000000 的 apk 文件的应用图标。</p>    <p>以上有个小细节,我们把那些可能会影响加载结果的因素,如应用程序的版本号、apk 文件的最后修改时间戳,加入到 URL 中。这样一来,一旦这些影响因素变了,URL 也会跟着改变,Image Loader 就会视它们为不同的图片,而不去使用错误的缓存。</p>    <p><strong>2. 配置 Image Loader,让其可以从自定义格式的 URL 中加载图片</strong></p>    <p>首先,定义特殊图片的 schema。</p>    <pre>  <code class="language-java">// 系统已安装应用的图标 schema  public static final String URL_SCHEMA_APPLICATION = "application"    // apk 文件的应用图标 schema  public static final String URL_SCHEMA_APK = "apk"    // 省略其他 schema  ...</code></pre>    <p>其次,自定义 Downloader( Universal-Image-Loader ) 或 RequestHandler(Picasso),在应用初始化时配置 Image Loader。</p>    <p><strong>Universal-Image-Loader :</strong></p>    <pre>  <code class="language-java">// 自定义 Downloader  private class CustomImageDownloader extends BaseImageDownloader {      // 省略部分代码    ...      @Override    protected InputStream getStreamFromOtherSource(String imageUri, Object extra)        throws IOException {      InputStream is = null;      Uri uri = Uri.parse(imageUri);      if (URL_SCHEMA_APPLICATION.equals(uri.getScheme())) {        String packageName = uri.getHost();        is = getApplicationIconInputStream(packageName);      } else if (URL_SCHEMA_APK.equals(uri.getScheme())) {        String apkPath = uri.getAuthority();        is = getApkIconInputStream(apkPath);      }      ... // 省略其他分支代码      return is;    }  }    // 配置 Universal-Image-Loader  ImageLoaderConfiguration.Builder configBuilder = new ImageLoaderConfiguration.Builder(context)        .imageDownloader(new CustomImageDownloader())        ... // 省略其他配置代码    ImageLoader.getInstance().init(configBuilder.build());</code></pre>    <p><strong>Picasso :</strong></p>    <pre>  <code class="language-java">// 自定义 RequestHandler  private class CustomRequestHandler extends RequestHandler {    @Override    public boolean canHandleRequest(Request data) {      return URL_SCHEMA_APPLICATION.equals(data.uri.getScheme())          || URL_SCHEMA_APK.equals(data.uri.getScheme())          || ... /* 省略其他 schema 的检查 */;    }      @Override    public Result load(Request request, int networkPolicy) throws IOException {      Uri uri = request.uri;      InputStream is = null;      if (URL_SCHEMA_APPLICATION.equals(uri.getScheme())) {        String packageName = uri.getHost();        int versionCode = uri.getPort();        is = getApplicationIconInputStream(packageName, versionCode);      } else if (URL_SCHEMA_APK.equals(uri.getScheme())) {        String apkPath = uri.getAuthority();        is = getApkIconInputStream(apkPath);      }      ... // 省略其他分支代码      return new Result(is, Picasso.LoadedFrom.DISK);    }  }    // 配置 Picasso  mPicasso = new Picasso.Builder(context)      .addRequestHandler(new CustomRequestHandler())      .build();</code></pre>    <p><strong>3. 组装图片 URL</strong></p>    <p>以加载 /sdcard/sample.apk 这个 apk 文件的应用图标为例,假设它的最后修改时间戳为 1470000000000 ,我们可以组装得到 URL "apk:///sdcard/sample.apk#1470000000000" 。</p>    <p><strong>4. 使用组装的 URL 加载图片</strong></p>    <p><strong>Universal-Image-Loader :</strong></p>    <pre>  <code class="language-java">ImageLoader.getInstance().displayImage(uri, imageView);</code></pre>    <p><strong>Picasso :</strong></p>    <pre>  <code class="language-java">mPicasso.load(uri).into(imageView);</code></pre>    <p>大功告成。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/95fc589678ed</p>    <p> </p>