Hands-on implementation of Android app plug-in _android

Source: Internet
Author: User
Tags constant reflection

Android plug-in currently has a lot of open source projects in China, but if not actually developed once, it is difficult to master very well.

The following is their own starting from 0, combined with the current open source projects and blogs, hands-on development of plug-in programs.

The following are some of the main issues to be addressed in the plug-in as needed:

1. Loading of code

(1) To solve the load of pure Java code

(2) Android components loaded, such as activity, Service, broadcast Receiver, ContentProvider, because they are life-cycle, so special treatment

(3) The loading of Android native code

(4) The handling of special Android controls, such as notification, etc.

2. Resource Loading

The resources of different plug-ins how to manage, is the common set or plug-in independent management?

Because access to resources in Android is through R. Realized,

The following is a step-by-step solution to the above problem

1. Load of pure Java code

The main thing is to add the path of the plug-in to the original array through ClassLoader, change dexelements.

Detailed analysis can refer to my reprint of an article, because the feeling of the original paste naming and structure is a bit messy, so reproduced record.

https://my.oschina.net/android520/blog/794715

Android provides Dexclassloader and Pathclassloader, all inherited Basedexclassloader, but the parameters of the construction method is different, that is, Optdex path is different, the source code is as follows

Dexclassloader.java public
class Dexclassloader extends Basedexclassloader {public
 dexclassloader (String Dexpath, String optimizeddirectory,
  string LibraryPath, ClassLoader parent) {
 super (Dexpath, New File ( Optimizeddirectory), LibraryPath, parent);
 }

Pathclassloader.java public
class Pathclassloader extends Basedexclassloader {public
 Pathclassloader ( String Dexpath, ClassLoader parent) {
 super (dexpath, NULL, NULL, parent);
 }

