Android多语言资源热更新方案AssetsManager

痛点分析

App的国家支持多,端侧在打包时多语言资源已经确定,而多语言配置变更频繁,准确性要求高,运营同学配置容易出错,特别是大促期间,版本发布冻结,为了能够及时修复线上的多语言问题,需要有动态更新多语言的能力。

简单来说,就是 修改多语言 String.xml中string文案,每次都需要发版本,这样效率不高。
<string name="my_gift_card_search_sort_prompt">Search By</string>
在这里插入图片描述

方案实现

实现原理:

通过AssetManager 异步载入资源包,构建新的Resource对象,并设置给全局单例对象GlobalResourceManager。
资源获取时调用 单例GlobalResourceManager.getString(resId);

步骤:
1.构建Patch资源apk
新建独立项目:新建一个独立Android App 应用。
配置修复资源:在对应 src/hotfix_resource/res/values 下面配置同名已经修复的string资源,
构建资源包:通过 ./gradlew assembleRelease 构建生成对应apk,最终生成后重命名为.patch。
资源包只包含需要修改的 string资源。

2.App下载资源包
可以利用App现有的能力,下载资源包到本地。(可以通过服务端提供资源下载地址;或者App自有的获取Orange配置等方式下载)

3.加载资源包
下载完成之后,需要完成有效性校验。 通过 AssetsManager 异步载入资源包,构建新的Resource对象,并设置给全局单例对象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.获取资源方式
GlobalResourceManager.getString(resId);

可以进行封装,如 kotlin扩展函数:

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
    }

猜你喜欢

转载自blog.csdn.net/adayabetter/article/details/127279050
今日推荐