前言:

在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势。现如今很多项目要求需要采用类似于微信或Q游这样的插件化开发模式越来越多,本文就是阐述android的动态加载技术来满足插件化开发模式的文章。

1.基本概念

1.1在Android中可以动态加载,但无法像Java中那样方便动态加载jar。

Android的虚拟机(DalvikVM)是不认识Java打出jar的byte code,需要通过dx工具来优化转换成Dalvikbyte code才行。这一点在咱们Android项目打包的apk中可以看出:引入其他Jar的内容都被打包进了classes.dex。即android要加载的java类必须dex格式的代码文件.

1.2在Android中可以加载基于NDK的so库。

NDK的执行效率很高,加密性很好,但同时开发入门难度大,一般用于加解密、数学运算等场合。so的加载很简单,如果APK发布时已经携带了so文件,只需要在加载时调用System.loadLibrary(libName)方法即可。由于软件的安装目录中存放so的目录是没有写权限的,开发者不能更改该目录的内容,所以如果要动态加载存放在其他地方的so文件,用System.load(pathName)方法即可。

1.3在Android中支持动态加载dex文件的两种方式:

DexClassLoader这个可以加载jar/apk/dex,也可以从SD卡中加载,也是本文的重点

PathClassLoader只能加载已经安装到Android系统中的apk文件。也就是 /data/app 目录下的 apk 文件。其它位置的文件加载的时候都会出现 ClassNotFoundException.因为 PathClassLoader 会去读取 /data/dalvik-cache 目录下的经过 Dalvik 优化过的 dex 文件,这个目录的 dex 文件是在安装 apk 包的时候由 Dalvik 生成的。


2.注意

2.1 采用不用安装的插件开发模式,只能够使用DexClassLoader进行加载.不过动态加载是有一些限制的,比如插件(子apk)包中的Activity、Service类是不能动态加载的,因为缺少声明;即使你在Manifest文件中进行了声明,系统默认也是到安装apk所在的路径中去寻找类,所以你会遇到一个ClassNotFound的异常。插件里你可以用主apk中先前放入的layout、strings等资源。但是插件中自带的界面只能用纯代码进行编写,插件中是不能加载插件(子apk)中的xml作为layout等资源使用的。所以在开发上一些特效会比较困难些,建议预先植入主apk中。

2.2大家可以看看DexClassLoader的API文档,里面不提倡从SD卡加载,不安全

3.如何制作插件

3.1 把工程导出为jar包

3.2 执行SDK安装目录android-sdk-windows\platform-tools下的dx命令,把jar包转换为dex格式

dx--dex--output=dex名 jar包名

4.如何做到启动未安装的apk中的activity?

采用反射机制,把主apk中的activity的context传递到插件的activity中,然后采用反射进行回调插件activity的方法。不足之出就是,插件中的activity并不是真正的activity,它只是运行在主activity中。比如:点击返回直接退出当前activity而不是回到主activity。实例如下:

这是调用的Activity:

[java] view plain copy
  1. packagecom.beyondsoft.activity;
  2. importjava.lang.reflect.Constructor;
  3. importjava.lang.reflect.InvocationTargetException;
  4. importjava.lang.reflect.Method;
  5. importdalvik.system.DexClassLoader;
  6. importandroid.app.Activity;
  7. importandroid.content.pm.PackageInfo;
  8. importandroid.os.Bundle;
  9. importandroid.util.Log;
  10. publicclassPlugActivityextendsActivity{
  11. privateClassmActivityClass;
  12. privateObjectmActivityInstance;
  13. ClasslocalClass;
  14. privateObjectinstance;
  15. @Override
  16. protectedvoidonCreate(BundlesavedInstanceState){
  17. super.onCreate(savedInstanceState);
  18. BundleparamBundle=newBundle();
  19. paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY",true);
  20. paramBundle.putString("str","PlugActivity");
  21. Stringdexpath="/sdcard/FragmentProject.apk";
  22. Stringdexoutputpath="/mnt/sdcard/";
  23. LoadAPK(paramBundle,dexpath,dexoutputpath);
  24. }
  25. @Override
  26. protectedvoidonStart(){
  27. super.onStart();
  28. Methodstart;
  29. try{
  30. start=localClass.getMethod("onStart");
  31. start.invoke(instance);
  32. }catch(Exceptione){
  33. //TODOAuto-generatedcatchblock
  34. e.printStackTrace();
  35. }
  36. }
  37. @Override
  38. protectedvoidonResume(){
  39. //TODOAuto-generatedmethodstub
  40. super.onResume();
  41. Methodresume;
  42. try{
  43. resume=localClass.getMethod("onResume");
  44. resume.invoke(instance);
  45. }catch(Exceptione){
  46. //TODOAuto-generatedcatchblock
  47. e.printStackTrace();
  48. }
  49. }
  50. @Override
  51. protectedvoidonPause(){
  52. super.onPause();
  53. Methodpause;
  54. try{
  55. pause=localClass.getMethod("onPause");
  56. pause.invoke(instance);
  57. }catch(Exceptione){
  58. e.printStackTrace();
  59. }
  60. }
  61. @Override
  62. protectedvoidonStop(){
  63. super.onStop();
  64. try{
  65. Methodstop=localClass.getMethod("onStop");
  66. stop.invoke(instance);
  67. }catch(Exceptione){
  68. e.printStackTrace();
  69. }
  70. }
  71. @Override
  72. protectedvoidonDestroy(){
  73. //TODOAuto-generatedmethodstub
  74. super.onDestroy();
  75. try{
  76. Methoddes=localClass.getMethod("onDestroy");
  77. des.invoke(instance);
  78. }catch(Exceptione){
  79. //TODOAuto-generatedcatchblock
  80. e.printStackTrace();
  81. }
  82. }
  83. publicvoidLoadAPK(BundleparamBundle,Stringdexpath,Stringdexoutputpath){
  84. ClassLoaderlocalClassLoader=ClassLoader.getSystemClassLoader();
  85. DexClassLoaderlocalDexClassLoader=newDexClassLoader(dexpath,dexoutputpath,null,localClassLoader);
  86. try{
  87. PackageInfoplocalObject=getPackageManager().getPackageArchiveInfo(dexpath,1);
  88. if((plocalObject.activities!=null)&&(plocalObject.activities.length>0)){
  89. Stringactivityname=plocalObject.activities[0].name;
  90. Log.d("sys","activityname="+activityname);
  91. localClass=localDexClassLoader.loadClass(activityname);//结果:"com.example.fragmentproject.FristActivity"
  92. mActivityClass=localClass;
  93. ConstructorlocalConstructor=localClass.getConstructor(newClass[]{});
  94. instance=localConstructor.newInstance(newObject[]{});
  95. Log.d("sys","instance="+instance);
  96. mActivityInstance=instance;
  97. Methoddes=localClass.getMethod("test");
  98. des.invoke(instance);
  99. MethodlocalMethodSetActivity=localClass.getDeclaredMethod("setActivity",newClass[]{Activity.class});
  100. localMethodSetActivity.setAccessible(true);
  101. localMethodSetActivity.invoke(instance,newObject[]{this});
  102. MethodmethodonCreate=localClass.getDeclaredMethod("onCreate",newClass[]{Bundle.class});
  103. methodonCreate.setAccessible(true);
  104. methodonCreate.invoke(instance,paramBundle);
  105. }
  106. return;
  107. }catch(Exceptionex){
  108. ex.printStackTrace();
  109. }
  110. }
  111. }


