The article directories

Android plugin series of articles

  1. The class loader that loads the plug-in package dex
  2. Life cycle callback method
  3. Proxy Activity component
  4. Blog resources

Refer to the implementation idea given in the “pile-driven” plug-in framework (principle and implementation idea) of [Android plug-in], and gradually realize the “pile-driven” plug-in framework;

In 】 【 Android plug-in plug-in pile “pluggable framework (class loader created | resource loading) blog, developed DexClassLoader class loader to load plug-in package, and use the AssetManager load module resources of plug-in package,

In this blog, we develop a local Activity pile, namely the empty shell Activity, which is used to hold the plug-in interface component and callback the corresponding lifecycle method of the plug-in interface Activity component in the lifecycle.

First, load the plug-in package dex class loader

Define a ProxyActivity, ProxyActivity, in the pluginizable framework. This Activity is an empty shell that holds the PluginActivity object loaded from apk. Call the lifecycle method of the corresponding PluginActivity class in the ProxyActivity declaration cycle method

Set the name of the full class to be loaded in ProxyActivity in the member property;

Private String className = ""; private String className = ";Copy the code

If you want to use a classloader to load the ProxyActivity in the apK package, you cannot use the application’s own classloader. The PluginManager classloader already loads the dex file in the apK package. So you can get the PluginActivity bytecode object;

/ / create DexClassLoader mDexClassLoader = new DexClassLoader (loadPath, / / loading paths optimizedDirectory getAbsolutePath (), // apk decompress the cache directory null, context.getClassLoader() // DexClassLoader parent class loader);Copy the code

In projects that support plug-ins, both the “host” module and the “plug-in” module depend on the “plug-in framework”;

The PluginActivity class object in the plug-in package APK is loaded by invoking the classloader in the PluginManager singleton in the plug-in framework.

/** * public class PluginManager {/** * class loader * for loading bytecode objects in classes.dex in apk */ private DexClassLoader mDexClassLoader; /** * Get the classloader * @return */ public DexClassLoader getmDexClassLoader() {return mDexClassLoader; }}Copy the code

Set the classloader in ProxyActivity as the classloader in PluginManager.

Public class ProxyActivity extends AppCompatActivity {/** * Extends the target Activity component class name */ private String className = ""; @Override public ClassLoader getClassLoader() { return PluginManager.getInstance().getmDexClassLoader(); }}Copy the code

In this way, we can call getClassLoader() in ProxyActivity to get a DexClassLoader from the plug-in manager, which is used to load bytecode class objects in the plug-in.

Second, the life cycle callback method

Define an interface that defines the lifecycle of the Activity component.

package com.example.plugin_core; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import androidx.annotation.NonNull; Public interface PluginActivityInterface {/** * attach @param proxyActivity */ void attach(Activity) proxyActivity); void onCreate(Bundle savedInstanceState); void onStart(); void onResume(); void onPause(); void onStop(); void onDestroy(); void onSaveInstanceState(Bundle outState); boolean onTouchEvent(MotionEvent event); void onBackPressed(); }Copy the code

Define an Activity base class BaseActivity that extends AppCompatActivity and implements PluginActivityInterface where the lifecycle functions involved are repeated. For example, the public void onCreate(Bundle savedInstanceState) method in AppCompatActivity and the public void in PluginActivityInterface The onCreate(Bundle savedInstanceState) method is duplicate, here annotate each method with @SuppressLint(“MissingSuperCall”) and ignore the error;

All activities in the plug-in package inherit from this BaseActivity;

The purpose of this writing is to make it easy to call the lifecycle functions of the Activity class in the plug-in package from the proxy Activity. These lifecycle functions are protected and cannot be called directly. Otherwise, each method call would have to reflect and change the accessibility first. Can be called;

package com.example.plugin_core; import android.annotation.SuppressLint; import android.app.Activity; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; Public Class BaseActivity extends AppCompatActivity Implements PluginActivityInterface {/** * Inject Activity */ private Activity that; /** * inject ProxyActivity * inject ProxyActivity component into ProxyActivity * @override public void attach(Activity proxyActivity) { that = proxyActivity; } @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 onPause() { } @SuppressLint("MissingSuperCall") @Override public void onStop() { } @SuppressLint("MissingSuperCall") @Override public void onDestroy() { } @SuppressLint("MissingSuperCall") @Override public void onSaveInstanceState(Bundle outState) { } }Copy the code

Proxy Activity component

In the ProxyActivity component ProxyActivity,

Maintains two member attributes,

Private String className = ""; private String className = ";Copy the code

The full class name of the plug-in package class, the bytecode object of which needs to be obtained by reflection;

/** * Private PluginActivityInterface pluginActivity;Copy the code

The Activity component class in the plug-in package is obtained by reflection. In each declaration cycle function of the Activity, the corresponding interfaces of the PluginActivityInterface need to be called.

In the onCreate method, we get the classloader and reflect the plug-in Activity bytecode object; And use reflection to create an Activity class object;

// Use the Class loader to load the interface component in the plug-in Class<? > clazz = getClassLoader().loadClass(className); Activity Activity = (Activity) clazz.newinstance ();Copy the code

Determine if the plug-in Activity is of type PluginActivityInterface. If it is, convert it to an object of type PluginActivityInterface and start injecting the context Activity. Wherever the context is involved in the plug-in class, the injected context object is called, that is, the context of the ProxyActivity.

If (Activity Instanceof PluginActivityInterface){// If it is This.pluginactivity = (PluginActivityInterface) Activity; // Inject the ProxyActivity binding into the PluginActivity class of the plug-in package // The PluginActivity has a context to run // Once the binding injection is successful, Then the PluginActivity being represented also has a context pluginActiv. attach(this); / / call pluginActivity. OnCreate (savedInstanceState); }Copy the code

Examples of complete code for ProxyActivity:

package com.example.plugin_core; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; /** * This Activity is an empty shell; */ public class ProxyActivity */ public class ProxyActivity */ public class ProxyActivity Extends AppCompatActivity {/** * extends compatactivity {/** * the full className of the projured target Activity component */ private String className = ""; /** * Private PluginActivityInterface pluginActivity; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_proxy); Try {// Use the ClassLoader to load the interface component of the plug-in Class<? > clazz = getClassLoader().loadClass(className); Activity Activity = (Activity) clazz.newinstance (); If (Activity Instanceof PluginActivityInterface){// If it is This.pluginactivity = (PluginActivityInterface) Activity; // Inject the ProxyActivity binding into the PluginActivity class of the plug-in package // The PluginActivity has a context to run // Once the binding injection is successful, Then the PluginActivity being represented also has a context pluginActiv. attach(this); / / call pluginActivity. OnCreate (savedInstanceState); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } @Override protected void onStart() { super.onStart(); pluginActivity.onStart(); } @Override protected void onResume() { super.onResume(); pluginActivity.onResume(); } @Override protected void onPause() { super.onPause(); pluginActivity.onPause(); } @Override protected void onStop() { super.onStop(); pluginActivity.onStop(); } @Override protected void onDestroy() { super.onDestroy(); pluginActivity.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); pluginActivity.onSaveInstanceState(outState); } @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); return pluginActivity.onTouchEvent(event); } @Override public void onBackPressed() { super.onBackPressed(); pluginActivity.onBackPressed(); } @Override public ClassLoader getClassLoader() { return PluginManager.getInstance().getmDexClassLoader(); }}Copy the code

Four, blog resources

Blog resources:

GitHub : https://github.com/han1202012/[Plugin]