Android插件化 类加载器DexClassLoader

本文详细介绍了在Android环境中通过两种方式动态加载插件类和资源的方法。包括使用IntentFilter和APK路径构建DexClassLoader加载插件类,以及通过PackageManager获取插件资源。同时,文章还展示了如何通过接口化提高动态加载的灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

加载插件class类

方式一:

从已安装的apk中读取,必须要有一个activity,且需要配置ACTION。利用这种方式获取插件信息,然后来构建classLoader加载类。

插件APP代码:

  • AndroidManifest.xml
<intent-filter>
    <action android:name="com.jiangshuai.pluginone"/>
</intent-filter>
  • PluginClass
public class PluginClass {

  public PluginClass() {
    Log.d("JG","初始化PluginClass");
  }

  public int function(int a, int b){
    return a+b;
  }
}

宿主APP代码

  public void intentFilter(View view) {
    useIntentFilterDexClassLoader();
  }

  private void useIntentFilterDexClassLoader() {
    Intent intent = new Intent("com.jiangshuai.pluginone");
    PackageManager pm = getPackageManager();
    List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
    if (resolveInfos.size() == 0) {
      return;
    }
    ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
    String packageName = activityInfo.packageName;
    String apkPath = activityInfo.applicationInfo.sourceDir;
    String dexOutputDir = getApplicationInfo().dataDir;
    String libDir = activityInfo.applicationInfo.nativeLibraryDir;
    DexClassLoader classLoader =
        new DexClassLoader(apkPath, dexOutputDir, libDir, this.getClass().getClassLoader());
    try {
      Class<?> clazz = classLoader.loadClass(packageName + ".PluginClass");
      Object obj = clazz.newInstance();
      Class[] param = new Class[2];
      param[0] = Integer.TYPE;
      param[1] = Integer.TYPE;
      Method method = clazz.getMethod("function", param);
      Integer result = (Integer) method.invoke(obj, 12, 11);
      Log.e(TAG, "result = " + result);
    } catch (Exception e) {

    }
  }

 

方式二:利用apk path来构建DexClassLoader加载插件目标class

  public void appPathLoader(View view) {
    if (!hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
      requestPermissions(Manifest.permission.READ_EXTERNAL_STORAGE);
      return;
    }
    useApkPathDexClassLoader();
  }

  public boolean hasPermission(Context context, String permission) {
    return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
        || (context != null
        && context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
  }

  private void useApkPathDexClassLoader() {
    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/app-debug.apk";
    File codeDir = getDir("dex", Context.MODE_PRIVATE);
    DexClassLoader classLoader =
        new DexClassLoader(path, codeDir.getAbsolutePath(),
            null, this.getClass().getClassLoader());
    try {
      Class<?> clazz = classLoader.loadClass("com.jiangshuai.pluginone.PluginClass");
      Object object = clazz.newInstance();
      Class[] param = new Class[2];
      param[0] = Integer.TYPE;
      param[1] = Integer.TYPE;
      Method method = clazz.getMethod("function", param);
      Integer result = (Integer) method.invoke(object, 12, 11);
      Log.e(TAG, "result = " + result);
    } catch (Exception e) {

    }
  }

  private void requestPermissions(String permission) {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
      if (ActivityCompat.checkSelfPermission(this,
          Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        RxPermissions rxPermission = new RxPermissions(this);
        rxPermission
            .requestEach(permission)
            .subscribe(new Consumer<Permission>() {
              @Override
              public void accept(Permission permission) throws Exception {

              }
            });
      }
    }
  }

加载插件resource

方式一:通过getResourcesForApplication

    PackageManager pm = getPackageManager();
    List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
    if (resolveInfos.size() == 0) {
      return;
    }
    ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
    String packageName = activityInfo.packageName;    
    try {
      Resources res = pm.getResourcesForApplication(packageName);
      int id = res.getIdentifier("timg2", "drawable", packageName);
      ((ImageView)findViewById(R.id.image)).setImageDrawable(res.getDrawable(id));
    } catch (Exception e) {

    }

方式二:通过AssetManager

  public void resourcesLoad(View view) {
    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/app-debug.apk";
    //获得包管理器
    PackageManager pm = getPackageManager();
    PackageInfo packageInfo=pm.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES);
    String packageName=packageInfo.packageName;
    loadResources(path);
    try {
      ((ImageView)findViewById(R.id.image)).setImageDrawable(
          getResources().getDrawable(getResources().getIdentifier("timg2", "drawable", packageName)));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private AssetManager mAssetManager;
  private Resources mResources;

  @Override
  public Resources getResources() {
    return mResources == null ? super.getResources() : mResources;
  }

  private void loadResources(String dexPath) {
    try {
      AssetManager assetManager = AssetManager.class.newInstance();
      Method addAssetPath =
          assetManager.getClass().getMethod("addAssetPath", String.class);
      addAssetPath.invoke(assetManager, dexPath);
      mAssetManager = assetManager;
    } catch (Exception e) {

    }
    Resources superRes = super.getResources();
    mResources = new Resources(mAssetManager,
        superRes.getDisplayMetrics(), superRes.getConfiguration());
  }

 

上述可以通过interface化来动态加载,提高动态加载的灵活性

宿主和插件引用同一目录结构下的interface,宿主为调用方,插件为实现方

一、宿主

  public void resourcesLoadByInterface() {
    //获取插件路径
    String path= Environment.getExternalStorageDirectory().getAbsolutePath()+"/app-debug.apk";
    File codeDir=getDir("dex", Context.MODE_PRIVATE);
    //创建类加载器,把dex加载到虚拟机中
    ClassLoader classLoader = new DexClassLoader(path,codeDir.getAbsolutePath() ,null,
        this.getClass().getClassLoader());
    loadResources(path);
    //获得包管理器
    PackageManager pm = getPackageManager();
    PackageInfo packageInfo=pm.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES);
    String packageName=packageInfo.packageName;
    try {
      Class<?> clazz = classLoader.loadClass(packageName+".PluginClass");
      PluginInterface obj = (PluginInterface) clazz.newInstance();
      ((ImageView)findViewById(R.id.image)).setImageDrawable(obj.getImage(this));

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void resourcesLoad(View view) {
    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/app-debug.apk";
    //获得包管理器
    PackageManager pm = getPackageManager();
    PackageInfo packageInfo=pm.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES);
    String packageName=packageInfo.packageName;
    loadResources(path);
    try {
      ((ImageView)findViewById(R.id.image)).setImageDrawable(
          getResources().getDrawable(getResources().getIdentifier("timg2", "drawable", packageName)));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private AssetManager mAssetManager;
  private Resources mResources;

  @Override
  public Resources getResources() {
    return mResources == null ? super.getResources() : mResources;
  }

  private void loadResources(String dexPath) {
    try {
      AssetManager assetManager = AssetManager.class.newInstance();
      Method addAssetPath =
          assetManager.getClass().getMethod("addAssetPath", String.class);
      addAssetPath.invoke(assetManager, dexPath);
      mAssetManager = assetManager;
    } catch (Exception e) {

    }
    Resources superRes = super.getResources();
    mResources = new Resources(mAssetManager,
        superRes.getDisplayMetrics(), superRes.getConfiguration());
  }

插件:

public class PluginClass implements PluginInterface {

  public PluginClass() {
    Log.d("JG","初始化PluginClass");
  }

  public int function(int a, int b){
    return a+b;
  }

  @Override
  public Drawable getImage(Context context) {
    return context.getResources().getDrawable(R.drawable.timg2);
  }
}

interface:

public interface PluginInterface {

  Drawable getImage(Context context);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值