Android动态加载

Android动态加载

一、动态加载Java Class

运行中使用类加载器加载sd卡中的jar包中的类,并调用该类的方法流程:

1.创建主工程

主工程很简单,就一个界面,界面中一个按钮,点击执行下面的代码

2.主工程实现插件(jar包)加载逻辑

//SD卡根目录
File sdcardPath = Environment.getExternalStorageDirectory().getAbsoluteFile();
String pluginName = "myplugin_dex.jar";
//插件(.jar 或 .dex)路径
String pluginPath = sdcardPath.getAbsolutePath() + File.separator + pluginName;
// PathClassLoader classLoader = new PathClassLoader(pluginPath, getClass().getClassLoader());
// ClassLoader classLoader = new BaseDexClassLoader(pluginPath, sdcardPath, null, getClass().getClassLoader());
//构造dex类加载器
DexClassLoader classLoader = new DexClassLoader(pluginPath, sdcardPath.getAbsolutePath(), null, getClass().getClassLoader());
 try {
   //加载SD卡jar包中的class
     Class mainClazz = classLoader.loadClass("com.example.myplugin.PluginMain");
                  
     Object pluginO = mainClazz.getConstructor(null).newInstance();

//                    IPlugin plugin = (IPlugin) pluginO;
//                    String version = plugin.getVersion();
//这里可以在主工程定义与插件中的类实现的接口相同的接口,通过强转直接调用方法,向上面这样
      Method method = mainClazz.getMethod("getVersion");
      String version = (String) method.invoke(pluginO, null);

      Log.v(TAG, "getVersion=" + version);
      mTv.setText("verson=" + version);
   } catch (Exception ) {
       e.printStackTrace();
   }

3.创建插件工程,插件工程打包生成dex,jar

​ 插件工程作为一个android library .gradle文件中 apply plugin: 'com.android.library'

​ 插件工程就是提供一个类,一些方法,生成jar包,给主工程运行时加载调用

  • 双击执行gradle的assemble任务
    在这里插入图片描述

  • 最终输出的aar文件(因为module是android library),但是我们需要中间生成的jar文件,路径为./myplugin/build/intermediates/aar_main_jar/debug/classes.jar
    在这里插入图片描述

  • dx工具在android sdk的build-tools文件夹中,先切换到dx工具所在目录,后执行以下命令对origin.jar做编译优化,输出的target.jar才可以被android的classloader加载

    • dx --dex --output=target.jar origin.jar
  • 另外,myplugin模块的buildTypes中debug中的minifyEnabled 要为false

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug{
            minifyEnabled false
        }
    }
    

4.dex/jar放入sdcard中

adb push myplugin_dex.jar /sdcard/

查看是否成功push到sd卡中,终端依次输入
adb shell
cd sdcard/                                                           
ls | grep .jar

5.运行加载插件

​ 执行加载的代码

二、加载apk中的资源

​ mypluginapk工程是一个普通的android application(apk)工程

目标:可以获取APK中的资源

流程:

1.获取资源id

这里我们传入apk的路径,获取apk的应用名称的id

public int getAppNameResId(String apkPath){
        PackageManager packageManager = this.getPackageManager();
        PackageInfo info = packageManager.getPackageArchiveInfo(apkPath, 0);
        return info.applicationInfo.labelRes;

}

2.从APK创建Resource对象

/**
     * 获去apk的res
     */
    public static Resources getResourcesObject(Context context, String apkPath) throws Exception {
        Resources res = context.getResources();

        Class<?> assertClass = Class.forName("android.content.res.AssetManager");
        Object assetMag  = assertClass.getConstructor(null).newInstance(null);
        Method method = assertClass.getMethod("addAssetPath", String.class);
        method.invoke(assetMag, apkPath);

        Class<?> resClass = Class.forName("android.content.res.Resources");
        res = (Resources) resClass.getConstructor(assertClass, res.getDisplayMetrics().getClass(), res.getConfiguration().getClass())
                .newInstance(assetMag, res.getDisplayMetrics(), res.getConfiguration());
        return res;
    }

通过反射调用AssetManager中的addAssetPath方法,可以将一个apk中的资源加载到Resources中,由于addAssetPath是隐藏api我们无法直接调用,所以只能通过反射,下面是它的声明,通过注释我们可以看出,传入的路径可以是zip文件也可以是一个资源目录,而apk就是一个zip,所以直接将apk的路径传给它,资源就加载到AssetManager中了,然后再通过AssetManager来创建一个新的Resources对象,这个对象就是我们可以使用的apk中的资源了

/** 
 * Add an additional set of assets to the asset manager.  This can be 
 * either a directory or ZIP file.  Not for use by applications.  Returns 
 * the cookie of the added asset, or 0 on failure. 
 * {@hide} 
 */  
public final int addAssetPath(String path) {  
    int res = addAssetPathNative(path);  
    return res;  
}  

3.从Resource对象获取资源,文本、图片等等

 File sdcardPath = Environment.getExternalStorageDirectory().getAbsoluteFile();
 String pluginApk = "mypluginapk-debug.apk";
 String pluginPath = sdcardPath.getAbsolutePath() + File.separator + pluginApk;

 int resId = getAppNameResId(pluginPath);
 Resources resources = getResourcesObject(MainActivity.this, pluginPath);
//获取资源
 String apkName = resources.getText(resId).toString();
 Log.v(TAG, "apkName=" + apkName);
 mTv.setText("apkName=" + apkName);

三、动态加载apk中的代码

原理也是通过类加载器,比如构造DexClassLoader对象,传入apk路径,然后通过反射调用apk中的类的方法。

项目地址:https://github.com/guoziren/Android/tree/master/AndroidDynamicLoad

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值