Detailed Android Plugin Development-resource access

Source: Internet
Author: User

Dynamic loading technology (also known as plug-in technology), when the project is becoming more and more large, we can not only reduce the memory and CPU usage of the application through the plug-in development, but also can realize hot-swapping, that is, to update some modules without releasing the new version.
Usually we make the Android resource file into plug-in form, no more than a few:

Zip, jar, Dex, APK (no apk installed, install apk)

For the user is not installed APK is the user needs, do not install, do not restart, silently load the resource files, this is what our developers are seeking results.
However, the development of the host program to set up the plugin is not installed APK, a big problem is how to access resources, the ID of these resource files are mapped in the R.java under the Gen folder, and the plug-in is usually the beginning of the R resources are inaccessible. The reason is because the host program does not have the resources of the plug-in, so the resources through R to load the plug-in is not feasible, the program throws an exception: unable to find the corresponding resource of the ID.
So what to do in the development, today we come together to explore the plug-in development of resource file access solutions.
Presumably everyone has written similar code in development, for example, accessing a string file in the main program

this.getResources().getString(R.string.app_name);

Here this is, in fact, the context, contextual object. Usually our APK installation path is:

/data/apk/packagename~1/base.apk

APK starts, and when the context is loaded through the ClassLoader, it goes to the APK to load the resource file. It must be known that the work of activity is mainly done by Contextimpl, there is a member variable called Mbase in activity, and its type is Contextimpl. Notice that there are two abstract methods in the context, which seem to be related to resources, in fact, the context is to obtain resources through them. The real implementation of these two abstract methods is in Contextimpl, that is to say, as long as the two methods are implemented, you can solve the resource problem.

/** Return an AssetManager instance for your application‘s package. */public abstract AssetManager getAssets();/** Return a Resources instance for your application‘s package. */public abstract Resources getResources();

If we want to use these two methods, we need to instantiate the context object, usually we can complete the creation of the context object based on the package name in the APK:

this.createPackageContext("com.castiel.demo",flags);

However, there is a premise that you must require that you load your apk when initializing, and if we are loading a plugin apk that is not installed, it is definitely not available. Why, look at the source:

Resources Resources = PackageInfo. Getresources (Mainthread); if (resources = null) {if (Activitytoken! = NULL | | Displayid! = Display.applicationscale! = R Esources.getcompatibilityinfo () .gettoplevelresources ( Packageinfo.getresdir (), Packageinfo.getoverlaydirs (), Packageinfo.getapplicationinfo () .sharedlibraryfiles, DisplayId, Overrideconfiguration, Compatinfo, Activitytoken) ;} Mresources = Resources          

The resources are assigned here, and we go to the first line of the code in PackageInfo, which comes from the loadedapk class, where the Getresources method is as follows:

public Resources getResources(ActivityThread mainThread) {        if (mResources == null) {            mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,                    mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this); } return mResources; }

The method uses a singleton pattern, noting that the first parameter in the Gettoplevelresources () method Mresdir, we continue to find its source, in the Activitythread class, found:

    /**     * Creates the top level resources for the given package.     */    Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,            String[] libDirs, int displayId, Configuration overrideConfiguration,            LoadedApk pkgInfo) {        return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,                displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);    }

