2011-09-20 21 views
2

我正在考虑从其他APK加载资源的某种方式。例如,我在SD卡上安装了一个APK(它只是APK,它不安装在手机内存中),其中包含我想要在我安装的应用程序中使用的资源。是否可以从储存在SD卡上的APK的res/x资源加载到我安装的应用程序(例如布局,图像,字符串...)。从其他APK的动态资源加载

感谢

+0

看起来没有人能够解决它。我个人认为这是不可能的,但你永远不知道...... – Waypoint

+0

可能是可能的,但不会工作:反正ID会冲突。 – m0skit0

回答

0

在阅读并调试一些AOSP代码后,我终于设法动态加载资源。一步一步的指导:

  1. 创建一个新的Android项目,我把它叫做“动态APK加载”,指定whatever.dynamicapkloading应用ID,和左“应用”模块名称。

  2. 创建另一个没有任何活动的Android应用程序模块。我在同一个项目中完成了这项工作,但这取决于你。我调用了模块'dynamic',并将应用程序ID设置为whatever.dynamic

  3. 从'动态'项目中删除所有不必要的东西。我已经将任何其他资源一起删除了启动画drawable,并从build.gradle中删除了AppCompat depencency。我的清单内容看起来很简约,如下所示:<application />

  4. 将一些代码添加到'dynamic'项目。例如:

    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(); 
        } 
    } 
    
  5. 将一些资源添加到'动态'项目。我创建strings.xml

    <resources> 
        <string name="someString">This is a dynamically loaded string.</string> 
        <string name="anotherString">Lol! That\'s it.</string> 
    </resources> 
    
  6. 将它添加到运行配置。将启动选项/启动设置为Nothing。构建 - >构建APK!在这一步我有4.9 KB的文件叫做dynamic-debug.apk

  7. 将此文件dynamic/build/outputs/apk/dynamic-debug.apk移到我们的主项目资产中,即i。即到app/src/main/assets/

  8. 创建/打开一个类,它将加载代码&资源。说,这将是DynamicActivity。

  9. 编写将代码从资产复制到私人应用程序目录的代码。这是无聊琐碎&,你知道:

    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); 
        } 
    } 
    
  10. 写一段代码,这将加载必要的类并创建实例。

    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); 
    
  11. 编写将从指定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); 
    
  12. 使用这些资源!有两种获取资源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并在配置发生更改时重新查询资源值。

有用的链接: