(一)Android 占位式/插桩式 插件化之`Activity`

1,628 阅读3分钟

源码 github.com/itang01/jia…

没有安装的apk插件,是没有运行环境的,宿主app需要通过 代理类 去加载没有安装的apk插件(主要加载class和资源文件),将宿主的环境传递给插件,这种方式就叫做占位/插桩,所以叫 占位式(插桩式)插件化

插件中启动Activity流程

第一步,加载插件

关键点

  1. DexClassloader (插件的DexClassloader,new 创建);
  2. AssetManager(反射创建,AssetManager 被 final 修饰);
  3. Resources (插件的Resources,new 创建);
  4. PackageManager、PackageInfo 和 ActivityInfo;
  5. 了解系统源码,反射的使用等等;

具体步骤

MainActivity

/**
 * 1. 加载插件
 */
public void loadPlugin(View view) {
    PluginManager.getInstance(this).loadPlugin();
}

PluginManager

/**
 * 加载插件 class and resources
 */
public void loadPlugin() {
    try {
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
        if (!file.exists()) {
            Log.d(TAG, "插件包 不存在...");
            return;
        }
        String pluginPaht = file.getAbsolutePath();
        // >>>> 下面是加载插件里面的 class
        // dexClassLoader需要一个缓存目录   /data/data/当前应用的包名/pDir
        File fileDir = context.getDir("pDir", Context.MODE_PRIVATE);
        // Activity class
        dexClassLoader = new DexClassLoader(pluginPaht, fileDir.getAbsolutePath(), null, context.getClassLoader());
        // >>>> 下面是加载插件里面的layout
        // 加载资源
        AssetManager assetManager = AssetManager.class.newInstance();
        // 我们要执行此方法,为了把插件包的路径 添加进去
        // public final int addAssetPath(String path)
        Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class); // 他是类类型 Class
        addAssetPathMethod.invoke(assetManager, pluginPaht); // 插件包的路径   pluginPaht
        Resources r = context.getResources(); // 宿主的资源配置信息
        // 特殊的 Resources,加载插件里面的资源的 Resources
        resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration()); // 参数2 3  资源配置信息
    } catch (Exception e) {
        e.printStackTrace();
    }
}

第二步,启动插件里面的Activity

MainActivity

/**
 * 2. 启动插件里面的Activity
 */
public void startPluginActivity(View view) {
    File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
    String path = file.getAbsolutePath();
    // 获取插件包 里面的 Activity
    PackageManager packageManager = getPackageManager();
    PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
    ActivityInfo activityInfo = packageInfo.activities[0];
    // 占位  代理Activity
    Intent intent = new Intent(this, ProxyActivity.class);
    intent.putExtra("className", activityInfo.name);
    startActivity(intent);
}

ProxyActivity

/**
 * 代理的Activity 代理/占位 插件里面的 Activity
 */
public class ProxyActivity extends Activity {

    @Override
    public Resources getResources() {
        return PluginManager.getInstance(this).getResources();
    }

    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance(this).getClassLoader();
    }

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

        // 真正的加载 插件里面的 Activity
        String className = getIntent().getStringExtra("className");

        try {
            Class mPluginActivityClass = getClassLoader().loadClass(className);
            // 实例化 插件包里面的 Activity
            Constructor constructor = mPluginActivityClass.getConstructor(new Class[]{});
            Object mPluginActivity = constructor.newInstance(new Object[]{});

            ActivityInterface activityInterface = (ActivityInterface) mPluginActivity;

            // 注入
            activityInterface.insertAppContext(this);

            Bundle bundle = new Bundle();
            bundle.putString("appName", "我是宿主传递过来的信息");

            // 执行插件里面的onCreate方法
            activityInterface.onCreate(bundle);

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

    @Override
    public void startActivity(Intent intent) {
        String className = intent.getStringExtra("className");

        Intent proxyIntent = new Intent(this, ProxyActivity.class);
        proxyIntent.putExtra("className", className); // 包名+TestActivity

        //让 Activity 进栈(本例子中的 TestActivity )
        super.startActivity(proxyIntent);
    }
}

PluginActivity

public class PluginActivity extends BaseActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.plugin_main);
                // this 会报错,因为插件没有安装,也没有组件的环境,所以必须使用宿主环境
        Toast.makeText(appActivity, "我是插件", Toast.LENGTH_SHORT).show();
        findViewById(R.id.bt_start_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(appActivity, TestActivity.class));
            }
        });
    }

}

BaseActivity

public class BaseActivity extends Activity implements ActivityInterface {

    public Activity appActivity; // 宿主的环境

    @Override
    public void insertAppContext(Activity appActivity) {
        this.appActivity = appActivity;
    }

    public void setContentView(int resId) {
        appActivity.setContentView(resId);
    }

    public View findViewById(int layoutId) {
        return appActivity.findViewById(layoutId);
    }

    @Override
    public void startActivity(Intent intent) {
        Intent intentNew = new Intent();
        intentNew.putExtra("className", intent.getComponent().getClassName());
        appActivity.startActivity(intentNew);
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onCreate(Bundle savedInstanceState) {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStart() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onResume() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onDestroy() {

    }
}

ActivityInterface

/**
 * 标准
 */
public interface ActivityInterface {

    /**
     * 把宿主(app)的环境 给 插件
     * @param appActivity
     */
    void insertAppContext(Activity appActivity);

    // 生命周期方法
    void onCreate(Bundle savedInstanceState);

    void onStart();

    void onResume();

    void onDestroy();

}

TestActivity

public class TestActivity extends BaseActivity {

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

        setContentView(R.layout.activity_test);
    }
}

包结构