Focus on the Resdir parameters, we find the source code, and finally find the Resourcesmanager class, find Gettoplevelresources () method:

    /** * Creates the top level Resources for applications with the given compatibility info. *     *@param resdir the Resource directory. *@param overlaydirs the resource overlay directories. *@param libdirs the Shared library resource dirs this app references. *@param compatinfo the compability info. Must not is null. *@param token the application token for determining stack bounds. */Public ResourcesGettoplevelresources (String resdir, string[] splitresdirs, string[] overlaydirs, string[] libdirs,int Displayid, Configuration overrideconfiguration, Compatibilityinfo compatinfo, IBinder token) {Finalfloat scale = Compatinfo.applicationscale; Resourceskey key =New Resourceskey (Resdir, Displayid, overrideconfiguration, scale, token); Resources R;Synchronized (This) {Resources are app scale dependent.if (False) {SLOG.W (TAG,"Gettoplevelresources:" + Resdir +"/" + scale);} Weakreference<resources> WR = Mactiveresources.get (key); r = WR! =Null? Wr.get ():Nullif (r! = null) slog.i (TAG, "isuptodate" + Resdir + ":" + r.getassets (). IsUpToDate ());if (r! =Null && r.getassets (). IsUpToDate ()) {if (False) {SLOG.W (TAG,"Returning Cached Resources" + R +"" + Resdir +": appscale=" + r.getcompatibilityinfo (). Applicationscale); }return R; } }if (r! = null) {SLOG.W (TAG, "throwing away out-of-date resources!!!!"+ R + "" + Resdir);} Assetmanager Assets =New Assetmanager ();Resdir can null if the ' Android ' package is creating a new Resources object.This are fine, since each assetmanager automatically loads the ' Android ' packageAlready.if (resdir! =NULL) {if (Assets.addassetpath (resdir) = =0) {ReturnNull } }if (splitresdirs! =NULL) {for (String splitresdir:splitresdirs) {if (Assets.addassetpath (splitresdir) = =0) {ReturnNull } } }if (overlaydirs! =NULL) {for (String idmappath:overlaydirs) {Assets.addoverlaypath (Idmappath);}}if (libdirs! =NULL) {for (String libdir:libdirs) {if (Assets.addassetpath (libdir) = =0) {SLOG.W (TAG,"Asset path" + Libdir +"' does not exist or contains no resources."); } } }SLOG.I (TAG, "resource:key=" + key + ", display metrics=" + metrics); Displaymetrics DM = getdisplaymetricslocked (Displayid); Configuration config;Boolean Isdefaultdisplay = (Displayid = = Display.default_display);FinalBoolean hasoverrideconfig = Key.hasoverrideconfiguration ();if (!isdefaultdisplay | | hasoverrideconfig) {config =New Configuration (GetConfiguration ());if (!isdefaultdisplay) {applynondefaultdisplaymetricstoconfigurationlocked (DM, config);}if (hasoverrideconfig) {config.updatefrom (key.moverrideconfiguration);}}else {config = GetConfiguration ();} r =New Resources (assets, DM, config, compatinfo, token);if (False) {SLOG.I (TAG,"Created App Resources" + Resdir +"" + R +":" + r.getconfiguration () + "appscale=" + r.getcompatibilityinfo (). Applicationscale); } synchronized (this) {weakreference<resources> WR = Mactiveresources.get (key); Resources existing = WR! = null? Wr.get (): null; if (existing! = null && existing.getassets (). IsUpToDate ()) {//Someone else already created the resources while we were //unlocked; go ahe AD and use theirs. R.getassets (). Close (); return existing;} //XXX need to remove entries when weak references go away mactiveresources.put (key, new weakreference<resources> (R)); return r;}            

In the comments of the method, it is clear that @param resdir the resource directory, load the local resource directory, and load its own apk.

With the above analysis, we know that the getresources () method loads its own apk through Assetmanager, then we want to load the plugin apk that is not installed, only the custom implementation of a resources class, specifically used to load the APK is not installed. But I tried, direct rewrite not, why, because Android does not provide resource construction method of the Assetmanager construction method, we look at the source:

    /**     * Create a new Resources object on top of an existing set of assets in an     * AssetManager.     *     * @param assets Previously created AssetManager.     * @param metrics Current display metrics to consider when     *                selecting/computing resource values.     * @param config Desired device configuration to consider when     *               selecting/computing resource values (optional).     */    public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) { this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO); }

Then look at the source code of the Assetmanager parameter in the resource construction method

 /** * Creat     e a new assetmanager containing only the basic system assets. * Applications won't generally use this method, instead retrieving the * appropriate asset manager with {@link Reso    Urces#getassets}.     Not for * use by applications. * {@hide} */public assetmanager () {synchronized (this) {if (debug_refs) {mNumRefs = 0; increfslocked (this.hashcode ());} init (false); if (LOCALLOGV) log.v (TAG, " New asset manager: "+  this); Ensuresystemassets (); } }

Note {@hide} in the comment, hidden, the Android system does not let us use. Since we are not going to use it directly, we can get assetmanager in a reflective way. Next I post the custom implementation class and give you an example:

/** *  *@ClassName: Mypluginresources *@Description: Custom plug-in resource file Get tool class *@author Http://blog.csdn.net/mynameishuangshuai the monkeys moved in.@version */PublicClassMypluginresourcesExtendsresources{PublicMypluginresources (Assetmanager assets, displaymetrics Metrics, Configuration config) {Super (assets, Metrics, config); }/** * Resource method to customize the return plugin's resource file *@param resources *@param assets *@return */PublicStatic mypluginresourcesGetpluginresources (Resources Resources,assetmanager assets) {Mypluginresources pluginresources =New Mypluginresources (Assets, Resources.getdisplaymetrics (), resources.getconfiguration ());return pluginresources; }Define your own add-ons apk assetsmanagerPublicStatic AssetmanagerGetpluginassetsmanager (File apkfile,resources Resources)throws classnotfoundexception{ //Because the system does not provide an instantiation method for Assetmanager, we use reflection class<?> forname = Class.forName ( "Android.content.res.AssetManager"); method[] Declaredmethods = Forname.getdeclaredmethods (); For (method:d eclaredmethods) { if (Method.getname (). Equals ("Addassetpath")) { try {Assetmanager Assetmanager = AssetManager.class.newInstance (); //Call the Addassetpath method, parameters for our plugin apk path Method.invoke (Assetmanager, Apkfile.getabsolutepath ()); return Assetmanager;} catch (Exception e) {e.printstacktrace ();}} } return null;}}           

In this way, we can use our custom Assetmanager in our project to get the resource files from the plugin apk.

AssetManager assetManager = PluginResources.getPluginAssetsManager(apkFile,            this.getResources());

Reference: "Android Development Art exploration"

Detailed Android Plugin Development-resource access

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.