跨进程通信方式
Android是在Linux内核基础之上运行,因此Linux中存在的IPC机制在Android中基本都能使用,如:
- 管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
- 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
- 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
- 套接字:作为更通用的接口,传输效率低;
除了上面 Linux 的 IPC 机制外,Android 还提供了 Messenger、AIDL、ContentProvider、MemoryFile、SharedMemory 这几种 IPC 方式。其中 Messenger、AIDL、ContentProvider 内部都是由 Binder 实现,而 MemoryFile、SharedMemory 则是共享内存的方式实现的。下面分别介绍如何通过它们来实现跨进程通信。
Messenger
Messenger可以翻译为信使,顾名思义,通过它可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。Messenger的使用如下:
- 客户端
kotlin
class MessengerActivity : AppCompatActivity() {
companion object {
private const val TAG = "Messenger"
}
// 用于与服务通信的Messenger对象,通过它向服务发送消息
private var serviceMessenger: Messenger? = null
// 客户端的Messenger对象,用于接收服务返回的消息
// 内部关联了ClientHandler,处理从服务端收到的消息
private val clientMessenger = Messenger(ClientHandler())
// 客户端的消息处理器,继承自Handler
private class ClientHandler : Handler(Looper.getMainLooper()) {
// 处理接收到的消息
override fun handleMessage(msg: Message) {
// 根据消息的what字段判断消息类型
when (msg.what) {
// 处理服务端的回复消息(类型2)
2 -> Log.d("Messenger", "客户端收到回复:${msg.data.getString("reply")}")
// 处理服务端主动推送的消息(类型3)
3 -> Log.d("Messenger", "服务端主动推送的消息")
// 可以添加更多消息类型的处理
else -> super.handleMessage(msg)
}
}
}
// 服务连接对象,用于监听与服务的连接状态
private val connection = object : ServiceConnection {
// 当服务连接成功时调用
override fun onServiceConnected(className: ComponentName, service: IBinder) {
// 通过服务返回的IBinder创建Messenger,用于和服务通信
serviceMessenger = Messenger(service)
// 连接成功后发送测试消息
sendMsg(-1)
sendMsg(0)
}
// 当服务意外断开连接时调用(正常关闭不会触发)
override fun onServiceDisconnected(className: ComponentName) {
// 清空服务端Messenger,避免空引用
serviceMessenger = null
}
}
// 向服务端发送消息的方法
// what参数用于标识消息类型
private fun sendMsg(what: Int) {
// 创建消息对象,指定消息类型what
val msg = Message.obtain(null, what).apply {
// 设置消息数据(Bundle)
data = Bundle().apply {
// 这里可以添加要发送的数据,键值对形式
// putString("msg", StringUtils.getOneMStringContent()) // 注释掉的是可能的大字符串测试
putString("msg", "客户端数据") // 实际发送的测试数据
}
// 设置回复的Messenger,服务端通过这个回复客户端
replyTo = clientMessenger
}
try {
// 通过服务端的Messenger发送消息
serviceMessenger?.send(msg)
} catch (e: RemoteException) {
// 捕获远程调用异常(如服务已断开)
e.printStackTrace()
}
}
// 活动创建时调用
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置布局文件
setContentView(R.layout.activity_messenger)
// 绑定服务:创建意图,指定要绑定的服务
Intent(this, MessengerService::class.java).also { intent ->
// 绑定服务,参数:意图、连接对象、绑定选项(自动创建服务)
bindService(intent, connection, BIND_AUTO_CREATE)
}
// 给按钮添加点击事件
findViewById<Button>(R.id.add_user).setOnClickListener {
// 点击时发送类型为1的消息
sendMsg(1)
}
}
// 活动销毁时调用
override fun onDestroy() {
super.onDestroy()
// 解除与服务的绑定,避免内存泄漏
unbindService(connection)
}
}
- 服务端
kotlin
class MessengerService : Service() {
companion object {
private const val TAG = "Messenger"
}
// 用于保存客户端的Messenger引用,以便服务端主动向客户端发送消息
private var clientMessenger: Messenger? = null
// 服务端的消息处理器,用于处理客户端发送的消息
private val handler = object : Handler(Looper.getMainLooper()) {
// 处理接收到的消息
override fun handleMessage(msg: Message) {
// 根据消息的what字段区分不同类型的消息
when (msg.what) {
// 消息类型-1:绑定客户端的Messenger
-1 -> {
Log.d(TAG, "绑定客户端 Messenger ${msg.replyTo}")
// 保存客户端的Messenger,用于后续主动推送消息
clientMessenger = msg.replyTo
}
// 消息类型0:普通消息接收
0 -> {
// 从消息数据中获取客户端发送的内容
val msgStr = msg.data.getString("msg")
Log.d(TAG, "服务端收到:$msgStr")
}
// 消息类型1:需要回复的消息
1 -> {
// 获取客户端发送的内容
val msgStr = msg.data.getString("msg")
Log.d(TAG, "服务端收到:$msgStr")
// 获取客户端的Messenger,用于回复消息
val clientMessenger = msg.replyTo
// 创建回复消息,类型为2
val replyMsg = Message.obtain(null, 2).apply {
// 设置回复数据
data = Bundle().apply {
putString("reply", "已收到消息")
}
}
try {
// 向客户端发送回复
clientMessenger.send(replyMsg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
// 可以添加更多消息类型的处理逻辑
}
}
}
// 服务端的Messenger,关联上面定义的handler,用于接收客户端消息
private val serviceMessenger = Messenger(handler)
// 服务创建时调用
override fun onCreate() {
super.onCreate()
// 创建协程作用域,在IO线程中执行定时任务
CoroutineScope(Dispatchers.IO).launch {
// 循环执行,每秒向客户端主动推送一次消息
while (true) {
delay(1000) // 延迟1秒
try {
// 创建主动推送的消息,类型为3
val replyMsg = Message.obtain(null, 3).apply {
data = Bundle().apply {
putString("reply", "服务端主动推送的消息")
}
}
// 通过之前保存的clientMessenger向客户端发送消息
clientMessenger?.send(replyMsg)
} catch (e: RemoteException) {
// 捕获异常(如客户端已断开连接)
e.printStackTrace()
}
}
}
}
// 当客户端绑定服务时调用,返回服务端的IBinder
override fun onBind(intent: Intent): IBinder {
// 返回serviceMessenger的binder,客户端通过它与服务通信
return serviceMessenger.binder
}
}
从上面的代码可以发现,Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。
注意:Messenger 进行跨进程通信的大小限制为 1M
AIDL
在使用 AIDL 之前,我们需要在模块的 build.gradle
文件中声明配置,如下所示:
ini
android {
...
buildFeatures {
aidl = true
}
}
然后在 main 目录下创建 aidl
目录,在 aidl
目录中创建相应的 aidl 文件。如下所示:
- IUserManager.aidl
java
package com.example.ipc;
import com.example.ipc.User; // 显式导入
import com.example.ipc.IUserChangeCallback;
// 需要通过 Android studio 的 aidl 来生成 package 才行
interface IUserManager {
void addUser(in User user); // in:数据从客户端到服务端
List<User> getUserList();
void registerCallback(IUserChangeCallback callback);
void unregisterCallback(IUserChangeCallback callback);
}
- IUserChangeCallback.aidl
java
package com.example.ipc;
import com.example.ipc.User;
// Declare any non-default types here with import statements
interface IUserChangeCallback {
void changed(in User user);
}
- User.aidl
go
// User.aidl
package com.example.ipc;
parcelable User;
需要注意,创建的 User.aidl 必须要存在一个对应的数据类。这里的数据类是也叫 User。如下所示。
java
// 注意,包名要和 User.aidl 相同,否则会出现找不到的错误。
package com.example.ipc;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
public final class User implements Parcelable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
@NonNull
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 构造方法、describeContents、writeToParcel 等方法
}
aidl文件位置如下所示:

创建好 aidl 文件后,我们就可以像在同一进程调用方法一样来使用它们了。代码如下所示:
- 客户端
kotlin
class AidlActivity : AppCompatActivity() {
private var userManager: IUserManager? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
userManager = IUserManager.Stub.asInterface(service)
userManager?.registerCallback(object : IUserChangeCallback.Stub() {
override fun changed(user: User?) {
Log.d("AidlActivity", "客户端收到的用户信息: $user")
}
})
}
override fun onServiceDisconnected(className: ComponentName) {
userManager = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_aidl)
// 绑定服务
Intent(this, AidlService::class.java).also { intent ->
bindService(intent, connection, BIND_AUTO_CREATE)
}
findViewById<Button>(R.id.add_user).setOnClickListener(this::addUser)
}
fun addUser(view: View) {
try {
//userManager?.addUser(User(StringUtils.getOneMStringContent(), 25))
userManager?.addUser(User("客户端创建的用户", 25))
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection)
}
}
- 服务端
kotlin
class AidlService : Service() {
companion object {
private const val TAG = "AidlService"
}
// 线程安全的用户列表,用于存储用户数据
private val _userList = CopyOnWriteArrayList<User>()
// 远程回调列表,用于管理客户端注册的回调接口
// RemoteCallbackList专门用于跨进程回调管理
private val _callbackList = RemoteCallbackList<IUserChangeCallback>()
// 实现AIDL接口的Binder对象,客户端通过它调用服务端方法
private val binder = object : IUserManager.Stub() {
// 实现AIDL中定义的添加用户方法
@Throws(RemoteException::class)
override fun addUser(user: User) {
// 将用户添加到列表
_userList.add(user)
// 打印当前列表中的所有用户,用于调试
_userList.forEach {
Log.d(TAG, "addUser: $it")
}
}
// 实现AIDL中定义的获取用户列表方法
@Throws(RemoteException::class)
override fun getUserList(): MutableList<User> {
return _userList
}
// 注册回调接口,客户端通过此方法注册以接收通知
override fun registerCallback(callback: IUserChangeCallback?) {
_callbackList.register(callback)
}
// 解除注册回调接口
override fun unregisterCallback(callback: IUserChangeCallback?) {
_callbackList.unregister(callback)
}
}
// 服务创建时调用
override fun onCreate() {
super.onCreate()
// 创建协程作用域,在IO线程中执行定时任务
CoroutineScope(Dispatchers.IO).launch {
// 循环执行,每秒向所有注册的客户端发送一次回调通知
while (true) {
delay(1000) // 延迟1秒
// 使用RemoteCallbackList时,必须首先调用beginBroadcast(),最后调用finishBroadcast().得成对出现
val callbackCount = _callbackList.beginBroadcast()
// 遍历所有注册的回调
for (i in 0 until callbackCount) {
try {
// 调用每个回调的changed方法,发送一个新创建的User对象
_callbackList.getBroadcastItem(i)?.changed(User("服务端创建的 User", 18))
} catch (e: RemoteException) {
e.printStackTrace()
}
}
_callbackList.finishBroadcast()
}
}
}
// 当客户端绑定服务时调用,返回AIDL的Binder对象
override fun onBind(intent: Intent): IBinder {
return binder
}
}
注意:AIDL 和 Messenger 一样,进行跨进程通信的大小限制为 1M。这是因为它们内部都是通过 Binder 实现的。
ContentProvider
ContentProvider 是 Android 四大组件之一。虽然 ContentProvider 内部也是由Binder 实现通信的,但是它却没有数量量的限制,可能是内部进行了处理。ContentProvider 的使用如下:
- ContentProvider 端
kotlin
class BigDataContentProvider: ContentProvider() {
companion object {
private const val TAG = "BigDataContentProvider"
// 定义 authority,作为 ContentProvider 的唯一标识
const val AUTHORITY: String = "com.example.myapp.provider"
const val BIG_STRING_DATA: Int = 1
const val FILE: Int = 2
// 定义 Uri 匹配器,用于匹配不同的 Uri 请求
@JvmStatic
val sUriMatcher: UriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
// 注册 Uri 模式
addURI(AUTHORITY, "data", BIG_STRING_DATA)
addURI(AUTHORITY, "files/*", FILE)
}
}
// 初始化 Provider(如数据库连接)
override fun onCreate(): Boolean {
return true
}
// 查询数据
override fun query(
uri: Uri, projection: Array<String?>?, selection: String?,
selectionArgs: Array<String?>?, sortOrder: String?
): Cursor? {
Log.d(TAG, "query: uri = $uri")
when (sUriMatcher.match(uri)) {
BIG_STRING_DATA -> {
val columns = arrayOf("_id", "content")
val cursor = MatrixCursor(columns)
cursor.addRow(arrayOf<Any>(0, StringUtils.getOneMStringContent()))
return cursor
}
else -> {
Log.e(TAG, "query: ")
}
}
// 返回查询结果 Cursor
return null
}
// 核心方法:返回文件描述符,支持跨进程高效传输
@Throws(FileNotFoundException::class)
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
Log.d(TAG, "openFile: uri = $uri context = $context")
val context = context ?: return null
return runCatching {
// 注意,获取手机存储的文件对象。注意,apk 包内部的文件,比如 assets 下
// 的文件是无法传递出去的
val file = getFile(context)
Log.d(TAG, "openFile: file = $file ")
// 打开文件并返回文件描述符
val result = ParcelFileDescriptor.open(file, MODE_READ_ONLY)
Log.d(TAG, "openFile: result = $result")
result
}.onFailure {
Log.e(TAG, "openFile: ",it )
}.getOrNull()
}
// 获取数据类型
override fun getType(uri: Uri): String? {
Log.d(TAG, "getType: uri = $uri")
return when (sUriMatcher.match(uri)) {
BIG_STRING_DATA -> "vnd.android.cursor.dir/vnd.com.example.data"
FILE -> "application/octet-stream"
else -> null
}
}
// 插入数据
override fun insert(uri: Uri, values: ContentValues?): Uri? {
// 处理插入逻辑
return null
}
// 删除数据
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String?>?): Int {
// 处理删除逻辑
return 0
}
// 更新数据
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String?>?
): Int {
// 处理更新逻辑
return 0
}
}
AndroidManifest.xml 配置如下:
ini
<provider
android:name=".contentprovider.BigDataContentProvider"
android:process=":remoteProvider"
android:authorities="com.example.myapp.provider"
android:exported="true"
/>
- 客户端
kotlin
class ContentProviderActivity : AppCompatActivity() {
companion object {
private const val TAG = "ContentProviderActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_memory_file_client)
findViewById<Button>(R.id.add_user).setOnClickListener {
getBigString()
getFile()
}
}
private fun getFile() {
// 获取一张图片
val uri = Uri.parse("content://com.example.myapp.provider/files/xxx.jpg")
try {
// 通过 ContentResolver 打开输入流(底层使用文件描述符)
val inputStream = contentResolver.openInputStream(uri) ?: run {
Log.e(TAG, "getFile: null", )
return
}
val bitmap = BitmapFactory.decodeStream(inputStream)
Log.d(TAG, "getFile: $bitmap")
findViewById<ImageView>(R.id.serviceView).setImageBitmap(bitmap)
} catch (e: Exception) {
Log.e(TAG, "getFile: ", e)
}
}
private fun getBigString() {
// 获取超过 1 M 的字符串数据
val cursor = contentResolver.query(
Uri.parse("content://com.example.myapp.provider/data"),
arrayOf("_id", "content"),
null, null, null
)
if (cursor != null) {
try {
// 逐条读取,避免一次性获取所有数据
while (cursor.moveToNext()) {
val content = cursor.getString(cursor.getColumnIndexOrThrow("content"))
// 打印大量数据
Log.d(TAG, "content: $content")
}
} finally {
cursor.close() // 务必关闭 Cursor 释放资源
}
}
}
}
MemoryFile 和 SharedMemory
在 Android 中,我们还可以使用 MemoryFile 来实现共享内存。在 Android framework 层,对于大数据(超过1M)就是通过 MemoryFile 来实现跨进程通信的。其中 MemoryFile 内部就是由 SharedMemory 实现的。
要使用 MemoryFile ,我们就需要先使用 aidl 来传递 ParcelFileDescriptor 对象,通过它来实现共享内存。aidl 文件如下所示:
java
// IMemoryManager.aidl
package com.example.ipc;
import com.example.ipc.IMemoryCallback;
// Declare any non-default types here with import statements
interface IMemoryManager {
void client2server(in ParcelFileDescriptor pfd);
void registerCallback(IMemoryCallback callback);
void unregisterCallback(IMemoryCallback callback);
}
// IMemoryCallback.aidl
package com.example.ipc;
interface IMemoryCallback {
void server2client(in ParcelFileDescriptor pfd);
}
- 客户端
kotlin
class MemoryFileClientActivity : AppCompatActivity() {
companion object {
private const val TAG = "MemoryFileDemo"
}
private val handler = Handler(Looper.getMainLooper())
private var memoryManager: IMemoryManager? = null
private val memoryCallback = object : IMemoryCallback.Stub() {
override fun server2client(pfd: ParcelFileDescriptor?) {
handler.post {
val fd = pfd?.fileDescriptor
if (fd != null) {
val inputStream = FileInputStream(fd)
val bitmap = BitmapFactory.decodeStream(inputStream)
findViewById<ImageView>(R.id.serviceView).setImageBitmap(bitmap)
}
}
}
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
// https://github.com/kongpf8848/aidldemo
memoryManager = IMemoryManager.Stub.asInterface(service)
memoryManager?.registerCallback(memoryCallback)
}
override fun onServiceDisconnected(className: ComponentName) {
memoryManager?.unregisterCallback(memoryCallback)
memoryManager = null
}
}
private fun readFromMemory() {
var pfd: ParcelFileDescriptor? = null
try {
val inputStream = assets.open("server1.jpg") // 读取assets目录下文件
val byteArray = inputStream.readBytes() // 将inputStream转换成字节数组
val memoryFile = MemoryFile("image", byteArray.size) // 创建MemoryFile
memoryFile.writeBytes(byteArray, 0, 0, byteArray.size) // 向MemoryFile中写入字节数组
// 反射获取 FileDescriptor
val fdField: Method = MemoryFile::class.java.getDeclaredMethod("getFileDescriptor")
fdField.isAccessible = true
val fd = fdField.invoke(memoryFile) as FileDescriptor // 获取MemoryFile对应的FileDescriptor
pfd = ParcelFileDescriptor.dup(fd) // 根据FileDescriptor创建ParcelFileDescriptor
memoryManager?.client2server(pfd)
} catch (e: Exception) {
Log.e(TAG, "读取共享内存失败", e)
} finally {
pfd?.close()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_memory_file_client)
// 绑定服务(跨进程)
Intent(this, MemoryFileService::class.java).also { intent ->
bindService(intent, connection, BIND_AUTO_CREATE)
}
findViewById<Button>(R.id.add_user).setOnClickListener {
readFromMemory()
}
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection)
}
}
- 服务端
kotlin
class MemoryFileService : Service() {
companion object {
private const val TAG = "MemoryFileDemo"
}
private val callbacks = RemoteCallbackList<IMemoryCallback>()
private var memoryFile: MemoryFile? = null
private val hookFdUtils: HookFdUtils by lazy {
HookFdUtils()
}
private val binder = object : IMemoryManager.Stub() {
override fun client2server(pfd: ParcelFileDescriptor?) {
val pfd = pfd ?: return
/**
* 从ParcelFileDescriptor中获取FileDescriptor
*/
val fileDescriptor = pfd.fileDescriptor
/**
* 根据FileDescriptor构建InputStream对象
*/
val fis = FileInputStream(fileDescriptor)
runCatching {
// 把大数据传递回去
val count = callbacks.beginBroadcast()
for (i in 0 until count) {
val callback = callbacks.getBroadcastItem(i)
val byteArray = fis.readBytes()
Log.d(TAG, "把传递过来的 ${byteArray.size} byte 数据传递回去")
memoryFile?.close()
memoryFile = MemoryFile("server_image", byteArray.size)
val memoryFile = memoryFile ?: return
memoryFile.writeBytes(byteArray, 0, 0, byteArray.size)
val fdField: Method = MemoryFile::class.java.getDeclaredMethod("getFileDescriptor")
fdField.isAccessible = true
val fd = fdField.invoke(memoryFile) as FileDescriptor // 获取MemoryFile对应的FileDescriptor
val pfd= ParcelFileDescriptor.dup(fd)
callback.server2client(pfd)
}
callbacks.finishBroadcast()
}.onFailure {
Log.e(TAG, "server2client error: ${it.message}", it)
}
}
override fun registerCallback(callback: IMemoryCallback?) {
callbacks.register(callback)
}
override fun unregisterCallback(callback: IMemoryCallback?) {
callbacks.unregister(callback)
}
}
override fun onBind(intent: Intent): IBinder {
return binder
}
override fun onDestroy() {
super.onDestroy()
// 释放资源
memoryFile?.close()
}
}
从上面的代码可以看到,我们需要通过反射的方式来获取 MemoryFile 所对应的 FileDescriptor。这是因为 MemoryFile 的 getFileDescriptor 方法是@hide
方法,无法直接调用,因此使用反射。但是在 Android 9 以后,Google 限制访问 @hide
方法,导致 MemoryFile 法反射调用MemoryFile 的 getFileDescriptor 方法,从而无法跨进程访问。
解决方向有:可以使用 JNI 来反射调用MemoryFile 的 getFileDescriptor 方法。代码如下所示:
首先,设置 ndk 的配置:
ini
android {
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
}
然后创建 cpp 目录,再该目录下创建 CMakeLists.txt 和 HookFdUtils.cpp。
- CMakeLists.txt
scss
cmake_minimum_required(VERSION 3.22.1)
project("hookfdbinary")
add_library(hookfdbinary SHARED
HookFdUtils.cpp)
target_link_libraries(hookfdbinary
android
log)
- HookFdUtils.cpp
scss
#include <jni.h>
#include <android/log.h>
#include <string>
#define LOG_TAG "HookFdUtils"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
// 实现getMemoryFileFd方法
jobject getMemoryFileFd(JNIEnv* env, jobject thiz, jobject memoryFile) {
if (memoryFile == nullptr) {
LOGE("memoryFile is null");
return nullptr;
}
// 获取MemoryFile类的Class对象
jclass memoryFileClass = env->GetObjectClass(memoryFile);
if (memoryFileClass == nullptr) {
LOGE("Failed to get MemoryFile class");
return nullptr;
}
// 获取getFileDescriptor方法ID (返回FileDescriptor对象,无参数)
jmethodID getFdMethod = env->GetMethodID(
memoryFileClass,
"getFileDescriptor",
"()Ljava/io/FileDescriptor;"
);
if (getFdMethod == nullptr) {
LOGE("Failed to get getFileDescriptor method");
env->DeleteLocalRef(memoryFileClass);
return nullptr;
}
// 调用getFileDescriptor方法获取FileDescriptor对象
jobject fileDescriptor = env->CallObjectMethod(memoryFile, getFdMethod);
// 释放局部引用
env->DeleteLocalRef(memoryFileClass);
return fileDescriptor;
}
// 方法映射表:Java方法名 -> 方法签名 -> C++函数指针
static const JNINativeMethod gMethods[] = {
{
"getMemoryFileFd", // Java方法名
"(Landroid/os/MemoryFile;)Ljava/io/FileDescriptor;", // 方法签名
(void*)getMemoryFileFd // 对应C++函数
}
};
// 要注册的类名(完整包名+类名)
static const char* const kClassName = "com/example/ipc/HookFdUtils";
// JNI_OnLoad:库加载时自动调用,完成注册
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = nullptr;
jint result = -1;
// 获取JNI环境
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
LOGE("Failed to get JNI environment");
return result;
}
if (env == nullptr) {
LOGE("env is null");
return result;
}
// 查找要注册的类
jclass clazz = env->FindClass(kClassName);
if (clazz == nullptr) {
LOGE("Failed to find class: %s", kClassName);
return result;
}
// 注册方法
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0) {
LOGE("Failed to register natives");
env->DeleteLocalRef(clazz);
return result;
}
env->DeleteLocalRef(clazz);
// 返回支持的JNI版本
return JNI_VERSION_1_6;
}
- HookFdUtils
java
public class HookFdUtils {
static {
System.loadLibrary("hookfdbinary");
}
public native FileDescriptor getMemoryFileFd(MemoryFile memoryFile);
}
这样就可以使用 HookFdUtils 来代替java反射来调用MemoryFile 的 getFileDescriptor 方法了。代码如下所示:
kotlin
private val hookFdUtils: HookFdUtils by lazy {
HookFdUtils()
}
val fd = hookFdUtils.getMemoryFileFd(memoryFile)
这样就可以在 Android 9 以上的版本中使用 MemoryFile 来传输大数据了。