通过Dalvik加载自定义类(而不是从默认的dex文件里面去加载)
2012-03-11 23:32
302 查看
Posted by Tim Bray on 28 July 2011 at 8:35 AM
[This post is by Fred Chung, who’s an Android Developer Advocate — Tim Bray]
The Dalvik VM provides facilities for developers to perform custom class loading. Instead of loading Dalvik executable (“dex”) files from the default location, an application can load them from alternative locations such as internal storage or over the network.
This technique is not for every application; In fact, most do just fine without it. However, there are situations where custom class loading can come in handy. Here are a couple of scenarios:
Big apps can contain more than 64K method references, which is the maximum number of supported in a dex file. To get around this limitation, developers can partition part of the program into multiple secondary dex files, and load them at runtime.(大应用最多只可以含有64k的方法引用,这是dex文件所能支持的最大限制。为了突破这个限制,开发者可以将程序分成多个dex文件,然后在运行时再去加载这些类)
Frameworks can be designed to make their execution logic extensible by dynamic code loading at runtime.(Frameworks本身就是设计支持在运行时逻辑上时可扩展的)
We have created a sample app to demonstrate the partitioning of dex files and runtime class
loading. (Note that for reasons discussed below, the app cannot be built with the ADT Eclipse plug-in. Instead, use the included Ant build script. See Readme.txt for detail.)
The app has a simple Activity that invokes a library component to display a Toast. The Activity and its resources are kept in the default dex, whereas the library code is stored in a secondary dex bundled in the APK. This requires a modified build process,
which is shown below in detail.
Before the library method can be invoked, the app has to first explicitly load the secondary dex file. Let’s take a look at the relevant moving parts.
Code Organization
The application consists of 3 classes.
com.example.dex.MainActivity: UI component from which the library is invoked
com.example.dex.LibraryInterface: Interface definition for the library
com.example.dex.lib.LibraryProvider: Implementation of the library
The library is packaged in a secondary dex, while the rest of the classes are included in the default (primary) dex file. The “Build process” section below illustrates how to accomplish this. Of course, the packaging decision is dependent on the particular
scenario a developer is dealing with.
Class loading and method invocation
The secondary dex file, containing LibraryProvider, is stored as an application asset. First, it has to be copied to a storage location whose path can be supplied to the class loader. The sample app uses the app’s private internal storage area for this purpose.
(Technically, external storage would also work, but one has to consider the security implications of keeping application binaries there.)
Below is a snippet from MainActivity where standard file I/O is used to accomplish the copying.
Next, a DexClassLoader is instantiated to load the library from the extracted
secondary dex file. There are a couple of ways to invoke methods on classes loaded in this manner. In this sample, the class instance is cast to an interface through which the method is called directly.
Another approach is to invoke methods using the reflection API. The advantage of using reflection is that it doesn’t require the secondary dex file to implement any particular interfaces. However, one should be aware that reflection is verbose and slow.
In order to churn out two separate dex files, we need to tweak the standard build process. To do the trick, we simply modify the “-dex” target in the project’s Ant build.xml.
The modified “-dex” target performs the following operations:
Create two staging directories to store .class files to be converted to the default dex and the secondary dex.
Selectively copy .class files from PROJECT_ROOT/bin/classes to the two staging directories.
Convert .class files from the two staging directories into two separate dex files.
Add the secondary dex file to a jar file, which is the expected input format for the DexClassLoader. Lastly, store the jar file in the “assets” directory of the project.
To kick-off the build, you execute ant debug (or release) from the project root directory.
That’s it! In the right situations, dynamic class loading can be quite useful.
[This post is by Fred Chung, who’s an Android Developer Advocate — Tim Bray]
The Dalvik VM provides facilities for developers to perform custom class loading. Instead of loading Dalvik executable (“dex”) files from the default location, an application can load them from alternative locations such as internal storage or over the network.
This technique is not for every application; In fact, most do just fine without it. However, there are situations where custom class loading can come in handy. Here are a couple of scenarios:
Big apps can contain more than 64K method references, which is the maximum number of supported in a dex file. To get around this limitation, developers can partition part of the program into multiple secondary dex files, and load them at runtime.(大应用最多只可以含有64k的方法引用,这是dex文件所能支持的最大限制。为了突破这个限制,开发者可以将程序分成多个dex文件,然后在运行时再去加载这些类)
Frameworks can be designed to make their execution logic extensible by dynamic code loading at runtime.(Frameworks本身就是设计支持在运行时逻辑上时可扩展的)
We have created a sample app to demonstrate the partitioning of dex files and runtime class
loading. (Note that for reasons discussed below, the app cannot be built with the ADT Eclipse plug-in. Instead, use the included Ant build script. See Readme.txt for detail.)
The app has a simple Activity that invokes a library component to display a Toast. The Activity and its resources are kept in the default dex, whereas the library code is stored in a secondary dex bundled in the APK. This requires a modified build process,
which is shown below in detail.
Before the library method can be invoked, the app has to first explicitly load the secondary dex file. Let’s take a look at the relevant moving parts.
Code Organization
The application consists of 3 classes.
com.example.dex.MainActivity: UI component from which the library is invoked
com.example.dex.LibraryInterface: Interface definition for the library
com.example.dex.lib.LibraryProvider: Implementation of the library
The library is packaged in a secondary dex, while the rest of the classes are included in the default (primary) dex file. The “Build process” section below illustrates how to accomplish this. Of course, the packaging decision is dependent on the particular
scenario a developer is dealing with.
Class loading and method invocation
The secondary dex file, containing LibraryProvider, is stored as an application asset. First, it has to be copied to a storage location whose path can be supplied to the class loader. The sample app uses the app’s private internal storage area for this purpose.
(Technically, external storage would also work, but one has to consider the security implications of keeping application binaries there.)
Below is a snippet from MainActivity where standard file I/O is used to accomplish the copying.
// 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= newFile(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( newFileOutputStream(dexInternalStoragePath)); byte[] buf= newbyte[BUF_SIZE]; int len; while((len= bis.read(buf,0, BUF_SIZE))> 0){ dexWriter.write(buf,0, len); } dexWriter.flush(); dexWriter.close(); bis.close(); } catch(. ..) {...}
Next, a DexClassLoader is instantiated to load the library from the extracted
secondary dex file. There are a couple of ways to invoke methods on classes loaded in this manner. In this sample, the class instance is cast to an interface through which the method is called directly.
Another approach is to invoke methods using the reflection API. The advantage of using reflection is that it doesn’t require the secondary dex file to implement any particular interfaces. However, one should be aware that reflection is verbose and slow.
// Internal storage where the DexClassLoader writes the optimized dex file to final File optimizedDexOutputPath= getDir("outdex",Context.MODE_PRIVATE); DexClassLoader cl= newDexClassLoader(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){ ...}Build Process
In order to churn out two separate dex files, we need to tweak the standard build process. To do the trick, we simply modify the “-dex” target in the project’s Ant build.xml.
The modified “-dex” target performs the following operations:
Create two staging directories to store .class files to be converted to the default dex and the secondary dex.
Selectively copy .class files from PROJECT_ROOT/bin/classes to the two staging directories.
<!-- Primary dex to include everything but the concrete library implementation. --> <copytodir="${out.classes.absolute.dir}.1"> <filesetdir="${out.classes.absolute.dir}"> <excludename="com/example/dex/lib/**"/> </fileset> </copy> <!-- Secondary dex to include the concrete library implementation. --> <copytodir="${out.classes.absolute.dir}.2"> <filesetdir="${out.classes.absolute.dir}"> <includename="com/example/dex/lib/**"/> </fileset> </copy>
Convert .class files from the two staging directories into two separate dex files.
Add the secondary dex file to a jar file, which is the expected input format for the DexClassLoader. Lastly, store the jar file in the “assets” directory of the project.
<!-- Package the output in the assets directory of the apk. --> <jardestfile="${asset.absolute.dir}/secondary_dex.jar" basedir="${out.absolute.dir}/secondary_dex_dir" includes="classes.dex"/>
To kick-off the build, you execute ant debug (or release) from the project root directory.
That’s it! In the right situations, dynamic class loading can be quite useful.
相关文章推荐
- Android调试大法 自定义IDE默认签名文件==>微信支付、微信登录、微信分享,debug时调试通过,release时调不起微信
- 通过Load table命令将数据文件加载到Sybase IQ数据库里面的Python脚本
- 程序员自定义的一个头文件,怎样通过#include<xxx.h>调用而不是#include"xxx.h"
- DexClassLoader自定义加载Assets目录下的dex、jar文件
- Flex4加载swf文件通过自定义冒泡事件通讯
- 通过本地加载ga.js文件提高Google Anlytics性能
- Ribbon通过配置文件自定义负载均衡规则
- JAVA之旅(二十五)——文件复制,字符流的缓冲区,BufferedWriter,BufferedReader,通过缓冲区复制文件,readLine工作原理,自定义readLine
- 对于当浏览器通过ajax加载的文件还是修改前的文件的处理方法
- 加载自定义 cell 的 XIB 文件 的两种方式
- spring boot通过jar包启动时,配置文件的加载顺序
- 在封装过程中通过修改注册表更改用户配置文件的默认路径 推荐
- 【转】【Android测试技巧】01. root后adb shell默认不是root用户时,如何将文件放入手机系统中
- 加载自定义的配置文件
- Myeclipse从SVN上加载项目时出现jar包混乱和项目里面的文件没错项目名上面有红叉解决方法
- webview加载页面为什么在UI线程里面做,难道不是耗时操作么
- 161216、使用spring的DefaultResourceLoader自定义properties文件加载工具类
- 轻松下载.pdf文件(直接下载,而不是在IE浏览器里面打开)
- webservice通过soap协议出现不能加载wsdl文件解决办法
- 部署maven时,src/main/resources里面配置文件加载不到webapp下classes路径下的问题