The article directories

Android plug-ins series of articles

preface

  1. The host module starts the plug-in module process

    1.1. Obtain the module name of “plug-in”

    1.2. “Plug-in” module class loader is obtained

  2. The “host” module loads the resource files in the “plug-in” module

  3. Blog resources

The “host” module starts the “plug-in” module process

1. Obtain the module name of “plug-in”

Load the “plug-in” module APK installation package:

Copy the plug-in package: After the application is started, copy the apk file of the plug-in package from assets to getExternalFilesDir(NULL).

Load plug-in packages: Use PluginManager to load plug-in packages; ··· /* Copy the plugin package to */ String path = getExternalFilesDir(NULL).getabsolutePath () + “/plugin-debug.apk”; PluginManager.getInstance().loadPlugin(this, path); …

Get the Activity array information from the plug-in module:

/ / the Activity of "plug-in" module array information ActivityInfo [] activityInfos = PluginManager. GetInstance (). GetmPackageInfo () activities;Copy the code

GetAbsolutePath () + “/plugin-debug.apk” loadPath getExternalFilesDir(null).getabsolutePath () + “/plugin-debug.apk”

/** * PluginManager {private PackageInfo mPackageInfo; /** * loadPlugin * @param loadPath loadPlugin context */ public void loadPlugin(context context, String loadPath) { this.mContext = context; / / the Activity class in the plug-in package information mPackageInfo = context. GetPackageManager (). GetPackageArchiveInfo (loadPath, // The loaded plug-in package path packagemanager.get_activities); * @return */ public PackageInfo getmPackageInfo() {return mPackageInfo; }}Copy the code

Get the class name of the Activity to start: Get the class name of the Activity in the plug-in package, wrap it in the Intent, and start the Activity.

Public void onClick(View View) {public void onClick(View View) {public void onClick(View View) {public void onClick(View View)  Intent(this, ProxyActivity.class); / / the Activity of "plug-in" module array information ActivityInfo [] activityInfos = PluginManager. GetInstance (). GetmPackageInfo () activities;  // The Activity in the plugin package is not empty, If (activityInfos.length > 0) {// This is the order in which the Activity components are defined in the androidmanifest.xml manifest file A Launcher Activity must be defined in the first place. // The Activity component cannot be defined before the Launcher Activity. // The full class name of the target component of the agent is passed in intent.putExtra("CLASS_NAME", activityInfos[0]); startActivity(intent); }}Copy the code

Pay special attention to the order of the Activity components in the androidmanifest.xml manifest of the plug-in package. The order of the Activity components must be defined as the bottom one.

<? The XML version = "1.0" encoding = "utf-8"? > <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="kim.hsl.plugin"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Plugin"> <activity android:name=".PluginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>Copy the code

2. Obtain the “plug-in” module class loader

Given the full class name of the Activity to start in the plug-in package, in the ProxyActivity, you can use the classloader obtained from the PluginManager based on the full class name of the Activity. Use reflection to load the Activity class corresponding to the full class name and use reflection to initialize the class.

/** * The Activity is just an empty shell; Public class ProxyActivity is used to hold the Activity class loaded from APK and call the PluginActivity lifecycle method */ in the ProxyActivity declaration cycle method extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_proxy); ClassName = getIntent().getStringExtra("CLASS_NAME"); // Use the ClassLoader to load the interface components in the plug-in. Class<? > clazz = getClassLoader().loadClass(className); // Use reflection to create a plug-in interface component Activity Activity Activity = (Activity) clazz.newinstance (); If (Activity instanceof PluginActivityInterface){// If yes PluginActivity = (PluginActivityInterface) activity; PluginActivityInterface = PluginActivityInterface; // Context injection // Inject the ProxyActivity binding into the PluginActivity class of the plugin package // The PluginActivity has a running context // Once the binding injection is successful, The proxied PluginActivity also has a context pluginactivity.attach (this); / / call pluginActivity. OnCreate (savedInstanceState); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); }}}Copy the code

Note that ProxyActivity’s getClassLoader must be overridden, otherwise classloaders using the “host” module will not find the bytecode classes in the “plug-in” module installation package.

/** * The Activity is just an empty shell; Public class ProxyActivity is used to hold the Activity class loaded from APK and call the PluginActivity lifecycle method */ in the ProxyActivity declaration cycle method Extends AppCompatActivity {/** * a ClassLoader that needs to be loaded using the plug-in package * @return */ @override public ClassLoader getClassLoader() {return PluginManager.getInstance().getmDexClassLoader(); }}Copy the code

The plug-in package class loader is obtained by creating a DexClassLoader, which needs to pass in the dex bytecode class in the plug-in package.

/** * Public class PluginManager {/** * class loaders * used to load bytecode objects in the classes. Dex file of the plugin package apk */ private DexClassLoader mDexClassLoader; /** * loadPlugin * @param loadPath loadPlugin context */ public void loadPlugin(context context, String loadPath) { this.mContext = context; // The DexClassLoader optimizedDirectory operation directory must be private // (mode must be context.mode_private) File optimizedDirectory = context.getDir("plugin", Context.MODE_PRIVATE); / / create DexClassLoader mDexClassLoader = new DexClassLoader (loadPath, / / loading paths optimizedDirectory getAbsolutePath (), // apk decompress the cache directory null, context.getClassLoader() // DexClassLoader's parent class loader); } public DexClassLoader getmDexClassLoader() {return mDexClassLoader; }}Copy the code

The “host” module loads the resource file in the “plug-in” module

In the “host” module, using Resources is not available in the “plug-in” module resource file, before using the “plug-in” module resource file, must be loaded in the resource file.

/** * The Activity is just an empty shell; Public class ProxyActivity is used to hold the Activity class loaded from APK and call the PluginActivity lifecycle method */ in the ProxyActivity declaration cycle method Extends AppCompatActivity {/** * requires Resources loaded in the plugin package * @return */ @override public Resources getResources() {return PluginManager.getInstance().getmResources(); }}Copy the code

The Resources in the plug-in package are loaded as follows:

First, create an AssetManager through reflection,

Then, reflection gets the addAssetPath hiding method in AssetManager,

Then, call the addAssetPath method of the AssetManager,

Finally, create the Resources object through AssetManager;

/ pluggable framework core class * * * * / public class PluginManager {/ loading plug-ins * * * * @ param context the context of the load the plug-in application * @ param loadPath loaded plug-in package address * / public void loadPlugin(Context context, String loadPath) { this.mContext = context; / / load resources try {/ / by reflection to create AssetManager AssetManager AssetManager = AssetManager. Class. NewInstance (); Method addAssetPathMethod = assetManager.getClass (). getDeclaredMethod("addAssetPath"); / / call reflection method addAssetPathMethod. Invoke (assetManager, loadPath); MResources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration() ); } the catch (IllegalAccessException e) {/ / call AssetManager. Class. NewInstance () constructor reflected abnormal e.p rintStackTrace (); } the catch (InstantiationException e) {/ / call AssetManager. Class. NewInstance () constructor reflected abnormal e.p rintStackTrace (); } catch (NoSuchMethodException e) {// getDeclaredMethod error e.printStackTrace(); } the catch (InvocationTargetException e) {/ / invoke execute reflection method abnormal e.p rintStackTrace (); Public Resources getmResources() {return mResources; }}Copy the code

Third, blog resources

Blog Resources:

Making: github.com/han1202012/…