Android中的动态加载

来源:互联网 发布:淘宝卖高仿包的店铺 编辑:程序博客网 时间:2024/06/06 03:37

前言:

上篇说了下java的动态加载机制,最终还是为android的动态加载准备的。android中的动态加载有所不同,android中Dalvik虚拟机所支持的是.dex文件,我们项目中中的代码就是在dex文件中。而ClassLoader运用的也是"双亲代理模式",Android中运用的classloader有2中,分别是--DexClassLoader以及PathClassLoader
两者的区别:
DexClassLoader:用于加载jar/apk/dex,可以加载SD卡中的apk;
PathClassLoader:用于加载被安装过的apk。
不过项目中基本使用的都是DexClassLoader。来看下DexClassLoader的构造函数。
new DexClassLoader(dexPath, optimizedDirectory, libraryPath, parent)
第一个参数:dex/apk/jar压缩文件的路径;
第二个参数:dex解压后存放的路径;
第三个参数:C/C++依赖的本地库文件,可以为null;
第四个参数:上一级的classloader。
DexClassLoader有2中实现方式,一种是通过接口来实现,另一种是通过反射机制来实现。
其他的不多说,先来个实现结果展示,通过DexClassLoader来动态加载我们的class的实现。

分别点击两个按钮,一种是通过反射,另一种是通过接口来实现。

以下是我的工程目录:

所要动态加载的类是我们的ToastImpl.java,而ToastInterface则是ToastImpl实现的接口:

ToastInterface.java:
public interface ToastInterface {public String toastString();}

ToastInterface.java:
public class ToastImpl implements ToastInterface {@Overridepublic String toastString() {// TODO Auto-generated method stubreturn "classLoader successed";}}

然后把我们的ToastImpl.java导出生成.jar文件,再把我们的.jar文件用dx.bat转成我们的Dalvik所识别的.dex文件


准备工作已经做好,接下来就是我们的MainActivity的实现了:

public class MainActivity extends Activity {private Button flect, inter;// 通过dx.bat生成的dex文件存放的目录,这里我直接放到了工程目录的cache下private final static String DEX_PATH = "/data/data/com.example.classloaderforandroid02/cache/target.dex";// dex解压之后存放的路径,如果是一个固定的路径运行程序的时候会报错:optimizedDirectory not readable/writableprivate File dexOutputDir;//用于动态加载类的DexClassLoaderprivate DexClassLoader classLoader;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Context context = getApplicationContext();dexOutputDir = context.getDir("dex", 0);/** * 第一个参数:dex压缩文件的路径  * 第二个参数: dex解压之后存放的路径  * 第三个参数: C/C++依赖的本地库文件目录,可以为null * 第四个参数:上一级的classloader */classLoader = new DexClassLoader(DEX_PATH,dexOutputDir.getAbsolutePath(), null, getClassLoader());flect = (Button) findViewById(R.id.flect);inter = (Button) findViewById(R.id.inter);//反射方式实现flect.setOnClickListener(reflectListener);//接口方式实现inter.setOnClickListener(interfaceListener);}/** 通过反射来调用加载到的类*/private View.OnClickListener reflectListener = new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubtry {Class<?> clazz = classLoader.loadClass("com.ljx.classloader.ToastImpl");Method method = clazz.getDeclaredMethod("toastString");method.setAccessible(true);String result = (String) method.invoke(clazz.newInstance());Toast.makeText(getApplicationContext(), result, 0).show();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};/** 通过接口的方式来调用加载到的类*/private View.OnClickListener interfaceListener = new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubtry {Class<?> clazz = classLoader.loadClass("com.ljx.classloader.ToastImpl");ToastInterface toastIn = (ToastInterface) clazz.newInstance(); String result = toastIn.toastString();Toast.makeText(getApplicationContext(), result, 0).show();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};}

代码中都有注释,就不详细解释了,不过值得注意的是android中的ClassLoader是通过loadClass来加载类的,内部实现和java中的有所不同,JVM中的classloader是通过defineClass来加载类的,而Dalvik则把这个方法注释掉了。

不过android中动态加载一个类和java中动态加载一个类还有一个更麻烦的地方,比如无法使用被动态记载apk中的resource,以及在创建Activity的时候没有注册到AndroidManifest.xml中,所以在PackageManagerService扫描我们项目apk的时候没有对我们动态加载的Activity进行初始化,导致动态加载Activity无法运行。当然也有解决方法,那就是用一个代理Activity来执行我们动态加载的Activity,或者也可以通过动态创建Activity来执行我们动态加载的Activity,这里仅仅只是提供一个入门级别的动态加载,如果有兴趣的话,可以更深入的了解如何动态创建一个Activity或者创建代理Activity供我们使用。



0 0
原创粉丝点击