 Public Pathclassloader (String Dexpath, String LibraryPath,
  ClassLoader parent) {
 super (Dexpath, NULL, LibraryPath, parent);
 }


Among them, Optimizeddirectory is used to store the opt after the Dex directory, must be the internal storage path.

Dexclassloader can load the external Dex or APK, as long as the OPT path is set by the parameters of an internal storage path.

Pathclassloader can only load installed APK, because the OPT path will use the default Dex path, not externally.

The following describes how to load Java code through the Dexclassloader implementation, referring to the Nuwa

This approach is similar to a hot fix, and if the plug-in and host code have access to each other, it needs to be implemented using the insert pile technique in packaging.

public static Boolean Injectdexatfirst (String Dexpath, String dexoptpath) {//Get the system's dexelements Object basedexelements

 = Getdexelements (Getpathlist (Getpathclassloader ())); Get patch dexelements Dexclassloader patchdexclassloader = new Dexclassloader (Dexpath, Dexoptpath, DexPath,
 Getpathclassloader ());

 Object patchdexelements = getdexelements (Getpathlist (Patchdexclassloader));

 Combination of the newest dexelements Object alldexelements = Combinearray (patchdexelements, basedexelements);
 Add the latest dexelements to the ClassLoader of the system Object PathList = Getpathlist (Getpathclassloader ());
Fieldutils.writefield (PathList, "dexelements", alldexelements);

public static ClassLoader Getpathclassloader () {return DexUtils.class.getClassLoader ();} /** * Reflection invokes Getpathlist method to obtain data * @param classLoader * @return * @throws classnotfoundexception * @throws nosuchfieldexc Eption * @throws illegalaccessexception */public static Object getpathlist (ClassLoader ClassLoader) throws Classnotfoun Dexception, NosuchFieldexception, Illegalaccessexception {return Fieldutils.readfield (ClassLoader, "pathlist");} /** * Reflection invokes Dexelements data for PathList objects * @param pathlist * @return * @throws nosuchfieldexception * @throws illegalaccess
 Exception */public static object Getdexelements (Object pathlist) throws Nosuchfieldexception, Illegalaccessexception {
 LOGUTILS.D ("Reflect to get dexelements");
Return Fieldutils.readfield (PathList, "dexelements"); /** * Stitching dexelements, Patch Dex into the original Dex's head * @param firstelement * @param secondelement * @return/public static

 Object Combinearray (Object Firstelement, Object secondelement) {logutils.d ("Combine dexelements"); Gets the class object of an array, and if the object is an array, GetClass can only return the array type, and Getcomponenttype can return the actual type Class Objtypeclass = Firstelement.getclass of the array (

 ). Getcomponenttype ();
 int firstarraylen = Array.getlength (firstelement);
 int secondarraylen = Array.getlength (secondelement);

 int Allarraylen = Firstarraylen + Secondarraylen; Object Allobject = Array.newinstancE (Objtypeclass, Allarraylen); for (int i = 0; i < Allarraylen i++) {if (I < Firstarraylen) {Array.set (Allobject, I, Array.get (Firstelement,
 i));
 else {array.set (allobject, I, Array.get (secondelement, I-firstarraylen));
} return allobject;

 }

The activity that is started in the above way is life-cycle and should be done using the system's default creation activity rather than its own new activity object, so the open activity lifecycle is normal.

However, the above method must ensure that the activity is registered in the host Androidmanifest.xml.

2. Below is a description of how to load unregistered activity features

The loading principle of activity reference https://my.oschina.net/android520/blog/795599

Mainly through the hook system Iactivitymanager complete

3. Resource Loading

Resource access is through the R. Way, in fact Android generates an int constant value of 0x7f****** format, associating the corresponding resource.

If the resource changes, such as layout, IDs, drawable, and so on, the R.java content will be regenerated, and the int constant value will change.

The resource in the plug-in cannot be accessed through R because it is not compiled by a resource participating in the host program.

Specific principle Reference: http://www.jb51.net/article/100245.htm

When you add a plug-in path to the host program by using the Addassetpath method, because the plug-in is packaged separately, the resource ID starts at 1 and the host program starts at 1, which can cause conflicts between the plug-in and the host resource, and the system loads the resource with the most recent resource found. So there is no guarantee that the interface shows the host or the plugin.

In this way, you can change the scope of the resource ID generation for each plug-in when you package it, and refer to the public.xml introduction.

Code Reference Amigo

public static void Loadpatchresources (context context, String Apkpath) throws Exception {Assetmanager Newassetmanager =
 AssetManager.class.newInstance ();
 InvokeMethod (Newassetmanager, "Addassetpath", Apkpath);
 InvokeMethod (Newassetmanager, "ensurestringblocks");
Replaceassetmanager (context, Newassetmanager); private static void Replaceassetmanager (context context, Assetmanager Newassetmanager) throws Exception {Collection
 <WeakReference<Resources>> references; if (Build.VERSION.SDK_INT >= build.version_codes.
 KitKat) {class<?> Resourcesmanagerclass = Class.forName ("Android.app.ResourcesManager");

 Object Resourcesmanager = Invokestaticmethod (Resourcesmanagerclass, "getinstance"); if (GetField (Resourcesmanagerclass, "mactiveresources")!= null) {arraymap<?, weakreference<resources>>
  Arraymap = (arraymap) Readfield (Resourcesmanager, "mactiveresources", true);
 References = Arraymap.values (); else {references = (Collection) reaDfield (Resourcesmanager, "mresourcereferences", true); } else {hashmap<?, weakreference<resources>> map = (HASHMAP) Readfield (Activitythreadcompat.instance ()
 , "Mactiveresources", true);
 References = Map.values (); } Assetmanager Assetmanager = Context!= null?
 Context.getassets (): null;
 for (weakreference<resources> wr:references) {resources = Wr.get ();

 if (resources = null) continue;
  try {Writefield (resources, "massets", Newassetmanager);
 Originalassetmanager = Assetmanager;
  catch (Throwable ignore) {Object Resourceimpl = Readfield (Resources, "Mresourcesimpl", true);
 Writefield (Resourceimpl, "Massets", Newassetmanager);
 } resources.updateconfiguration (Resources.getconfiguration (), Resources.getdisplaymetrics ()); } if (Build.VERSION.SDK_INT >= build.version_codes.
  Lollipop) {for (weakreference<resources> wr:references) {resources = Wr.get ();

  if (resources = null) continue; Android. util.

  pools$synchronizedpool<typedarray> Object Typedarraypool = Readfield (Resources, "Mtypedarraypool", true);
 Clear all the pools while (InvokeMethod (Typedarraypool, "acquire")!= null);
 }
 }
}

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

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.