这是被调用的Activity:

[java] view plain copy
  1. publicclassFristActivityextendsActivity{
  2. privateButtonfragment;
  3. privateButtonlistFragment;
  4. privateButtoncontrolFragment;
  5. privateButtonviewFlipper;
  6. privateButtonviewPager;
  7. privateActivityotherActivity;
  8. publicvoidtest(){
  9. Log.i("sys","测试方法执行了");
  10. }
  11. @Override
  12. publicvoidonCreate(BundlesavedInstanceState){
  13. super.onCreate(savedInstanceState);
  14. //测试DexClassLoader动态加载未安装Apk中的类
  15. TextViewt=newTextView(otherActivity);
  16. t.setText("我是测试");
  17. otherActivity.setContentView(t);//R.layout.frist_activity_main
  18. Log.i("sys","Fragment项目启动了");
  19. }
  20. publicvoidsetActivity(ActivityparamActivity){
  21. Log.d("sys","setActivity..."+paramActivity);
  22. this.otherActivity=paramActivity;
  23. }
  24. @Override
  25. publicvoidonSaveInstanceState(BundleoutState){
  26. super.onSaveInstanceState(outState);
  27. Log.i("sys","OnSaveInstance被调了");
  28. }
  29. @Override
  30. publicvoidonStart(){
  31. Log.i("sys","onStart被调了");
  32. //TODOAuto-generatedmethodstub
  33. super.onStart();
  34. }
  35. @Override
  36. publicvoidonResume(){
  37. Log.i("sys","onResume被调了");
  38. //TODOAuto-generatedmethodstub
  39. super.onResume();
  40. }
  41. @Override
  42. publicvoidonPause(){
  43. Log.i("sys","onPause被调了");
  44. //TODOAuto-generatedmethodstub
  45. super.onPause();
  46. }
  47. @Override
  48. publicvoidonStop(){
  49. Log.i("sys","onStop被调了");
  50. //TODOAuto-generatedmethodstub
  51. super.onStop();
  52. }
  53. @Override
  54. protectedvoidonDestroy(){
  55. Log.i("sys","onDestroy被调了");
  56. //TODOAuto-generatedmethodstub
  57. super.onDestroy();
  58. }
  59. }
5.参考文章

1.http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html

2.http://blog.csdn.net/mirkerson/article/details/8771723

3.http://blog.csdn.net/scliu0718/article/details/8438823

4.http://www.verydemo.com/demo_c131_i24569.htmlAndroid 通过反射启动未安装的APK中的Activity的实例代码)

5.http://www.myexception.cn/android/1217391.htmlAndroid 通过反射启动未安装的APK中的Activity的实例图形说明)

更多相关文章

  1. 如何将Android(安卓)Studio的工程导入到eclipse中
  2. Android(安卓)HIDL HAL 接口定义语言详解
  3. Android动态加载jar/dex[转]
  4. Android(安卓)NDK开发之旅32--云服务器Ubuntu下搭建NDK环境,并编
  5. Android(安卓)Studio配置环境不成功(SDK相关文件下载不下来)!!!
  6. 个人解读Activity之四-Part1
  7. Android定制:修改开机启动画面
  8. android窗体加载过程剖析之消息处理的注册机制
  9. 彻底掌握Android多分包技术MultiDex-用Ant和Gradle分别构建(一)

随机推荐

  1. [置顶] Android之异常处理:CrashHandler–
  2. Android(安卓)Monkey Test
  3. keystore文件转换格式为pk8+x509.pem
  4. android ApiDemo学习(一)service——4 Mess
  5. android周期性任务
  6. android Looper类
  7. 初学Android,音频管理器之控制音频(六十
  8. android中listView如何复用多种布局
  9. ListView无法响应OnItemClickListener事
  10. Android中的Java与JavaScript方法互调