«

android 如何动态的加载类----app插件技术

时间:2024-3-2 17:44     作者:韩俊     分类: Android


文章出处:http://blog.csdn.net/mingli198611/article/details/8858076

在目前的软硬件环境下,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-windowsplatform-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
plaincopy

package com.beyondsoft.activity;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.os.Bundle;
import android.util.Log;

public class PlugActivity extends Activity {

private Class mActivityClass;  
private Object mActivityInstance;  
Class localClass;  
private Object instance;  

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  

    Bundle paramBundle = new Bundle();  
    paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY", true);  
    paramBundle.putString("str", "PlugActivity");  
    String dexpath = "/sdcard/FragmentProject.apk";  
    String dexoutputpath = "/mnt/sdcard/";  
    LoadAPK(paramBundle, dexpath, dexoutputpath);  
}  

@Override  
protected void onStart() {  
    super.onStart();  
    Method start;  
    try {  
        start = localClass.getMethod("onStart");  
            start.invoke(instance);  
    } catch (Exception e) {  
        // TODO Auto-generated catch block  
        e.printStackTrace();  
    }  
}  

@Override  
protected void onResume() {  
    // TODO Auto-generated method stub  
    super.onResume();  
    Method resume;  
    try {  
        resume = localClass.getMethod("onResume");  
        resume.invoke(instance);  
    } catch (Exception e) {  
        // TODO Auto-generated catch block  
        e.printStackTrace();  
    }  
}  

@Override  
protected void onPause() {  
    super.onPause();  
    Method pause;  
    try {  
        pause = localClass.getMethod("onPause");  
        pause.invoke(instance);  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
}  

@Override  
protected void onStop() {  
    super.onStop();  
    try {  
        Method stop = localClass.getMethod("onStop");  
        stop.invoke(instance);  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
}  

@Override  
protected void onDestroy() {  
    // TODO Auto-generated method stub  
    super.onDestroy();  
    try {  
        Method des = localClass.getMethod("onDestroy");  
        des.invoke(instance);  
    } catch (Exception e) {  
        // TODO Auto-generated catch block  
        e.printStackTrace();  
    }  
}  

public void LoadAPK(Bundle paramBundle, String dexpath, String dexoutputpath) {  
    ClassLoader localClassLoader = ClassLoader.getSystemClassLoader();  
    DexClassLoader localDexClassLoader = new DexClassLoader(dexpath, dexoutputpath, null, localClassLoader);  
    try {  
        PackageInfo plocalObject = getPackageManager().getPackageArchiveInfo(dexpath, 1);  

        if ((plocalObject.activities != null) && (plocalObject.activities.length > 0)) {  
            String activityname = plocalObject.activities[0].name;  
            Log.d("sys", "activityname = " + activityname);  

            localClass = localDexClassLoader.loadClass(activityname);//结果:"com.example.fragmentproject.FristActivity"  
            mActivityClass = localClass;  
            Constructor localConstructor = localClass.getConstructor(new Class[] {});  
            instance = localConstructor.newInstance(new Object[] {});  
            Log.d("sys", "instance = " + instance);  
            mActivityInstance = instance;  

            Method des = localClass.getMethod("test");  
            des.invoke(instance);  

            Method localMethodSetActivity = localClass.getDeclaredMethod("setActivity", new Class[] { Activity.class });  
            localMethodSetActivity.setAccessible(true);  
            localMethodSetActivity.invoke(instance, new Object[] { this });  

             Method methodonCreate = localClass.getDeclaredMethod("onCreate", new Class[] { Bundle.class });  
             methodonCreate.setAccessible(true);  
             methodonCreate.invoke(instance, paramBundle);  
        }  
        return;  
    } catch (Exception ex) {  
        ex.printStackTrace();  
    }  
}  

}


这是被调用的Activity:

[java] view
plaincopy

public class FristActivity extends Activity{

private Button fragment;  
private Button listFragment;  
private Button controlFragment;  
private Button viewFlipper;  
private Button viewPager;  
private Activity otherActivity;  

public void test() {  
    Log.i("sys", "测试方法执行了");  
}  

@Override  
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
     // 测试DexClassLoader 动态加载未安装Apk中的类  
    TextView t = new TextView(otherActivity);  
    t.setText("我是测试");  
    otherActivity.setContentView(t);// R.layout.frist_activity_main  

    Log.i("sys", "Fragment项目启动了");  
}  

public void setActivity(Activity paramActivity) {  
    Log.d("sys", "setActivity..." + paramActivity);  
    this.otherActivity = paramActivity;  
}  

@Override  
public void onSaveInstanceState(Bundle outState) {  
    super.onSaveInstanceState(outState);  
    Log.i("sys", "OnSaveInstance被调了");  
}  

@Override  
public void onStart() {  
    Log.i("sys", "onStart被调了");  
    // TODO Auto-generated method stub  
    super.onStart();  
}  

@Override  
public void onResume() {  
    Log.i("sys", "onResume被调了");  
    // TODO Auto-generated method stub  
    super.onResume();  
}  

@Override  
public void onPause() {  
    Log.i("sys", "onPause被调了");  
    // TODO Auto-generated method stub  
    super.onPause();  
}  

@Override  
public void onStop() {  
    Log.i("sys", "onStop被调了");  
    // TODO Auto-generated method stub  
    super.onStop();  
}  

@Override  
protected void onDestroy() {  
    Log.i("sys", "onDestroy被调了");  
    // TODO Auto-generated method stub  
    super.onDestroy();  
}  

}

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.html(Android 通过反射启动未安装的APK中的Activity的实例代码)

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


标签: android

热门推荐