通过类加载器从 dex 文件中访问 app 的类

Posted

技术标签:

【中文标题】通过类加载器从 dex 文件中访问 app 的类【英文标题】:accessing to classes of app from dex file by classloader 【发布时间】:2013-12-02 19:10:12 【问题描述】:

我有一个应用程序从服务器接收 dex 文件,然后将其保存在 sdcard 上并加载它以供

做一些功能。我正在从我的 dex 文件中加载我的应用程序的类,如下所示

dex文件

public class Main  


    public void onCreate() 
    
        System.out.print("------------onCreate------------");
        try 
             final ClassLoader classloader = ClassLoader.getSystemClassLoader();
            final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("com.example.myapp.M");
            final Object myInstance  = classToLoad.newInstance();
            final Method doSomething = classToLoad.getMethod("doSomething");
            doSomething.invoke(myInstance);

         catch (Exception e) 
            e.printStackTrace();
        
    


应用代码

public class MainActivity extends Activity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        try 
            final String libPath = Environment.getExternalStorageDirectory() + "/Lib/lib.apk";
            final File tmpDir = getDir("dex", 0);
            final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());
            final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("com.example.lib.Main");
            final Object myInstance  = classToLoad.newInstance();
            final Method doSomething = classToLoad.getMethod("onCreate");
            doSomething.invoke(myInstance);
             catch (Exception e) 
            e.printStackTrace();
        
    

我的应用程序中的 M 类

public class M 
public void doSomething()

    Log.e("doSomething", "--------------------doSomething----");


但它让我跟随错误。我的代码有什么问题

11-19 08:24:12.718: W/System.err(10867): java.lang.ClassNotFoundException: Didn't find class "com.example.myapp2.M" on path: .
11-19 08:24:12.738: W/System.err(10867):    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:65)
11-19 08:24:12.747: W/System.err(10867):    at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
11-19 08:24:12.758: W/System.err(10867):    at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
11-19 08:24:12.758: W/System.err(10867):    at com.example.lib.Main.onCreate(Main.java:15)
11-19 08:24:12.768: W/System.err(10867):    at java.lang.reflect.Method.invokeNative(Native Method)
11-19 08:24:12.788: W/System.err(10867):    at java.lang.reflect.Method.invoke(Method.java:511)
11-19 08:24:12.788: W/System.err(10867):    at com.example.myapp2.MainActivity.onCreate(MainActivity.java:28)
11-19 08:24:12.798: W/System.err(10867):    at android.app.Activity.performCreate(Activity.java:5104)
11-19 08:24:12.818: W/System.err(10867):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
11-19 08:24:12.828: W/System.err(10867):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
11-19 08:24:12.828: W/System.err(10867):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
11-19 08:24:12.828: W/System.err(10867):    at android.app.ActivityThread.access$600(ActivityThread.java:141)
11-19 08:24:12.828: W/System.err(10867):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
11-19 08:24:12.828: W/System.err(10867):    at android.os.Handler.dispatchMessage(Handler.java:99)
11-19 08:24:12.838: W/System.err(10867):    at android.os.Looper.loop(Looper.java:137)
11-19 08:24:12.847: W/System.err(10867):    at android.app.ActivityThread.main(ActivityThread.java:5041)
11-19 08:24:12.847: W/System.err(10867):    at java.lang.reflect.Method.invokeNative(Native Method)
11-19 08:24:12.847: W/System.err(10867):    at java.lang.reflect.Method.invoke(Method.java:511)
11-19 08:24:12.857: W/System.err(10867):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
11-19 08:24:12.898: W/System.err(10867):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
11-19 08:24:12.898: W/System.err(10867):    at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

消息ClassNotFoundException: Didn't find class on path .中对您来说有什么歧义 "path"。我可以通过类加载器从 dex 文件访问我的应用程序的类。如果可以,如何指定访问路径? 【参考方案1】:

看看您是否遵循这些步骤。

    SD 卡上的库文件是 APK 还是包含 (Classes.dex) 文件的 JAR? 将 APK 文件复制到应用程序目录 /data/data/your.app.name/tmp 的临时文件夹中。这一点很重要,因为出于安全原因,Android 不允许您从 SD 卡中加载内容。 按照此处所述使用DexClassLoader -> How to load a Java class dynamically on android/dalvik? 使用这个类加载器实例来加载任何动态类文件。

如果您有超过 1 个 JAR 文件,请将它们合并到一个 DEX 文件/APK 中。

要将您的 Java JAR 文件或类转换为 DEX,您需要使用 Android SDK 的 dx.bat。这是一个要执行的示例 ANT 脚本。这也可以通过命令行使用类似的参数来完成。

<exec executable="dx.bat">
    <arg value="--dex" />
    <arg value="--output=$target.directory.fullpath/$apk.name" />
    <arg value="--positions=lines" />
    <arg path="$classes.directory.full.path" />
</exec>

【讨论】:

我可以从我的 jar 文件中访问我的应用程序的类吗?你能解释一下我该怎么做吗? 我想从我的 jar 文件的类中调用我的应用程序的方法 是的,您的插件 JAR(DEX 格式)文件可以调用应用程序的方法而无需显式执行任何操作,因为它必须充当父类加载器。 查看 DexClassLoader 的构造函数,您将父类加载器设置为输入之一。 DexClassLoader。您可以使用 activity.getClassLoader() 方法获取父类加载器以传递给DexClassLoader() 构造函数 我在 jar 文件中使用了上面的代码,我又得到了那个错误。什么是错误路径?【参考方案2】:

我找到了我需要更改的答案

final Class<Object> classToLoad = (Class<Object>)classloader.loadClass("com.example.myapp.M");

final Class<Object> classToLoad = (Class<Object>)Class.forName("com.example.myapp.M");

在我的 dex 文件中

【讨论】:

它对你的工作方式很奇怪。你没有在任何地方提到你的 .jar 文件路径...... 那么,如果你这样做,它是否会加载 com.example.myapp.M 需要的所有其他类(尚未加载)? Zohreh 您是否从 SD 卡加载类文件?或者其他一些外部存储目录?我很好奇,因为@Swaroop 说你无法从 SD 卡加载它们。【参考方案3】:

@zohreh 替换下面的行

final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());

final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());

【讨论】:

【参考方案4】:

列出的答案对我不起作用。但是,通过Thread.currentThread().getContextClassLoader() 获取ClassLoader 原来是有效的:

final Class<Object> classToLoad = (Class<Object>) Class.forName("com.example.myapp.M", true, Thread.currentThread().getContextClassLoader());

【讨论】:

以上是关于通过类加载器从 dex 文件中访问 app 的类的主要内容,如果未能解决你的问题,请参考以下文章

是否可以通过使用Java Reflection从注入的DEX中获取app的类对象?

java类加载器?

Java类加载器ClassLoader总结

Android 逆向类加载器 ClassLoader ( 加载 Android 组件的类加载器 | 双亲委派机制实例分析 )

热修复之类加载机制总结

Java的类加载机制