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.