Android-使用DexClassLoader加载apk文件

时间:2023-01-18 10:14:21

I've hit a bit of a wall. Any help would be appreciated. I have an app that I want to use DexClassLoader to load another apk file.

我打了一堵墙。任何帮助,将不胜感激。我有一个应用程序,我想使用DexClassLoader加载另一个apk文件。

Here is my code:

这是我的代码:

DexClassLoader dLoader = new DexClassLoader("/sdcard/download/test.apk","/sdcard/download",null,ClassLoader.getSystemClassLoader().getParent());
Class calledClass = dLoader.loadClass("com.test.classname");
Intent it=new Intent(this, calledClass);
it.setClassName("com.test", "com.test.classname");
startActivity(it);

Now I had already installed test.apk so when I ran the above code it worked fine and launched the application. However I want to be able to run this without test.apk being installed already (as that would defeat the entire point of the application) . So I uninstalled it and when I ran the my app again I get this error:

现在我已经安装了test.apk,所以当我运行上面的代码时,它工作正常并启动了应用程序。但是我希望能够在没有test.apk的情况下运行它(因为这会破坏应用程序的整个点)。所以我卸载它,当我再次运行我的应用程序时,我收到此错误:

android.content.ActivityNotFoundException: Unable to find explicit
activity class {com.test/com.test.classname}; have you declared this
activity in your AndroidManifest.xml.

So I'm a bit stumped here. This activity is declared in the Manifest of the apk I am trying to run. I can't declare it in my applications Manifest. Any ideas?

所以我在这里有点难过。此活动是在我试图运行的apk的Manifest中声明的。我无法在我的应用程序清单中声明它。有任何想法吗?

Thanks, Craig

谢谢,克雷格

3 个解决方案

#1


10  

Try using Android's PathClassLoader:

尝试使用Android的PathClassLoader:

    String packagePath = "com.mypackage";
    String classPath = "com.mypackage.ExternalClass";

    String apkName = null;
    try {
        apkName = getPackageManager().getApplicationInfo(packagePath,0).sourceDir;
    } catch (PackageManager.NameNotFoundException e) {
        // catch this
    }

    // add path to apk that contains classes you wish to load
    String extraApkPath = apkName + ":/path/to/extraLib.apk" 

    PathClassLoader pathClassLoader = new dalvik.system.PathClassLoader(
            apkName,
            ClassLoader.getSystemClassLoader());

    try {
        Class<?> handler = Class.forName(classPath, true, pathClassLoader);
    } catch (ClassNotFoundException e) {
        // catch this
    }

#2


7  

Although the question is old, I will answer because I struggled a bit to find a clear answer for your same question for myself. First, I would like to highlight that a clear requirement in your question is to load a class from an .apk that is not already installed on the device. Therefore, calling the package manager using getPackageManager() and providing it with the package path will clearly lead to NameNotFoundException because the .apk that has the package is not installed on the device.

虽然这个问题很老,但我会回答,因为我为自己找到了同样问题的明确答案。首先,我想强调一下,您的问题中明确要求是从设备上尚未安装的.apk加载类。因此,使用getPackageManager()调用包管理器并为其提供包路径将明显导致NameNotFoundException,因为设备上未安装包含该包的.apk。

So, the way to go about loading classes from an .apk file that is not installed on the device (i.e. you only have the .apk stored in a directory on your SDCARD) is by using DexClassLoader as follows:

因此,从未安装在设备上的.apk文件加载类的方法(即,只有.apk存储在SDCARD的目录中)是使用DexClassLoader,如下所示:

1- Make sure you have the .apk file in a directory on your SDCARD. I've mine Offloadme.apk in the Download folder on my SDCARD.

1-确保SD卡上的目录中有.apk文件。我在SDCARD的Download文件夹中使用了Offloadme.apk。

2- Add read permission in your AndroidManifest.xml to allow your app to read from the manifest.

2-在AndroidManifest.xml中添加读取权限,以允许您的应用从清单中读取。

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

3- Use the following definitions to define the path of the .apk, the class name inside the apk, and method name in that class that you would like to invoke:

3-使用以下定义来定义.apk的路径,apk中的类名以及要调用的该类中的方法名称:

final String apkFile =Environment.getExternalStorageDirectory().getAbsolutePath()+"/Download/Offloadme.apk";
String className = "com.khaledalanezi.offloadme.SimpleCalculator";
String methodToInvoke = "add"; 

4- Use the DexClassLoader to load the .apk and call the add method in the SimpleCalculator class using reflection as follows:

