安卓动态加载资源的实现方案

本文介绍了在Android应用中如何实现动态资源切换,包括两种方案:一是通过更改资源名称来切换,二是创建新的Resources对象。文章提供了具体的代码示例,展示了如何加载外部APK资源,实现多语言或主题切换,并提到了动态监听和刷新资源的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在项目中可能会遇到需要在应用内动态或静态添加皮肤或多国文字切换等资源类修改需求

准备知识:

安卓的资源打包会生成一个resources.arsc文件,将生成的apk拖到android studio可以看到里面的内容(如下图,包含了包名、资源类型type、资源id、资源名称、资源的值等信息):在这里插入图片描述

这些资源主要是由AssetManager这个类管理的,并且通过类Resources供外部使用的。

主要两个方案:
一、使用和当前应用相同的Resources,资源切换时通过切换资源名字实现
二、资源切换时,创建新的Resouces,并切换Resources,不改变资源名字实现

1、方案一

Resources不变,资源名字变
直接上代码:

//获取  <string name="hello_tw">繁体Hello World!</string> 并显示在textView上
String name1 = getResources().getResourceEntryName(R.string.hello);
String type1 =  getResources().getResourceTypeName(R.string.hello);
int strIde= getResources().getIdentifier(name1+"_tw" , type1 , getPackageName());
textView.setText(strIde);

该方案可以实现该资源切换(如这里的多国语言切换)功能,但是缺点也是很明显的,无法动态的添加,而且增加一种语言就要手动增加资源名称。

1、方案二

Resources变,资源名字不变
直接上代码:

				//这里的资源包可以放在一个apk里,如这里的skin1.apk,可以动态下载后加载,
				//也可以在打包应用apk时将资源apk放在assets文件夹下,然后第一次切换资源时copy到app的cache文件夹下面
				String path = getCacheDir()+"/skin1.apk";
                try {
                    InputStream inputStream = null;
                    FileOutputStream outputStream = null;
                    try {
                        File skinFile = new File(path);
                        if(skinFile.exists()){
                            skinFile.delete();
                        }
                        skinFile.createNewFile();
                        if(skinFile.exists()){
                            inputStream = getResources().getAssets().open("skin1.apk");
                            outputStream = new FileOutputStream(skinFile);
                            byte[] readBytes = new byte[1024];
                            int readcount = 0;
                            while ( (readcount = inputStream.read(readBytes)) > 0){
                                outputStream.write(readBytes , 0 , readcount);
                            }

                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally {
                        if(inputStream != null){
                            inputStream.close();
                        }
                        if(outputStream != null){
                            outputStream.close();
                        }
                    }
                    //通过反射实例化一个AssetManager 并添加资源文件路径
                    AssetManager assetManager = AssetManager.class.newInstance();
                    Method method = AssetManager.class.getMethod("addAssetPath" , String.class);
                    method.invoke(assetManager,path);
                    if(skinResources != null && skinResources.getAssets() != null){
                        skinResources.getAssets().close();
                    }
                    //实例化一个Resources供外部直接使用
                    //这里的DisplayMetrics和Configuration没有变化
                    skinResources = new Resources(assetManager , getResources().getDisplayMetrics() , getResources().getConfiguration());
                    PackageInfo packageInfo = getPackageManager().getPackageArchiveInfo(path , PackageManager.GET_ACTIVITIES);
                    pkgName = packageInfo.packageName;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //切换资源(比如切换主题、语言等)后,获取资源方式
                //获取新的资源包下 相同资源名称的文字
                String name1 = getResources().getResourceEntryName(R.string.hello);
                String type1 =  getResources().getResourceTypeName(R.string.hello);
                int resId = skinResources.getIdentifier(name1 , type1 , pkgName);
                String strText = skinResources.getString(resId);
                textView.setText(strText);
                //获取新的资源包下 相同资源名称的颜色
                ViewGroup viewGroup = (ViewGroup) v.getParent();
                String name = MainActivity.this.getResources().getResourceEntryName(R.color.my_black);
                String type =  MainActivity.this.getResources().getResourceTypeName(R.color.my_black);
                int resColor = skinResources.getIdentifier(name,type,pkgName);
                viewGroup.setBackgroundColor(resColor);

上面是两种实现方案的demo部分代码,如果有相关需求,可以参考使用这两种方案实现(这里的代码只是为了演示,并不考虑设计上的合理性哈)。

这里多说几句,实现相关需求,肯定是需要动态监听当前资源是否切换,切换后怎么刷新,这些可以考虑使用LayoutInflater的setFactory2或setFactory参考源码(mLayoutInflater.createView(name, prefix, attrs);)拦截View的创建,并且过滤需要随资源切换变化的属性,使用观察者模式,实现自动刷新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值