Android multilingual resource hot update program AssetsManager

pain point analysis

The app supports many countries, and the multilingual resources have been determined when the terminal side is packaged. However, the multilingual configuration changes frequently, and the accuracy requirements are high. Operation students are prone to mistakes in configuration, especially during the promotion period. The version release is frozen. For multilingual issues on the Internet, it is necessary to have the ability to dynamically update multiple languages.

To put it simply, it is to modify the string copy in the multilingual String.xml, and the version needs to be released every time, which is not efficient.
<string name="my_gift_card_search_sort_prompt">Search By</string>
insert image description here

Program realization

Implementation principle:

Asynchronously load the resource bundle through AssetManager, build a new Resource object, and set it to the global singleton object GlobalResourceManager.
Call the singleton GlobalResourceManager.getString(resId) when the resource is obtained;

Steps:
1. Build the Patch resource apk
and create a new independent project: Create a new independent Android App application.
Configure repair resources: configure the repaired string resource with the same name under the corresponding src/hotfix_resource/res/values,
build resource package: generate corresponding apk through ./gradlew assembleRelease build, and finally rename it to .patch after generation.
The resource bundle only contains string resources that need to be modified.

2.App download resource package
You can use the existing capabilities of the App to download the resource package locally. (The resource download address can be provided through the server; or the app can download the Orange configuration by itself)

3. Load the resource pack
After the download is complete, the validity check needs to be completed. Asynchronously load resource bundles through AssetsManager, build a new Resource object, and set it to the global singleton object GlobalResourceManager.

public Resources loadHotPatchResource() {
    
    
	PackageManager pm = context.getPackageManager();
	PackageInfo mInfo = pm.getPackageArchiveInfo(hotfixResFilePath, PackageManager.GET_ACTIVITIES);
    
	AssetManager assetManager = AssetManager.class.newInstance();
	Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
	addAssetPath.invoke(assetManager, hotfixResFilePath);

	Resources originRes = mResourcesManager.getDefaultResources();
	return new Resources(assetManager, originRes.getDisplayMetrics(), originRes.getConfiguration());
}

4. Obtain resource method
GlobalResourceManager.getString(resId);

Can be encapsulated, such as kotlin extension function:

fun Context.getResourceString(@StringRes resId: Int):String {
    
    
    return GlobalResourceManager.getString(this, resId)
}

fun Activity.getResourceString(@StringRes resId: Int):String {
    
    
    return GlobalResourceManager.getString(this, resId)
}

fun Fragment.getResourceString(@StringRes resId: Int):String {
    
    
    return GlobalResourceManager.getString(this, resId)
}

fun View.getResourceString(@StringRes resId: Int):String {
    
    
    return GlobalResourceManager.getString(context, resId)
}

fun RecyclerView.ViewHolder.getResourceString(@StringRes resId: Int):String {
    
    
    return GlobalResourceManager.getString(itemView.context, resId)
}


GlobalResouceManager

private fun getString(resString: String?, @StringRes resId: Int):String {
    
    
        var string = resString ?: "";
        try {
    
    
            if (ResourcePatchLoader.getPatchResource() != null && ResourcePatchLoader.getPatchResourcePackageName() != null) {
    
    
                val resName = ApplicationContext.getContext().resources.getResourceEntryName(resId)
                val patchResId = ResourcePatchLoader.getPatchResource()?.getIdentifier(
                        resName, "string", ResourcePatchLoader.getPatchResourcePackageName()) ?: 0

                if (patchResId == 0) {
    
    
                    return string
                }

                string = ResourcePatchLoader.getPatchResource()?.getString(patchResId) ?: ""
            }
        } catch (e: Throwable) {
    
    
            if (ResourcePatchDebug.isDebug()) {
    
    
                throw Resources.NotFoundException("String resource ID #0x" + Integer.toHexString(resId))
            } else {
    
    
                Logger.d(TAG_RESOURCE_HOT_PATCH, "GlobalResourceManager getString fail: " + e.message)
                PatchTrackUtil.onFail(POINT_GET_RESOURCE_STRING, 100, e.message)
            }
        }
        return string
    }

Guess you like

Origin blog.csdn.net/adayabetter/article/details/127279050