4-使用DexClassLoader加载.apk并使用反射调用SimpleCalculator类中的add方法,如下所示:

    final File optimizedDexOutputPath = getDir("outdex", 0);

    DexClassLoader dLoader = new DexClassLoader(apkFile,optimizedDexOutputPath.getAbsolutePath(),
            null,ClassLoader.getSystemClassLoader().getParent());

    try {
        Class<?> loadedClass = dLoader.loadClass(className);
        Object obj = (Object)loadedClass.newInstance();
        int x =5;
        int y=6;
        Method m = loadedClass.getMethod(methodToInvoke, int.class, int.class);
        int z = (Integer) m.invoke(obj, y, x);              
        System.out.println("The sum of "+x+" and "+"y="+z);

    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Note that in my simple example, I added two numbers using the add method available in the SimpleCalculator class loaded from the Offloadme.apk file stored on my SDCARD and I was able to print the correct answer which is 11.

请注意,在我的简单示例中,我使用从存储在SDCARD上的Offloadme.apk文件加载的SimpleCalculator类中可用的add方法添加了两个数字,并且我能够打印正确的答案,即11。

#3


-6  

You can't do that. Even if you're able to access classes from external file, Android still does not know them. And you don't run activities directly, but by requesting Android to run them, so they have to be registered/installed into system.

你不能这样做。即使您能够从外部文件访问类,Android仍然不知道它们。并且您不直接运行活动,而是通过请求Android运行它们,因此必须将它们注册/安装到系统中。

#1


10  

Try using Android's PathClassLoader:

尝试使用Android的PathClassLoader:

    String packagePath = "com.mypackage";
    String classPath = "com.mypackage.ExternalClass";

    String apkName = null;
    try {
        apkName = getPackageManager().getApplicationInfo(packagePath,0).sourceDir;
    } catch (PackageManager.NameNotFoundException e) {
        // catch this
    }

    // add path to apk that contains classes you wish to load
    String extraApkPath = apkName + ":/path/to/extraLib.apk" 

    PathClassLoader pathClassLoader = new dalvik.system.PathClassLoader(
            apkName,
            ClassLoader.getSystemClassLoader());

    try {
        Class<?> handler = Class.forName(classPath, true, pathClassLoader);
    } catch (ClassNotFoundException e) {
        // catch this
    }

#2


7  

Although the question is old, I will answer because I struggled a bit to find a clear answer for your same question for myself. First, I would like to highlight that a clear requirement in your question is to load a class from an .apk that is not already installed on the device. Therefore, calling the package manager using getPackageManager() and providing it with the package path will clearly lead to NameNotFoundException because the .apk that has the package is not installed on the device.

虽然这个问题很老,但我会回答,因为我为自己找到了同样问题的明确答案。首先,我想强调一下,您的问题中明确要求是从设备上尚未安装的.apk加载类。因此,使用getPackageManager()调用包管理器并为其提供包路径将明显导致NameNotFoundException,因为设备上未安装包含该包的.apk。

So, the way to go about loading classes from an .apk file that is not installed on the device (i.e. you only have the .apk stored in a directory on your SDCARD) is by using DexClassLoader as follows:

因此,从未安装在设备上的.apk文件加载类的方法(即,只有.apk存储在SDCARD的目录中)是使用DexClassLoader,如下所示:

1- Make sure you have the .apk file in a directory on your SDCARD. I've mine Offloadme.apk in the Download folder on my SDCARD.

1-确保SD卡上的目录中有.apk文件。我在SDCARD的Download文件夹中使用了Offloadme.apk。

2- Add read permission in your AndroidManifest.xml to allow your app to read from the manifest.

2-在AndroidManifest.xml中添加读取权限,以允许您的应用从清单中读取。

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

3- Use the following definitions to define the path of the .apk, the class name inside the apk, and method name in that class that you would like to invoke:

3-使用以下定义来定义.apk的路径,apk中的类名以及要调用的该类中的方法名称:

final String apkFile =Environment.getExternalStorageDirectory().getAbsolutePath()+"/Download/Offloadme.apk";
String className = "com.khaledalanezi.offloadme.SimpleCalculator";
String methodToInvoke = "add"; 

4- Use the DexClassLoader to load the .apk and call the add method in the SimpleCalculator class using reflection as follows:

4-使用DexClassLoader加载.apk并使用反射调用SimpleCalculator类中的add方法,如下所示:

    final File optimizedDexOutputPath = getDir("outdex", 0);

    DexClassLoader dLoader = new DexClassLoader(apkFile,optimizedDexOutputPath.getAbsolutePath(),
            null,ClassLoader.getSystemClassLoader().getParent());

    try {
        Class<?> loadedClass = dLoader.loadClass(className);
        Object obj = (Object)loadedClass.newInstance();
        int x =5;
        int y=6;
        Method m = loadedClass.getMethod(methodToInvoke, int.class, int.class);
        int z = (Integer) m.invoke(obj, y, x);              
        System.out.println("The sum of "+x+" and "+"y="+z);

    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Note that in my simple example, I added two numbers using the add method available in the SimpleCalculator class loaded from the Offloadme.apk file stored on my SDCARD and I was able to print the correct answer which is 11.

请注意,在我的简单示例中,我使用从存储在SDCARD上的Offloadme.apk文件加载的SimpleCalculator类中可用的add方法添加了两个数字,并且我能够打印正确的答案,即11。

#3


-6  

You can't do that. Even if you're able to access classes from external file, Android still does not know them. And you don't run activities directly, but by requesting Android to run them, so they have to be registered/installed into system.

你不能这样做。即使您能够从外部文件访问类,Android仍然不知道它们。并且您不直接运行活动,而是通过请求Android运行它们,因此必须将它们注册/安装到系统中。