Dalvik中自定义类加载

翻译自:http://android-developers.blogspot.com/2011/07/custom-class-loading-in-dalvik.html
Dalvik虚拟机为开发者提供了执行自定义类加载的设备。与从默认位置加载Dalvik的可执行文件(dex文件)不同,一个应用可以可以在可选择的地方加载类,比如内部存储或者通过网络。
这种技术并不适应每个应用。实际上,很多应用没有这个也能运行良好。但是自定义类加载时有适用场景的。这有以下几种场景:

  • 大的应用会超过65536的方法引用,这个数字是一个dex文件支持的最大方法数,为了避免这个限制,开发者可以把将应用中这部分分成多个二级dex文件,在运行时加载他们。
  • 框架可以被设计成运行时去动态的加载来作为可执行的扩展。

我们已经创建了一个简单的app来演示分包和运行时动态加载类(注意由于以上说明的原因,这个app无法用Eclipse的ADT插件来构建,对应的,用Ant来构建,在Readme.txt中有细节)

这个app有一个简单的Activity,包含了一个组件库来展示Toast,这个Activity和资源在默认的dex文件中,然而这个依赖库代码存储在apk中的二级dex中,这需要一个修改构建的进程,以下会展示的更详细。

在依赖库被调用之前,app需要首先明确的加载二级dex文件。让我们看看有关的运行部分。

代码结构

这个应用包含3个类。

  • com.example.dex.MainActivity:调用依赖库的UI组件
  • com.example.dex.LibraryInterface依赖库的接口定义
  • com.example.dex.lib.LibraryProvider 依赖库的执行

这个依赖库在二级dex文件中,其他的类在默认的dex文件中。以下的构建进程的部分说明如何完成这个过程。当然,打包的方式由开发者处理的脚本有关。

加载类和调用方法

二级dex文件包含LibraryProvider,以应用assert的形式存储。首先,它必须要拷贝到加载类的加载器路径中。这个应用使用私有的内部存储区域来达到目的(从技术上来看,外部存储也能工作,不过需要考虑在那里保存二进制文件的安全隐患)

下面是MainActivity的截图,包含标准的文件I/O流来实现拷贝。

  // Before the secondary dex file can be processed by the DexClassLoader,
  // it has to be first copied from asset resource to a storage location.
  File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE),
          SECONDARY_DEX_NAME);
  ...
  BufferedInputStream bis = null;
  OutputStream dexWriter = null;

  static final int BUF_SIZE = 8 * 1024;
  try {
      bis = new BufferedInputStream(getAssets().open(SECONDARY_DEX_NAME));
      dexWriter = new BufferedOutputStream(
          new FileOutputStream(dexInternalStoragePath));
      byte[] buf = new byte[BUF_SIZE];
      int len;
      while((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
          dexWriter.write(buf, 0, len);
      }
      dexWriter.close();
      bis.close();

  } catch (. . .) {...}

下一步,声明一个dex加载器来从提取的二级dex文件中加载依赖库。有很多的方法来调用加载的类中的方法。在这个例子中,类被直接调用时,类实例转为一个接口。

另一个方法去调用方式是使用反射API。使用反射的方法的好处是不需要二级dex文件执行任何特定的接口。然而,应该知道反射是很慢的。

// Internal storage where the DexClassLoader writes the optimized dex file to
  final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);

  DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
                                         optimizedDexOutputPath.getAbsolutePath(),
                                         null,
                                         getClassLoader());
  Class libProviderClazz = null;
  try {
      // Load the library.
      libProviderClazz =
          cl.loadClass("com.example.dex.lib.LibraryProvider");
      // Cast the return object to the library interface so that the
      // caller can directly invoke methods in the interface.
      // Alternatively, the caller can invoke methods through reflection,
      // which is more verbose. 
      LibraryInterface lib = (LibraryInterface) libProviderClazz.newInstance();
      lib.showAwesomeToast(this, "hello");
  } catch (Exception e) { ... }

创建进程

为了得到两个分开的dex文件,我们需要调整标准构建过程。为了实现这个,我们在ant的build.xml简单的修改-dex指令
这个修改过的-dex指令执行以下的操作:

  1. 创建两个临时文件夹类存储.class文件,来转换为默认dex文件和二级dex文件。
  2. 有选择地从PROJECT_ROOT/bin/classes 复制.class文件到两个临时文件夹
      <!-- Primary dex to include everything but the concrete library
                 implementation. -->
            <copy todir="${out.classes.absolute.dir}.1" >
                <fileset dir="${out.classes.absolute.dir}" >
                        <exclude name="com/example/dex/lib/**" />
                </fileset>
            </copy>
            <!-- Secondary dex to include the concrete library implementation. -->
            <copy todir="${out.classes.absolute.dir}.2" >
                <fileset dir="${out.classes.absolute.dir}" >
                        <include name="com/example/dex/lib/**" />
                </fileset>
            </copy>
  1. 从两个临时文件夹转换.class文件成两个分开的dex文件
  2. 添加一个二级dex文件到jar文件中,jar文件为DexClassLoader提供输入格式。最后,将jar文件存储在项目的assets文件夹中。
<!-- Package the output in the assets directory of the apk. -->
            <jar destfile="${asset.absolute.dir}/secondary_dex.jar"
                   basedir="${out.absolute.dir}/secondary_dex_dir"
                   includes="classes.dex" />

为了构建,在项目根目录下执行ant debug (或者release)。
这就是合适的场景,动态加载类会很有帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值