我正在考虑从其他APK加载资源的某种方式。例如,我在SD卡上有一个APK(它只是APK,它没有安装在手机内存中),它包含我想在我安装的应用程序中使用的资源。是否可以将res / x中的资源从存储在SD卡上的APK加载到我安装的应用程序(例如布局,图像,字符串......)。
由于
答案 0 :(得分:2)
您可以使用PackageManager.getResourcesForApplication访问其他apk中的资源。
答案 1 :(得分:2)
在阅读并调试了一些AOSP代码之后,我终于成功地动态加载了资源。分步指南:
创建一个新的Android项目,我将其称为“动态APK正在加载”,指定为whatever.dynamicapkloading
应用ID,并保留“app”模块名称。
创建另一个没有任何活动的Android应用程序模块。我在同一个项目中完成了这个,但这取决于你。我将模块称为“动态”,并将应用程序ID设置为whatever.dynamic
。
从“动态”项目中删除所有不必要的内容。我已经删除了启动器drawable和任何其他资源,并从build.gradle中删除了AppCompat依赖性。我的清单内容看起来很简约,如下所示:<application />
。
为“动态”项目添加一些代码。例如:
package whatever.dynamic;
import android.content.Context;
import android.widget.Toast;
public final class Code implements Runnable {
private final Context context;
public Code(Context context) {
this.context = context;
}
@Override
public void run() {
Toast.makeText(context, "Running dynamic code!",
Toast.LENGTH_SHORT).show();
}
}
为“动态”项目添加一些资源。我创建了strings.xml
:
<resources>
<string name="someString">This is a dynamically loaded string.</string>
<string name="anotherString">Lol! That\'s it.</string>
</resources>
将其添加到“运行配置”。设置启动选项/启动到无。构建 - &gt;建立APK!在这一步中,我有4.9 KB文件名为dynamic-debug.apk
。
将此文件dynamic/build/outputs/apk/dynamic-debug.apk
移动到我们主要项目的资产中,i。即到app/src/main/assets/
。
创建/打开一个将加载代码和类的类。资源。比方说,这将是DynamicActivity。
编写将APK从资产复制到私有app目录的代码。这很乏味&amp;琐碎,你知道:
File targetApk = new File(getDir("dex", Context.MODE_PRIVATE), "app.apk");
// copy APK from assets to private directory
// remove this condition in order to keep dynamic APK fresh
if (!targetApk.exists() || !targetApk.isFile()) {
try (BufferedInputStream bis = new BufferedInputStream(
getAssets().open("dynamic-debug.apk"));
OutputStream dexWriter = new BufferedOutputStream(
new FileOutputStream(targetApk))) {
byte[] buf = new byte[4096];
int len;
while((len = bis.read(buf, 0, 4096)) > 0) {
dexWriter.write(buf, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
编写将加载必要类并实例化它的代码。
PathClassLoader loader = new PathClassLoader(
targetApk.getAbsolutePath(), getClassLoader());
Class<?> dynamicClass = loader.loadClass("whatever.dynamic.Code");
Constructor<?> ctor = dynamicClass.getConstructor(Context.class);
dynamicInstance = (Runnable) ctor.newInstance(this);
编写将从指定的APK加载资源的代码。这是一个艰难的!所有构造函数和方法都是公共的,但它们是隐藏的。我已经以反思的方式编写了它,但为了避免这种情况,您可以针对完整的框架jar编译代码,也可以在Smali中编写部分代码。
AssetManager assets = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class
.getMethod("addAssetPath", String.class);
if (addAssetPath.invoke(assets, targetApk.getAbsolutePath()) ==
Integer.valueOf(0)) {
throw new RuntimeException();
}
Class<?> resourcesImpl = Class.forName("android.content.res.ResourcesImpl");
Class<?> daj = Class.forName("android.view.DisplayAdjustments");
Object impl = resourcesImpl
.getConstructor(AssetManager.class, DisplayMetrics.class,
Configuration.class, daj)
.newInstance(assets, getResources().getDisplayMetrics(),
getResources().getConfiguration(), daj.newInstance());
dynamicResources = Resources.class.getConstructor(ClassLoader.class)
.newInstance(loader);
Method setImpl = Resources.class.getMethod("setImpl",
Class.forName("android.content.res.ResourcesImpl"));
setImpl.invoke(dynamicResources, impl);
使用这些资源!有两种获取资源ID的方法。
int someStringId = dynamicResources.getIdentifier(
"someString", "string", "whatever.dynamic");
String someString = dynamicResources.getString(someStringId);
Class<?> rString = Class.forName("whatever.dynamic.R$string", true, loader);
anotherStringId = rString.getField("anotherString").getInt(null);
String anotherString = dynamicResources.getString(anotherStringId);
就是这样!这对我有用。 Full DynamicActivity.java code
在实际项目中,您必须在构建时签署APK并在加载时检查其签名。当然,永远不要将它存储在SD卡上,否则你的APK可能会被欺骗!
您还应该静态缓存资源ID,但重新创建Resources
并在配置更改时重新查询资源值。
有用的链接: