Dynamic-load-apk plugin Principle Arrangement, dynamicloadapk

Source: Internet
Author: User

Dynamic-load-apk plugin Principle Arrangement, dynamicloadapk

Because the current project has more and more functions and compilation speed is getting slower and slower (the company's computer configuration is also quite poor ...), besides, the number of methods has exceeded the limit of 65535. Although multidex is used for the time being, this is not a good solution. Therefore, using plug-ins to speed up compilation and limit the number of solutions is an increasingly important task, and there are still many new requirements in the work, so I studied and sorted out the popular plug-in framework dynamic-load-apk during the two-day holiday.

 

Framework github address: https://github.com/singwhatiwanna/dynamic-load-apk

Svn address of lib module: https://github.com/singwhatiwanna/dynamic-load-apk/trunk/DynamicLoadApk/lib

 

I. Total process of loading apk:

// Plug-in File plugin = new File (apkPath); PluginItem item = new PluginItem (); // plug-in File path item. pluginPath = plugin. getAbsolutePath (); // PackageInfo = PackageManager. getPackageArchiveInfoitem. packageInfo = DLUtils. getPackageInfo (this, item. pluginPath); // launcherActivityif (item. packageInfo. activities! = Null & item. packageInfo. activities. length> 0) {item. launcherActivityName = item. packageInfo. activities [0]. name;} // launcherServiceif (item. packageInfo. services! = Null & item. packageInfo. services. length> 0) {item. launcherServiceName = item. packageInfo. services [0]. name;} // load the apk information DLPluginManager. getInstance (this ). loadApk (item. pluginPath );

 

 

Ii. loadApk information process:
1. createDexClassLoader:

private DexClassLoader createDexClassLoader(String dexPath) {    dexOutputPath = mContext.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath();    DexClassLoader loader = new DexClassLoader(dexPath,            dexOutputPath,  //getDir("dex", Context.MODE_PRIVATE)            mNativeLibDir,  //optimizedDirectory=getDir("pluginlib", Context.MODE_PRIVATE)            mContext.getClassLoader());  //host.Appliceation.getClassLoader()    return loader;}

 

2. createAssetManager:

Private AssetManager createAssetManager (String dexPath) {try {AssetManager assetManager = AssetManager. class. newInstance (); // call the addAssetPath Method through reflection to load the apk resource to AssetManager Method addAssetPath = assetManager. getClass (). getMethod ("addAssetPath", String. class); addAssetPath. invoke (assetManager, dexPath); return assetManager;} catch (Exception e) {e. printStackTrace (); return null ;}}

The getAssets () method of DLProxyActivity will be rewritten later, and the AssetManager generated here will be returned to load resources from the plug-in apk:

@Overridepublic AssetManager getAssets() {    return impl.getAssets() == null ? super.getAssets() : impl.getAssets();}

 

3. createResources:

Private Resources createResources (AssetManager assetManager) {// create Resources superRes = mContext of Plugin through the newly created assetManager and Resources of the Host Program. getResources (); Resources resources = new Resources (assetManager, superRes. getDisplayMetrics (), superRes. getConfiguration (); return resources ;}

The getResources () method of DLProxyActivity will be rewritten later, and the Resources generated here will be returned to load Resources from the plug-in apk:

@Overridepublic Resources getResources() {    return impl.getResources() == null ? super.getResources() : impl.getResources();}

 

4. Create pluginPackage and save the plug-in information through the packageName of the plug-in:
PluginPackage = new DLPluginPackage (dexClassLoader, resources, packageInfo );
MPackagesHolder. put (packageInfo. packageName, pluginPackage );

5. copySoLib (copy the so file to the pluginlib directory of the Application ):
SoLibManager. getSoLoader (). copyPluginSoLib (mContext, dexPath, mNativeLibDir );

 

Iii. Call plug-ins:
1. To pass serializable objects to the plug-in Intent, you must use DLIntent to set the ClassLoader of the Bundle:

@ Overridepublic Intent putExtra (String name, Parcelable value) {setupExtraClassLoader (value); return super. putExtra (name, value) ;}@ Overridepublic Intent putExtra (String name, Serializable value) {setupExtraClassLoader (value); return super. putExtra (name, value);} private void setupExtraClassLoader (Object value) {ClassLoader pluginLoader = value. getClass (). getClassLoader (); DLConfigs. sPluginClassloader = pluginLoader; setExtrasClassLoader (pluginLoader); // sets the ClassLoader of Bundle}

 

2. startPluginActivity:
You need to use this method to call each other's activities within the plug-in.

Public int startPluginActivityForResult (Context context, DLIntent dlIntent, int requestCode) {if (mFrom = DLConstants. FROM_INTERNAL) {dlIntent. setClassName (context, dlIntent. getPluginClass (); receivmstartactivityforresult (context, dlIntent, requestCode); return DLPluginManager. START_RESULT_SUCCESS;} String packageName = dlIntent. getPluginPackage (); // verify the intent package name if (TextUtils. isEmpty (packageNa Me) {throw new NullPointerException ("disallow null packageName. ");} // check whether the plugin loads DLPluginPackage pluginPackage = mPackagesHolder. get (packageName); if (pluginPackage = null) {return START_RESULT_NO_PKG;} // complete class path final String className = getPluginActivityFullPath (dlIntent, pluginPackage) of the agent Activity to be called ); // Class. forName Class <?> Clazz = loadPluginClass (pluginPackage. classLoader, className); if (clazz = null) {return START_RESULT_NO_CLASS;} // obtain the class of the proxy Activity, DLProxyActivity/DLProxyFragmentActivity Class <? Extends Activity> proxyActivityClass = getProxyActivityClass (clazz); if (proxyActivityClass = null) {return START_RESULT_TYPE_ERROR;} // put extra data dlIntent. putExtra (DLConstants. EXTRA_CLASS, className); dlIntent. putExtra (DLConstants. EXTRA_PACKAGE, packageName); dlIntent. setClass (mContext, proxyActivityClass); // start the host Activity using context (context, dlIntent, requestCode); return START_RESULT_SUCCESS ;}

 

 

Iv. Activity lifecycle management:
The activity in the plug-in apk is actually a common object, not a real activity (not registered in the Host Program and not fully initialized), and does not have the activity nature, because the system starts the activity to do a lot of initialization work, it is difficult for us to start the activity at the application layer through reflection to complete the initialization work done by the system, therefore, most features of an activity cannot be managed by the lifecycle of the activity. Therefore, we need to manage the activity by ourselves.
DL uses the interface mechanism to extract most of the lifecycle methods of the activity as an interface (DLPlugin), and then calls the lifecycle method implemented by the plug-in activity through the proxy activity (DLProxyActivity, in this way, the life cycle management of the plug-in activity is completed without reflection. When we want to add a new life cycle method, you only need to declare the interface and implement it in proxy activity at the same time.

public interface DLPlugin {    public void onCreate(Bundle savedInstanceState);    public void onStart();    public void onRestart();    public void onActivityResult(int requestCode, int resultCode, Intent data);    public void onResume();    public void onPause();    public void onStop();    public void onDestroy();    public void attach(Activity proxyActivity, DLPluginPackage pluginPackage);    public void onSaveInstanceState(Bundle outState);    public void onNewIntent(Intent intent);    public void onRestoreInstanceState(Bundle savedInstanceState);    public boolean onTouchEvent(MotionEvent event);    public boolean onKeyUp(int keyCode, KeyEvent event);    public void onWindowAttributesChanged(LayoutParams params);    public void onWindowFocusChanged(boolean hasFocus);    public void onBackPressed();    public boolean onCreateOptionsMenu(Menu menu);    public boolean onOptionsItemSelected(MenuItem item);}

 

Some implementations of DLBasePluginActivity:

Public class DLBasePluginActivity extends Activity implements DLPlugin {/*** proxy activity, which can be used as Context and will decide whether to direct to this */protected Activity mProxyActivity as needed; /*** is equivalent to mProxyActivity and can be used as Context, you can decide whether to point to this <br/> * instead of this (this should point to the Activity in the plug-in, which is no longer a normal activity, so this is meaningless) * if it is the method of the Activity that has been covered in DLPlugin, you don't need to use that. You can call this directly */protected Activity that; protected DLPluginManager mPlug InManager; protected DLPluginPackage mPluginPackage; protected int mFrom = DLConstants. FROM_INTERNAL; @ Override public void attach (Activity proxyActivity, callback pluginPackage) {mProxyActivity = (Activity) proxyActivity; that = mProxyActivity; response = pluginPackage ;}@ Override public void onCreate (Bundle callback) {if (savedInstanceState! = Null) {mFrom = savedInstanceState. getInt (DLConstants. FROM, DLConstants. FROM_INTERNAL);} if (mFrom = DLConstants. FROM_INTERNAL) {super. onCreate (savedInstanceState); mProxyActivity = this; that = mProxyActivity;} mPluginManager = DLPluginManager. getInstance (that) ;}@ Override public void setContentView (View view) {if (mFrom = DLConstants. FROM_INTERNAL) {super. setContentView (view);} else {mProxyActivity. setContentView (view );}}......}

 

Implementation in the proxy class DLProxyActivity:

public class DLProxyActivity extends Activity implements DLAttachable {    protected DLPlugin mRemoteActivity;    private DLProxyImpl impl = new DLProxyImpl(this);    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        impl.onCreate(getIntent());    }    @Override    public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager) {        mRemoteActivity = remoteActivity;    }    @Override    public AssetManager getAssets() {        return impl.getAssets() == null ? super.getAssets() : impl.getAssets();    }    @Override    public Resources getResources() {        return impl.getResources() == null ? super.getResources() : impl.getResources();    }    @Override    public Theme getTheme() {        return impl.getTheme() == null ? super.getTheme() : impl.getTheme();    }    @Override    public ClassLoader getClassLoader() {        return impl.getClassLoader();    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        mRemoteActivity.onActivityResult(requestCode, resultCode, data);        super.onActivityResult(requestCode, resultCode, data);    }    @Override    protected void onStart() {        mRemoteActivity.onStart();        super.onStart();    }    ......}

 

Summary:

The two main problems of the plug-in are resource loading and Activity lifecycle management.

Resource loading:

By calling the addAssetPath method of AssetManager through reflection, we can load Resources in a plug-in apk to AssetManager, and then create a new resource object through AssetManager, then you can access the Resources in the plug-in apk through the Resources object.

Activity lifecycle management:

The interface mechanism is used to extract most of the activity life cycle methods as an interface (DLPlugin), and then call the life cycle method implemented by the plug-in activity through the proxy activity (DLProxyActivity, this completes the lifecycle management of the plug-in activity.

 

Note the following:

Plug-in project reference android-support-v4.jar, lib. jar and other libs. These cannot be packaged into the apk when the apk is generated. They are referenced only during compilation. Only the host project is compiled and packaged to ensure that there is only one copy of the host and plug-in code.

Use provided instead of compile in studio:

Dependencies {
Provided files ('provide-jars/android-support-v4.jar ')
Provided files ('provide-jars/lib. jar ')
}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.