If you don't know what plug-in development is, then you should read this blog before: Android plug-in development, first into the palace
The previous blog mainly from the overall point of view of the Android plug-in development of several difficulties and dynamic loading of the APK is not installed in the activity and resource methods. In fact, the general plug-in development is mainly to load an activity, read some resources such as pictures. But there are always special situations, such as loading the service.
To dynamically load the service, there are two ways of thinking: one is to run the service through C + + in the form of the NDK (which I have not tried, just to listen to the friends in the group); In the form of hosting, as the previous blog is not clear, here is a detailed look at the method of loading the service in the plug-in via managed implementations.
The following points are certainly known to every Android development group: An APK is not ready to run if it is not installed. A class file for a Java class can be read by the Classload class loader. An apk is actually a compressed package that contains a. dex file that is our code file. So, the next basic idea we can be clear: the APK is not able to run directly, the APK has code files, code files can be classload read.
There are two kinds of classload in Android, namely Dexclassloader, Pathclassloader. The latter can only load the/data/app directory under the APK that APK must be installed to be loaded, which is not what we want, so we use the former: Dexclassloader.
public class cjclassloader extends dexclassloader { // Create a plug-in loader collection that uses a fixed loader for fixed dex to prevent multiple loaders from loading a single Dex error. private static final HashMap<String, CJClassLoader> Pluginloader = new hashmap<string, cjclassloader> (); Protected cjclassloader (string dexpath, string optimizeddirectory, string librarypath, classloader parent) { super (Dexpath, optimizeddirectory, librarypath, parent); } /** * Returns the dexpath corresponding to the loader */ public static cjclassloader getclassloader (STRING&NBSP;DEXPATH,&NBSP;CONTEXT&NBSP;CXT,&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSp; classloader parent) { cjclassloader cjloader = pluginloader.get (Dexpath); if (cjloader == null) { // get to the app's startup path final string dexoutputpath = cxt .getdir ("Dex", context.mode_private). GetAbsolutePath (); cjloader = new cjclassloader (dexpath, dexoutputpath, null, parent); pluginloader.put (Dexpath, cjloader); } return cjloader; }}
This is just the beginning, and then we need to consider a problem where a service has a Oncreate->onstart->ondestroy life cycle and some callback methods that are used by the parent class (including the has ..... a ... Relationship) or SDK management, when we load through the ClassLoader, it is not able to manage the parent class, that is to say we need to emulate the SDK to manage the plug-in service callback function. So the class to manage the plug-in service is the one previously mentioned.
Here is an interface that I pulled out of the service callback method to write
Public interface I_cjservice {ibinder onbind (Intent Intent); void OnCreate (); int Onstartcommand (Intent Intent, int flags, int startid); void OnDestroy (); void onconfigurationchanged (Configuration newconfig); void Onlowmemory (); void ontrimmemory (int level); Boolean onunbind (Intent Intent); void Onrebind (Intent Intent); void ontaskremoved (Intent rootintent);}
.
A managed class Cjproxyservice extends service{//adopts inclusive relationship protected i_cjservice mpluginservice; Plug-in service object}
The way to include relationships rather than inherit (or implement an interface) is because we need to rewrite the methods in the service, and the overridden methods need to use the appropriate interface methods for the interface objects.
public class cjproxyservice extends service{ @Override public void onconfigurationchanged (configuration Newconfig) { mpluginservice.onconfigurationchanged ( Newconfig); super.onconfigurationchanged (NewConfig); } @Override public void onlowmemory ( ) { mpluginservice.onlowmemory (); super.onlowmemory (); } @Override @SuppressLint ("Newapi") public void ontrimmemory (int Level) { mpluginservice.ontrimmemory (level); supEr.ontrimmemory (level); } @Override Public boolean onunbind (intent intent) { Mpluginservice.onunbind (intent); return super.onunbind ( Intent); } @Override public void onrebind (intent intent) { Mpluginservice.onrebind (intent); super.onrebind (Intent); }}
See here you should also understand that the host is actually a normal service class, but this is the normal operation of the host is the SDK management callback function, we through the service callback function to invoke the plug-in service corresponding callback method, Indirectly manages the life cycle of the plug-in service (here you can compare the relationship between activity and fragment)
So far, we've been able to successfully tune up a plugin service, and the next question is where does this I_cjsrvice object come from? It's simple to load a class loader with a
private void init (Intent itfromapp) { Object instance = null; try { class<?> serviceclass; if (CJConfig.DEF_ Str.equals (Mdexpath)) { serviceclass = super.getclassloader (). LoadClass (MClass); } else { serviceclass = this.getclassloader (). LoadClass (MClass); } constructor<?> serviceconstructor = serviceclass .getconstructor (New class[] {}); instance = serviceconstructor.newinstance (new object[] {}); } catch (exception e) { } setremoteservice (instance); mpluginservice.setproxy (This, mdexpath); } /** * Keep a plugin service object */ Protected void setremoteservice (Object service) { if (service iNstanceof i_cjservice) { mpluginservice = (I_cjservice) service; } else { throw new ClassCastException ( "Plugin service must implements i_cjservice"); } }
So you can get a I_cjsrvice object Mpluginservice, if so, there will be problems, Because at this point in the Mpluginservice, for example, the OnStart method also corresponds to the plug-in OnStart that is the parent class OnStart (here, I do not know how to describe), and before we said that through reflection loaded class is no parent class, Then if the @override method of the reflected object is invoked at this time, the null pointer will be reported because the parent class cannot be found. The solution, then, is to rewrite each @override method in the plugin service.
//... Limited space, partial interception of Public abstract class cjservice extends service implements i_ cjservice { /** * That pointer points to the context of the current plug-in (this pointer is absolutely not available due to plug-in development) */ protected Service that; // Replace this pointer @Override public ibinder onbind (intent intent) { if ( Mfrom == cjconfig.from_plugin) { return null; } else { return that.onbind (Intent); } }}
As you can see by substituting: we use a that object to replace the original this object, and then we just need to assign this object to the managed object by assigning the That is, all the that.xxx in the plug-in is equivalent to calling the managed this.xxx, then the purpose of the dynamic replacement is achieved, so we have successfully loaded an unused plugin apk service.
For the code in this class, as well as the full demo, you can focus on: Android Plug-in development framework Cjframeforandroid
Android plug-in development---run the service in the APK that is not installed