Android 如何在一个app中动态加载另一个app程序中的类

本文介绍如何在Android的一个应用中动态加载并调用另一个应用中的类。利用DexClassLoader和反射机制,从指定APK文件中加载目标类并执行其方法。

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

Android 如何在一个app程序中动态加载另一个app程序中的类

本人小白,初次写博客,欢迎大家指点 ~~

问题描述

假设在Android手机上有两个android应用,一个名为Host,另一个名为Client,现在有这样一个需求:在Client里面有一个类ClientPrint,需要在Host中调用这个ClientPrint类,并执行该类的内部方法,应该如何实现。

ClientPrint的具体实现如下:

public class ClientPrint {
    public void print() {
             Log.e("ClientPrint","hello world");
    }
}

相关的知识点:

1、利用Java的反射机制,动态加载ClientPrint

Dalvik虚拟机中有这样的一个类DexClassLoader,类似于JVM中的ClassLoader的作用,它的构造方法如下:

   / *
     * dexPath         *.dex文件的路径
     * dexOutputPath   解压出来的.dex文件存放的目录
     * libPath         C语言链接库的路径
     * classLoader     类加载器
     */
    public DexClassLoader(String dexPath,String dexOutputPath,String libPath,ClassLoader classLoader);

在此对上面的参数做一下说明:

我们知道,当一个程序被安装到手机上时,相应的.apk文件会被复制到手机分配的硬盘空间上,每当程序运行时,相应的 classes.dex文件和资源文件都会从这个apk中解压出来(apk文件其实也是一个zip包),然后被程序调用。

那么这些将要被解压的文件一般存放在手机哪个地方呢?

一般classes.dex文件存放在: /data/app/应用程序包名-1.apk
而被Java本地调用的C语言动态链接库则存放在:/data/app-lib/应用程序包名-1

所以上述参数的意义是这样的:dexPath代表将要被加载的类的路径,libPath代表程序需要调用C语言链接库的路径,dexOutputPath指明了解压出来的classes.dex文件存放的路径,classLoader就不做过多解释了,就是Java反射机制需要的类加载器。

现在万事俱备,可以着手加载ClientPrint类了,代码如下:

DexClassLoader dcl = new DexClassLoader(dexPath,dexOutputPath,libPath,this.getClass().getClassLoader());
try {
    //参数 packageName 是ClientPrint的包路径
    Class<?> clazz = dcl.loadClass(packageName + ".ClientPrint");
    Object obj = clazz.newInstance();
    Method method = clazz.getMethod("print",null);
    //成功调用方法
    method.invoke(obj,null);
} 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 (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (InvocationTargetException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

总结一下:从dexPath和libPath中分别解压出classes.dex和C语言动态链接库文件,并将解压结果存放到dexOutputPath,利用Java反射机制从中动态加载这些类。

2、相关路径的获取

为了得到目标程序的相关信息。我们可以在目标程序中新建一个空的Activity,并在清单文件中配置action节点属性,这样可以方便我们用Intent来获取Client程序的相关信息。

代码如下:

package com.example.client;

import android.app.Activity;
import android.os.Bundle;

public class EmptyActivity extends Activity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

}

AndroidManifest.xml配置

<activity
     android:name=".EmptyActivity"
     android:label="@string/app_name">
     <intent-filter>
         <action android:name="com.example.client.emptyactivity"/>
     </intent-filter>
</activity>

这样在Host程序中就可以通过Intent来获取Client的信息

Intent it = new Intent("com.example.client.emptyactivity",null);
PackageManager pm = getPackageManager();
List<ResolveInfo> queryIntentActivities = pm.queryIntentActivities(it,0);
ResolveInfo rinfo = queryIntentActivities.get(0);
ActivityInfo ainfo = rinfo.activityInfo;
//注意,在此处获取路径
String dexPath = ainfo.applicationInfo.sourceDir;
String dexOutputPath = getApplicationInfo().dataDir;
String libPath = ainfo.applicationInfo.nativeLibraryDir;

全部源码

首先是Client部分

EmptyActivity.java

package com.example.client;

import android.app.Activity;
import android.os.Bundle;

public class EmptyActivity extends Activity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

}

AndroidManifest.xml节点属性配置

<activity
            android:name=".EmptyActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="com.example.client.emptyactivity"/>
            </intent-filter>
        </activity>

ClientPrint类

package com.example.client;

import android.util.Log;

public class ClientPrint{

    @Override
    public void print() {
        // TODO Auto-generated method stub
        Log.e("ClientPrint","hello world");
    }

}

Host部分

package com.example.host;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import dalvik.system.DexClassLoader;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

@SuppressLint("NewApi") public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

/*
* 当屏幕上的Button被点击后,会触发这个方法
*/
    public void press(View view) {
        Intent it = new Intent("com.example.client.emptyactivity",null);
        PackageManager pm = getPackageManager();
        List<ResolveInfo> queryIntentActivities = pm.queryIntentActivities(it,0);
        ResolveInfo rinfo = queryIntentActivities.get(0);
        ActivityInfo ainfo = rinfo.activityInfo;

        String packageName = ainfo.packageName;
        String dexPath = ainfo.applicationInfo.sourceDir;
        String dexOutputPath = getApplicationInfo().dataDir;
        String libPath = ainfo.applicationInfo.nativeLibraryDir;
        DexClassLoader dcl = new DexClassLoader(dexPath,dexOutputPath,libPath,this.getClass().getClassLoader());
        try {
            Class<?> clazz = dcl.loadClass(packageName + ".ClientPrint");


        Obj obj = clazz.newInstance();
        Method method = clazz.getMethod("print",null);
        method.invoke(obj,null);

        } 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 (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }
}

同时安装Host.apk和Client.apk,然后运行Host,会发现控制台上输出”Hello world”.


后记

可以在Host和Client同时定义一个接口,让ClientPrint继承它,这样在Host之中调用会方便些。在具体实现时,可以在Host中定义接口,然后再Client中导入这个接口的jar包。但是一定要注意,要以Library形式导入这个jar包,否则会因为Host和Client程序中有同名的接口而冲突。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值