Android : App客户端与后台服务的AIDL通信以及后台服务的JNI接口实现
一、APP客户端进程与后台服务进程的AIDL通信
AIDL(Android Interface definition language-“接口定义语言”) 是 Android 提供的一种进程间通信 (IPC:Inter-Process Communication) 机制,支持的数据类型:
1. Java 的原生类型;
2. String 和CharSequence;
3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型; 以上三种类型都不需要导入(import);
4. AIDL 自动生成的接口 需要导入(import);
5. 实现android.os.Parcelable 接口的类. 需要导入(import)。
Android studio工程建立如下:
app和remoteserver按常规应用建立,remoteservicecontract通过新建Android Library生成:
也可以将原本的应用模块改成库模块:
然后在remoteservicecontract建立aidl目录并新建AIDL文件:
建立如下三个AIDL接口:
aidl文件的声明和java实现如下:
(1)Entity.aidl 是声明本地实现的 android.os.Parcelable 接口的类
// Entity.aidl package com.example.remoteserver; parcelable Entity;
java实现:
package com.example.remoteserver; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; public class Entity implements Parcelable { private int age; private String name; private final String TAG = "Engity"; public Entity() { } public Entity(int age, String name) { Log.i(TAG,"new age="+age+",name="+name); this.age = age; this.name = name; } protected Entity(Parcel in) { age = in.readInt(); name = in.readString(); } public static final Creator<Entity> CREATOR = new Creator<Entity>() { @Override public Entity createFromParcel(Parcel in) { return new Entity(in); } @Override public Entity[] newArray(int size) { return new Entity[size]; } }; public int getAge() { Log.i(TAG,"get age="+age); return this.age; } public void setAge(int age) { Log.i(TAG,"set age="+age); this.age = age; } public String getName() { Log.i(TAG,"get name="+name); return this.name; } public void setName(String name) { Log.i(TAG,"set name="+name); this.name = name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(age); dest.writeString(name); } @Override public String toString() { return String.format("age=%s, name=%s", getAge(), getName()); } }
(2)IRemoteService.aidl声明服务端供客户端调用的接口:
// IRemoteService.aidl package com.example.remoteserver; import com.example.remoteserver.Entity; import com.example.remoteserver.ITVCallback; // Declare any non-default types here with import statements interface IRemoteService { void doSomeThing(int anInt,String aString); void addEntity(in Entity entity); void setEntity(int index,in Entity entity); List<Entity> getEntity(); void asyncCallSomeone( String para, ITVCallback callback); }
java实现:
package com.example.remoteserver; import android.Manifest; import android.app.Service;import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.location.LocationManager; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.provider.Settings; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Toast; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; public class RemoteService extends Service { public static final String TAG = "RemoteService"; private List<Entity> data = new ArrayList<Entity>();
int mStartMode; // indicates how to behave if the service is killed final RemoteCallbackList<ITVCallback> remoteCallbackList = new RemoteCallbackList<>(); public void onCreate() { // Used to load the 'native-lib' library on application startup. System.loadLibrary("RemoteServiceJNI"); //加载native接口的c库 pthreadState = true; DataThread datathread = new DataThread(); datathread.start(); Nano_Printf("service onCreate"); Nano_Printf(String.format("<%s>",stringFromJNI())); //调用JNI接口 }
public int onStartCommand(Intent intent, int flags, int startId) { Nano_Printf("service onStartCommand"); return mStartMode; }
/*返回Binder对象实例*/ public IBinder onBind(Intent intent) { Nano_Printf("service on bind,intent = %s",intent.toString()); return binder; } public void onDestroy() { Nano_Printf("service onDestroy"); pthreadState = false; // 取消掉所有的回调 remoteCallbackList.kill(); } private void Nano_Printf(String...args) { String str = ""; for(int i = 0; i < args.length; i++){ str += args[i]; if( i != args.length - 1){ str += ", "; } } Log.d(TAG, str); }
/*生成的 Binder 对象实例,实现接口定义的方法*/ private final IRemoteService.Stub binder = new IRemoteService.Stub() { @Override public void doSomeThing(int anInt, String aString) throws RemoteException { Log.i(TAG, String.format("rcv:%s, %s", anInt, aString)); } @Override public void addEntity(Entity entity) throws RemoteException { Log.i(TAG, String.format("rcv:entity = %s", entity)); data.add(entity); } @Override public List<Entity> getEntity() throws RemoteException { Log.i(TAG, String.format("get:List<Entity> = %s", data)); return data; } public void setEntity(int index, Entity entity) throws RemoteException { Log.i(TAG, String.format("set:entity[%d] = %s", index, entity)); data.set(index, entity); } @Override
/*客户端调用asyncCallSomeone接口并传过来callback实例,服务端注册callback并回调修改结果*/ public void asyncCallSomeone(String para, ITVCallback callback) throws RemoteException { Log.i(TAG, String.format("asyncCallSomeone...")); remoteCallbackList.register(callback); final int len = remoteCallbackList.beginBroadcast(); for (int i = 0; i < len; i++) { remoteCallbackList.getBroadcastItem(i).onSuccess(para + "_callbck"); } remoteCallbackList.finishBroadcast(); } }; /*native interface*/ public native String stringFromJNI(); }
(3)ITVCallback.aidl声明客户端向服务端注册的回调接口:
// Callback.aidl package com.example.remoteserver; // Declare any non-default types here with import statements interface ITVCallback { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void onSuccess(String aString); }
app客户端Java实现:
package com.example.administrator.sheldon_aidl;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.nfc.Tag;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
/*导入资源库中AIDL定义的类*/
import com.example.remoteserver.Entity;
import com.example.remoteserver.ITVCallback;
import com.example.remoteserver.IRemoteService;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private boolean mBound = false;
private IRemoteService iRemoteService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mBound) {
alert("未连接到远程服务");
return;
}
try {
Entity entity = new Entity(100, "sheldon");
if (iRemoteService != null){
iRemoteService.addEntity(entity); //调用服务端的接口添加成员变量
iRemoteService.registerCallBack(mCallback);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
findViewById(R.id.modify).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mBound) {
alert("未连接到远程服务");
return;
}
if (iRemoteService != null) {
try {
List<Entity> entityList = iRemoteService.getEntity();
int pos = 1;
if(entityList.size()>pos){
entityList.get(pos).setAge(1314);
entityList.get(pos).setName("li");
iRemoteService.setEntity(pos,entityList.get(pos)); //调用服务端的接口修改成员变量
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
findViewById(R.id.callback).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mBound) {
alert("未连接到远程服务");
return;
}
if (iRemoteService != null) {
try {
final String para = "canshu";
iRemoteService.asyncCallSomeone(para, mCallback); //调用服务端的接口并传入回调
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
private void alert(String str) {
//解决在子线程中调用Toast的异常情况处理(还是有异常)
//Looper.prepare();
Toast.makeText(this, str, 0).show();
//Looper.loop();
}
@Override
protected void onStart() {
super.onStart();
if (!mBound) {
attemptToBindService(); // 尝试绑定服务
}
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mServiceConnection); // 解绑服务
mBound = false;
}
}
/**
* 尝试与服务端建立连接
*/
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("com.example.REMOTE.myserver"); //这里的action由..\remoteserver\src\main\AndroidManifest.xml中指定
intent.setPackage("com.example.remoteserver"); //这里即为服务端进程包名
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
/*实现 ServiceConnection 接口,在其中拿到IRemoteService AIDL类*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(getLocalClassName(), "service connected");
iRemoteService = IRemoteService.Stub.asInterface(service);
mBound = true;
if (iRemoteService != null) {
try {
iRemoteService.doSomeThing(0, "anything string");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(getLocalClassName(), "service disconnected");
mBound = false;
}
};
/*实现callback接口*/
private ITVCallback mCallback = new ITVCallback.Stub() {
@Override
public void onSuccess(String aString) throws RemoteException { //回调接口被服务端调用,获得结果并用Toast显示
Log.d("nano-client ", String.format("service arrived %s",aString));
alert(String.format("回调: %s", aString));
}
};
}
客户端和服务端的通信AIDL接口定义在remoteservicecontract库中,需要在各模块导入使用,
如果各模块在同一个Android Studio工程开发,可通过修改build.gradle直接应用:
也可以将生成的aar,提供给另一个工程导入使用:
二、后台服务的JNI接口实现:
目录结构如下:

1.声明native方法,如 RemoteService.java 中声明的:
/*native interface*/ public native String stringFromJNI();
2.通过javah生成native格式的头文件 com_example_remoteserver_RemoteService.h:

javah -d 【头文件生成路径】 -classpath 【java文件路径】-jni 【包名.类名】
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_remoteserver_RemoteService */ #ifndef _Included_com_example_remoteserver_RemoteService #define _Included_com_example_remoteserver_RemoteService #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_remoteserver_RemoteService * Method: stringFromJNI * Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_remoteserver_RemoteService_stringFromJNI (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
3.根据生成的jni头文件建立 RemoteServiceJNI.c 文件实现其接口:
#include <jni.h> #include <string.h> #include <android/log.h> #define TAG "nano-jni" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_remoteserver_RemoteService * Method: stringFromJNI * Signature: ()Ljava/lang/String;
参数说明:
Native的对应函数名要以“Java_”开头,后面依次跟上Java的“package名”、“class名”、“函数名”,中间以下划线“_” 分割,在package名中的“.”也要改为“_”。
关于函数的参数和返回值也有相应的规则。对于Java中的基本类型如int 、double 、char 等,
在Native端都有相对应的类型来表示,如jint 、jdouble 、jchar 等;其他的对象类型则统统由jobject 来表示,
(String 是个例外,由于其使用广泛,故在Native代码中有jstring 这个类型来表示)。
而对于Java中的数组,在Native中由jarray 对应,具体到基本类型和一般对象类型的数组则有jintArray 等
和jobjectArray 分别对应(String 数组在这里没有例外,同样用jobjectArray 表示)。
另外在JNI的Native函数中,其前两个参数JNIEnv *和jobject 是必需的,前者是一个JNIEnv 结构体的指针,这个结构体中定义了很多JNI的接口函数指针,
使开发者可以使用JNI所定义的接口功能;后者指代的是调用这个JNI函数的Java对象,有点类似于C++中的this 指针。
在上述两个参数之后,还需要根据Java端的函数声明依次对应添加参数,如下Java中声明的JNI函数没有参数,则Native的对应函数只有类型为JNIEnv *和jobject 的两个参数。
*/ JNIEXPORT jstring JNICALL Java_com_example_remoteserver_RemoteService_stringFromJNI (JNIEnv *env, jobject thiz){ return (*env)->NewStringUTF(env, "Hi! Sheldon, I`m JNI ~"); } #ifdef __cplusplus } #endif
4.编译c文件生成so:
Android studio 的gradle3.0版本以下可以配置NDK编译c/cpp文件:
修改对应模块的build.gradle在defaultConfig中添加:
//gradle3.0以上已经不支持该方式 ndk { moduleName "libRemoteServiceJNI" //指定生成的so文件名 ldLibs "log", "z", "m" //添加log库 abiFilters "armeabi", "armeabi-v7a", "x86" //支持cpu的类型 }
而gradle3.0以上版本需要用CMake工具编译:
首先Android studio安装CMake工具:
然后同样在defaultConfig{}中添加编译参数:
// 使用Cmake工具 externalNativeBuild { cmake { cppFlags "" //生成多个版本的so文件 abiFilters 'armeabi-v7a' //,'arm64-v8a','x86','x86_64' } }
另外在defaultConfig{}的外一层即android{}中配置编译脚本的路径:
// 配置CMakeLists.txt路径 externalNativeBuild { cmake { path "CMakeLists.txt" //编译脚本 } }
比较关键的是CMakeLists.txt编译脚本,具体内容如下:
############################### #1.cmake verson,指定cmake版本 cmake_minimum_required(VERSION 3.4.1) #2.C++ 的编译选项是 CMAKE_CXX_FLAGS # 指定编译参数,可选 #SET(CMAKE_C_FLAGS "-Wno-error=format-security -Wno-error=pointer-sign") #3.设置cmake生成so输出的路径 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}) #4.包含头文件,导入第三方动态库 include_directories( ${CMAKE_SOURCE_DIR}/src/main/jni/include ) #5.指定源文件和编译生成so名及类型 # 生成在intermediates/cmake/和以上指定的目录下(指定的话build.gradle设置pickFirst避免冲突) add_library(RemoteServiceJNI SHARED ${CMAKE_SOURCE_DIR}/src/main/jni/RemoteServiceJNI.c) #6.设置需要生成so的第三方链接库 target_link_libraries( RemoteServiceJNI log android ) #添加子目录,将会调用子目录中的CMakeLists.txt #ADD_SUBDIRECTORY(one) #ADD_SUBDIRECTORY(two) ###############################
配置好编译环境后,点击make project生成so在remoteserver\build\intermediates\cmake\debug\obj\armeabi-v7a\libRemoteServiceJNI.so
在java中加载调用即可:
如果遇到: More than one file was found with OS independent path 'lib/armeabi-v7a/xxx.so' 的报错,则在build.gradle中的android {}里添加:
packagingOptions { //For Error: More than one file was found with OS independent path pickFirst 'lib/armeabi-v7a/libnano_socket.so' pickFirst 'lib/armeabi-v7a/libRemoteServiceJNI.so' }
完整工程已上传到GitHub: https://github.com/dragonforgithub/sheldon_aidl.git
posted on 2018-07-05 15:50 sheldon_blogs 阅读(3847) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY