前言:
在目前的软硬件环境下,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:
- packagecom.beyondsoft.activity;
- importjava.lang.reflect.Constructor;
- importjava.lang.reflect.InvocationTargetException;
- importjava.lang.reflect.Method;
- importdalvik.system.DexClassLoader;
- importandroid.app.Activity;
- importandroid.content.pm.PackageInfo;
- importandroid.os.Bundle;
- importandroid.util.Log;
- publicclassPlugActivityextendsActivity{
- privateClassmActivityClass;
- privateObjectmActivityInstance;
- ClasslocalClass;
- privateObjectinstance;
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- BundleparamBundle=newBundle();
- paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY",true);
- paramBundle.putString("str","PlugActivity");
- Stringdexpath="/sdcard/FragmentProject.apk";
- Stringdexoutputpath="/mnt/sdcard/";
- LoadAPK(paramBundle,dexpath,dexoutputpath);
- }
- @Override
- protectedvoidonStart(){
- super.onStart();
- Methodstart;
- try{
- start=localClass.getMethod("onStart");
- start.invoke(instance);
- }catch(Exceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- }
- @Override
- protectedvoidonResume(){
- //TODOAuto-generatedmethodstub
- super.onResume();
- Methodresume;
- try{
- resume=localClass.getMethod("onResume");
- resume.invoke(instance);
- }catch(Exceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- }
- @Override
- protectedvoidonPause(){
- super.onPause();
- Methodpause;
- try{
- pause=localClass.getMethod("onPause");
- pause.invoke(instance);
- }catch(Exceptione){
- e.printStackTrace();
- }
- }
- @Override
- protectedvoidonStop(){
- super.onStop();
- try{
- Methodstop=localClass.getMethod("onStop");
- stop.invoke(instance);
- }catch(Exceptione){
- e.printStackTrace();
- }
- }
- @Override
- protectedvoidonDestroy(){
- //TODOAuto-generatedmethodstub
- super.onDestroy();
- try{
- Methoddes=localClass.getMethod("onDestroy");
- des.invoke(instance);
- }catch(Exceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- }
- publicvoidLoadAPK(BundleparamBundle,Stringdexpath,Stringdexoutputpath){
- ClassLoaderlocalClassLoader=ClassLoader.getSystemClassLoader();
- DexClassLoaderlocalDexClassLoader=newDexClassLoader(dexpath,dexoutputpath,null,localClassLoader);
- try{
- PackageInfoplocalObject=getPackageManager().getPackageArchiveInfo(dexpath,1);
- if((plocalObject.activities!=null)&&(plocalObject.activities.length>0)){
- Stringactivityname=plocalObject.activities[0].name;
- Log.d("sys","activityname="+activityname);
- localClass=localDexClassLoader.loadClass(activityname);//结果:"com.example.fragmentproject.FristActivity"
- mActivityClass=localClass;
- ConstructorlocalConstructor=localClass.getConstructor(newClass[]{});
- instance=localConstructor.newInstance(newObject[]{});
- Log.d("sys","instance="+instance);
- mActivityInstance=instance;
- Methoddes=localClass.getMethod("test");
- des.invoke(instance);
- MethodlocalMethodSetActivity=localClass.getDeclaredMethod("setActivity",newClass[]{Activity.class});
- localMethodSetActivity.setAccessible(true);
- localMethodSetActivity.invoke(instance,newObject[]{this});
- MethodmethodonCreate=localClass.getDeclaredMethod("onCreate",newClass[]{Bundle.class});
- methodonCreate.setAccessible(true);
- methodonCreate.invoke(instance,paramBundle);
- }
- return;
- }catch(Exceptionex){
- ex.printStackTrace();
- }
- }
- }
这是被调用的Activity:
- publicclassFristActivityextendsActivity{
- privateButtonfragment;
- privateButtonlistFragment;
- privateButtoncontrolFragment;
- privateButtonviewFlipper;
- privateButtonviewPager;
- privateActivityotherActivity;
- publicvoidtest(){
- Log.i("sys","测试方法执行了");
- }
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- //测试DexClassLoader动态加载未安装Apk中的类
- TextViewt=newTextView(otherActivity);
- t.setText("我是测试");
- otherActivity.setContentView(t);//R.layout.frist_activity_main
- Log.i("sys","Fragment项目启动了");
- }
- publicvoidsetActivity(ActivityparamActivity){
- Log.d("sys","setActivity..."+paramActivity);
- this.otherActivity=paramActivity;
- }
- @Override
- publicvoidonSaveInstanceState(BundleoutState){
- super.onSaveInstanceState(outState);
- Log.i("sys","OnSaveInstance被调了");
- }
- @Override
- publicvoidonStart(){
- Log.i("sys","onStart被调了");
- //TODOAuto-generatedmethodstub
- super.onStart();
- }
- @Override
- publicvoidonResume(){
- Log.i("sys","onResume被调了");
- //TODOAuto-generatedmethodstub
- super.onResume();
- }
- @Override
- publicvoidonPause(){
- Log.i("sys","onPause被调了");
- //TODOAuto-generatedmethodstub
- super.onPause();
- }
- @Override
- publicvoidonStop(){
- Log.i("sys","onStop被调了");
- //TODOAuto-generatedmethodstub
- super.onStop();
- }
- @Override
- protectedvoidonDestroy(){
- Log.i("sys","onDestroy被调了");
- //TODOAuto-generatedmethodstub
- super.onDestroy();
- }
- }
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.html(Android 通过反射启动未安装的APK中的Activity的实例代码)
5.http://www.myexception.cn/android/1217391.html(Android 通过反射启动未安装的APK中的Activity的实例图形说明)