背景
最近打算学习一下如何在framework层添加一个自定义service。
了解到自定义service需要使用aidl,为了加强对aidl的了解和使用过程,特意又温习了一下aidl的使用,并用博客的形式记录下来。
aidl官方参考:https://developer.android.google.cn/develop/background-work/services/aidl?hl=en
AIDL是什么
官方解释:Android 接口定义语言 (AIDL) 类似于其他 IDL:它允许您定义客户端和服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。
定义 AIDL 接口
这里我使用的ide是Android Studio Iguana版本。
首先创建一个project,里面创建两个app一个叫client,另一个叫server。
在server app的src/main目录创建一个aidl文件IMyAidlInterface.aidl,内容如下
// IMyAidlInterface.aidl
package com.hai.server;
// Declare any non-default types here with import statements
//aidl说明参考:https://developer.android.google.cn/develop/background-work/services/aidl?hl=en
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getPid();
oneway void getThreadId();
}
IMyAidlInterface.aidl中定义了三个接口供客户端调用。
然后编译一下project,就会在server的build目录下自动生成IMyAidlInterface.java接口文件,内容如下
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.hai.server;
// Declare any non-default types here with import statements
//aidl说明参考:https://developer.android.google.cn/develop/background-work/services/aidl?hl=en
public interface IMyAidlInterface extends android.os.IInterface
{
/** Default implementation for IMyAidlInterface. */
public static class Default implements com.hai.server.IMyAidlInterface
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
}
@Override public int getPid() throws android.os.RemoteException
{
return 0;
}
@Override public void getThreadId() throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.hai.server.IMyAidlInterface
{
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.hai.server.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.hai.server.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.hai.server.IMyAidlInterface))) {
return ((com.hai.server.IMyAidlInterface)iin);
}
return new com.hai.server.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
}
switch (code)
{
case TRANSACTION_basicTypes:
{
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
break;
}
case TRANSACTION_getPid:
{
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
break;
}
case TRANSACTION_getThreadId:
{
this.getThreadId();
break;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
return true;
}
private static class Proxy implements com.hai.server.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int getPid() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void getThreadId() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getThreadId, _data, null, android.os.IBinder.FLAG_ONEWAY);
}
finally {
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getThreadId = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public static final java.lang.String DESCRIPTOR = "com.hai.server.IMyAidlInterface";
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int getPid() throws android.os.RemoteException;
public void getThreadId() throws android.os.RemoteException;
}
IMyAidlInterface.java结构图
可以看到IMyAidlInterface.java也定义了和aidl相同的三个接口,并且定义了Default和Stub两个静态内部类去实现IMyAidlInterface接口。
- Default类默认空实现且没有继承Binder,实际无意义。
- Stub是一个继承Binder抽象类,实际是要在server端写一个Stub的实现类去实现实际的接口功能
- Stub.Proxy一般用于client端。
Server端实现服务
接下来就是在server端的service中实现aidl接口服务,这里我写了一个ServerService类,内容如下
package com.hai.server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.system.Os;
import android.util.Log;
public class ServerService extends Service {
private static final String TAG = "ServiceService";
IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
Log.d(TAG, "basicTypes() called with: anInt = [" + anInt + "], aLong = [" + aLong + "], aBoolean = [" + aBoolean + "], aFloat = [" + aFloat + "], aDouble = [" + aDouble + "], aString = [" + aString + "]");
}
@Override
public int getPid() throws RemoteException {
SystemClock.sleep(2000);
return Os.getpid();
}
@Override
public void getThreadId() throws RemoteException {
SystemClock.sleep(2000);
}
};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind() called with: intent = [" + intent + "]");
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind() called with: intent = [" + intent + "]");
return super.onUnbind(intent);
}
}
为了client端可以通过隐式服务绑定服务,还需要在AndroidManifest.xml做一下配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AidlDemo">
<service
android:name=".ServerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.hai.server.ServerService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
当client端通过bindservice绑定成功后,服务端就会返回server端的binder对象给到client端。
Client端使用服务
首先需要把server端的aidl整个文件夹拷贝到client端的src目录,以便client端能够获得aidl的访问权限。client端的代码结构如图
编译一下project,就会在client的build目录下自动生成IMyAidlInterface.java接口文件。
接下来就是bindservice,bindservice服务成功后就可以获得binder的代理对象,进而调用aidl接口服务。如下是在activity中通过bindservice的方式调用aidl接口服务。
package com.hai.aidldemo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import com.hai.server.IMyAidlInterface;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
IMyAidlInterface myAidlInterface;
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected() called with: name = [" + name + "], service = [" + service + "]");
myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected() called with: name = [" + name + "]");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
}
public void clk(View view) {
switch (view.getId()) {
case R.id.btn_bind:
if (myAidlInterface == null) {
boolean b = bindService(new Intent("com.hai.server.ServerService").setPackage("com.hai.server"), serviceConnection, Context.BIND_AUTO_CREATE);
Log.d(TAG, "bindService: " + b);
}
break;
case R.id.btn_unbind:
if (myAidlInterface != null) {
unbindService(serviceConnection);
}
break;
case R.id.btn_basicType:
try {
myAidlInterface.basicTypes(1, 2, true, 4, 5, "6");
} catch (RemoteException e) {
throw new RuntimeException(e);
}
break;
case R.id.btn_getPid:
try {
long start = System.currentTimeMillis();
int pid = myAidlInterface.getPid();
Log.d(TAG, "getPid: " + pid + ", cost:" + (System.currentTimeMillis() - start));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
break;
case R.id.btn_getThreadId:
try {
long start = System.currentTimeMillis();
myAidlInterface.getThreadId();
Log.d(TAG, "getThreadId: cost:" + (System.currentTimeMillis() - start));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
break;
}
}
}
项目源码:
https://gitee.com/menty/aidl-demo/blob/master/client/src/main/java/com/hai/aidldemo/MainActivity.java