Dynamic loading of Android plug-in resources
I. Overview
An important issue with Android Plug-ins is the issue of plug-in resource access, which lists the issues that will be faced
1. How to load Plug-in resources
2. How to deal with plug-in resources and host resources in the process: the effect of the plug-in resource problem is, if we want to get the resources in the plug-in to find, then load the priority load plug-in, if not found, then to the host resources to find. This can do the effect of dynamic update.
3. How to ensure that Plug-ins and hosts are using the modified resources.
Two. Principle Analysis
Before you can do one thing, you have to figure out the principle, so here's how the Android resource system works.
1. Resource Chain
Context: The number of context in a apk is the sum of the application+activity+service, because they all inherit the context, but the context is just an abstract class, Its true implementation class is Contextimpl, that. For activity, in the initiation process of activity, The Attach method of the activity is invoked in the Activitythread performlaunchactivity () method to pass the Contextimp instance to the activity (that is, to assign to the member variable mbase within the activity).
In Resources:contextimpl, there is a member variable mresources of resources, representing the resource of the application, which we usually get from the call to the Getresources () method.
An important member of the assetmanager:resources is Assetmanager (massets), which points to the APK resource path, which is ultimately obtained through the acquisition of resources. It should be noted here that Assetmanager is not a stand-alone possession of resources, which means that the system is not necessarily acquired through the resource, sometimes directly through the Assetmanager, such as Typedarray, I've stepped in this hole before.
2.Android is how to construct an application of resources, and how it is passed to us to use, this is to say a lot of things, you can read another article, this is mainly about resource plug-ins.
Three. Solution to the problem
1. Load Plug-in Resources
The load of the resource is finally passed through a method within the Assetmanager Addassetpath (String path)
This method receives the parameter is the plug-in apk path, the internal will call the native method to load the plug-in apk corresponding resources to enter. However the method is hide, we cannot call directly, all can only through reflection.
This successfully constructs a assetmanager that points to the plug-in resources. Of course this time is not available, but also to invoke the Assetmanager Ensurestringblocks () method to initialize its internal parameters, also use reflection.
2. How to solve the plug-in resources and host resources of the sudden
If a resource is used, the plug-in and the host both exist, and the resource for the plug-in is used, and if only the plug-in is used, the host is used if the resource being used is only available to the host.
Assetmanager's Addassetpath () method invokes the Addassetpath () method of the native layer Assetmanager object, which, by looking at C + + code, can be called multiple times, and each call adds the corresponding resource , and later added in the use of resources is to be first searched. How to understand, C + + layer Assetmanager have a storage stack of resources, each call Addassetpath () method will be the resources of the stack, while reading the search resources from the top of the stack to search, can not find on the downward check. So we can deal with Assetmanager and get resources.
Where DexPath2 is the host apk path, Dexpath is the plug-in apk path, Superres is the host resource, and resources is the resource for the Fusion plug-in and the host.
3. How to ensure that Plug-ins and hosts are using the modified resources:
This is a very important step, we have successfully acquired the resources and modify it, now to do is to replace the Android for us to generate the resources, this is the idea of the hook.
There are two places where resources are used, One is to get it through context.getresources in Java code, where a resource is specified in an XML file (such as a layout file), but in fact the XML file is ultimately a context-specific source of resources that he generally obtains from Assetmanager. So, we can replace the resources (mresources) Inside the context object when it is created and unused. As I said before, the whole context of the application equals the number of application+activity+service, and the context is created and added to these classes when they create the object. And these behaviors are done in activitythread and instrumentation.
Take activity, for example, with the following steps:
A:activity object creation is the Newactivity method of calling instrumentation in Activitythread
Activitythread:
Instrumentation:
The B:context object is created by invoking the Createbasecontextforactivity method in Activitythread
Activitythread:
The c:activity binding context is the Attach method that invokes the activity object in Activitythread, where Appcontext is the context object created above
Activitythread:
The callback of the D:activity OnCreate () method is the Callactivityoncreate () method that calls instrumentation in Activitythread
Activitythread:
It is best to replace the resources in the context of the activity, and based on the observations above, we can replace the resources by invoking Instrumentation's Callactivityoncreate () method. So the question comes again, how do we control the execution of the Callactivityoncreate () method, and here we have to use the idea of hook, That is, replace the instrumentation object (minstrumentation) inside the Activitythread with reflection. Steps are as follows
A: Get the Activitythread object
Activitythread has a static method that returns the Activitythread object itself, so we can call the method to get the Activitythread object
However, Activitythread is hide, so it has to be handled by reflection, as follows:
B: Get the instrumentation object in Activitythread
C: Build our own instrumentation object and write the Callactivityoncreate method from
In the Callactivityoncreate method, first get the context (Mbase) in the current Activity object, and then obtain the resources (mresources) variables in the context object. In the mresources variable point to our constructed resources object, do deceitful act.
Myinstrumentation:
D: Finally, make the activitythread inside the minstrumentation variable point to the Myinstrumentation object we built.
Code
Four. Application
one application of dynamic resource loading is of course the use of Android Plug-ins. Another application is the skin-changing function, only in the project to add this code (of course, some logic), and then users want to apply for skin, theme, etc., you can download the plugin from the background apk, placed in a designated folder on the relationship between the application of resources, play the effect of skin change. Of course, the dynamic loading of resources there are other application methods, their own pondering!!!
Five. There are problems
1. Compatibility issues because the hook uses reflection to get the system hide or the private properties of the class. Hide them because of their instability, if the day Google think that the name of the variable to change the bad luck, then the error. Of course, there is a solution, which is to write different code for different APIs.
2.R aspect of the problem. When we add a resource (such as adding a string to the String.xml), the system corresponds to the ID that generates an int for the resource in R and uses it to find the corresponding resource based on that ID. Resource IDs are incremented according to the dictionary order of resource Names. Take a string to say.
If we declare a resource called Za,zb in our string.xml,
The corresponding ID will be generated in R.
Based on the above observations, we will find a problem: for example
The host resource condition is: Existence za (id=0x7f060004) ZB (id=0x7f060005)
Plug-in resource situation is: Presence Za (id=0x7f060004) Zab (id=0x7f060005) AB (0x7f060006)
At this time in the host to obtain resources ZB, according to the above, will be based on id=0x7f060005 the first plug-in resources, it is zab instead of ZB, this is a mistake.
The solution is that if you add a new resource in the plug-in, the name of the dictionary to install is incremented under the original resource. Of course there are other options, you think about it.
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.