Adroid动态加载Apk-插件化技术框架(动态代理方案)

技术:Android + java +动态加载+插件化
 

概述

为什么要使用插件化?在开发中,一个项目只会越做越大。初始版本可能是单一功能,后续可能加上各种风马牛不相及的功能。所以我认为插件化可以使得业务分离的更彻底,一人负责哪几个模块,问题也能快速定位。但是也会带来问题:插件和插件之间的交互的复杂性更高、底层支持库因为多个插件需要使用相同的代码可能会变得很大。所以插件化看似解耦了程序员的职责,实际上对于代码质量的要求更高。   实现插件化,最快的方法就是找一个第三方框架。但是要想真正理解,需要真正自己写一个.下面本文就带大家写一个动态加载插件化的框架

详细

 

Android动态加载Apk-插件化技术(动态代理方案)

一.概述

 

为什么要使用插件化?在开发中,一个项目只会越做越大。初始版本可能是单一功能,后续可能加上各种风马牛不相及的功能。所以我认为插件化可以使得业务分离的更彻底,一人负责哪几个模块,问题也能快速定位。但是也会带来问题:插件和插件之间的交互的复杂性更高、底层支持库因为多个插件需要使用相同的代码可能会变得很大。所以插件化看似解耦了程序员的职责,实际上对于代码质量的要求更高。

  要想实现插件化,最快的方法就是找一个第三方框架接入。但是要想真正理解,需要真正自己写一个.下面本文就带大家写一个动态加载插件化的框架



二. 什么是插件化

 

1. 主App(宿主App)加载插件apk的实现

2. 每个业务组件模块形成一个独立的Apk, 然后通过主App动态加载部署业务组件模块Apk的一种方案

三.效果演示图&应用场景

1.效果演示图:

1551677279143090192.png 1551677395686035818.png

 

2.实际开发中,比如微信和支付宝的如下页面就是典型的插件化应用场景

1551677447098003903.jpg 1551677489063007128.jpg

 

三.插件化的优点好处

1. 业务组件解耦,能够实现业务组件模块的热插拔

2. 更改产品迭代模式,可分为主App和次Apk(动态加载业务组件模块)

3. 改善产品更新过程,可以在不影响用户的情况下实现业务组件模块更新以及重要Bug修复

4. 减轻主App的内存和CPU占用,提高应用的性能.

四.插件化的思想

动态加载Apk的主要思想是:主App是被系统(PMS)安装,被系统(AMS)调用,整个过程都是由系统提供的,而插件Apk并非一个真正的Apk,只是一个打包成Apk的一个组件模块,因为它并非被系统安装调用.简言之,需要讲插件Apk看成一个”非Apk”文件,只是一个结构比较复杂的压缩打包成Apk格式的文件.调用插件即用某种特殊技术手段打开文件并执行其相关代码.

五.插件化的步骤-分析主App

1.主APp打包完成解压后,会有dex,images,xml,asset等类型文件

2.Dex靠PathClassLoader加载运行

3.图片以及xml等资源依靠Resources&AssetManager加载管理

六.插件化的实现流程

 

1551676499854069490.png

 

六. 插件化的代码实现步骤

1.创建DexClassLoader加载插件化Apk相关代码,核心代码如下:

1
2
3
4
5
6
7
/**'
 * 创建DexClassLoader
 */
private DexClassLoader createDexClassLoader(String apkPath) {
    File file = mContext.getDir("dex",Context.MODE_PRIVATE);
    return new DexClassLoader(apkPath,file.getAbsolutePath(),null,mContext.getClassLoader());
}

 

 

2.创建Resources&AssetManager来加载插件化Apk的资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * 获取到插件中的Resource
 */
private Resources createResources(AssetManager am) {
    Resources resources = mContext.getResources();
    return new Resources(am,resources.getDisplayMetrics(),resources.getConfiguration());
}
 
/**
 * 获取插件的AssetManager
 */
private AssetManager createAssetManager(String apkPath) {
    try {
        AssetManager am = AssetManager.class.newInstance();
        Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class);
        method.invoke(am,apkPath);
        return am;
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return null;
}

 

 

3.管理插件Apk里的组件(如Activity)的生命周期

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.czm.pluginlib;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
 
/**
 * Created by caizhiming on 2018/3/3.
 */
 
public interface IPlugin {
 
    int FROM_INTERNAL = 0;//内部跳转
    int FROM_EXTERNAL = 1;//外部跳转
 
    void attach(Activity activity);
 
    void onCreate(Bundle bundle);
    void onStart();
    void onRestart();
    void onActivityResult(int requestCode, int resultCode, Intent data);
    void onResume();
    void onPause();
    void onStop();
    void onDestroy();
}

 

4.通过代理模式实现对插件Apk里面组件的管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@Override
protected void onCreate( Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mClassName = getIntent().getStringExtra("className");
    mPluginApk = XCPluginManager.getInstance().getPluginApk();
 
    launchPluginActivity();
}
 
private void launchPluginActivity() {
    if(mPluginApk == null){
        throw new RuntimeException("请先加载插件Apk");
    }
    try {
        //clazz 就是Activity的实例对象,但是该对象没有生命周期,没有上下文环境
        Class<?> clazz = mPluginApk.mDexClassLoader.loadClass(mClassName);
        Object object = clazz.newInstance();
        if(object instanceof IPlugin) {
            mIPlugin = (IPlugin) object;
            mIPlugin.attach(this);
            Bundle bundle = new Bundle();
            bundle.putInt("FROM",IPlugin.FROM_EXTERNAL);
            mIPlugin.onCreate(bundle);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
 
@Override
public Resources getResources() {
    if(mPluginApk != null) {
        return mPluginApk.mResources;
    } else {
        return super.getResources();
    }
}
 
@Override
public AssetManager getAssets() {
    if(mPluginApk != null) {
        return mPluginApk.mAssetManager;
    }else {
        return super.getAssets();
    }
}
 
@Override
public ClassLoader getClassLoader() {
    if(mPluginApk != null) {
        return mPluginApk.mDexClassLoader;
    }else {
        return super.getClassLoader();
    }
}

 

以上就是实现插件化的主要过程步骤,具体细节优化读者可以自己扩展优化补充.

 

七.项目代码目录结构图

 

apkplugin.png

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

posted on   demo例子集  阅读(1786)  评论(0编辑  收藏  举报

(评论功能已被禁用)
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示