概述
在Android中实现跨进程通信的方式有很多种,如广播、Content Provider、AIDL(Android Interface Definition Language)。AIDL在跨进程通信中用得最多,因为AIDL相比其他方式,速度更快,效率更高。
AIDL最常见的使用场景:让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
接下来我们创建一个服务端和一个客户端,说明客户端如何从服务端获取数据。首先,我们先把客户端和服务端的项目目录建出来:
服务端
1、IMyAidlInterface.aidl文件
右键项目remoteservice->New->AIDL->AIDL File
创建好后的目录:
// IMyAidlInterface.aidl
package com.wong.remoteservice;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
// 服务端数据
String getString();
}
创建完aidl文件以后,build一下项目,然后会在remoteservice/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out/下会生成一个aidl文件,那说明AIDL文件已经编译成功,如:
编译好后的IMyAidlInterface.java,是这样的:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.wong.remoteservice;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Default implementation for IMyAidlInterface. */
public static class Default implements com.wong.remoteservice.IMyAidlInterface
{
// 服务端数据
@Override public java.lang.String getString() throws android.os.RemoteException
{
return null;
}
@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.wong.remoteservice.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.wong.remoteservice.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.wong.remoteservice.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.wong.remoteservice.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.wong.remoteservice.IMyAidlInterface))) {
return ((com.wong.remoteservice.IMyAidlInterface)iin);
}
return new com.wong.remoteservice.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;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getString:
{
data.enforceInterface(descriptor);
java.lang.String _result = this.getString();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.wong.remoteservice.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;
}
// 服务端数据
@Override public java.lang.String getString() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getString();
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.wong.remoteservice.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_getString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.wong.remoteservice.IMyAidlInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.wong.remoteservice.IMyAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
// 服务端数据
public java.lang.String getString() throws android.os.RemoteException;
}
是的,你没有看错,在这个编译好的IMyAidlInterface.java文件,除了最后一个getString()方法是我们自己写的以外,其他都是在编译时加上的,而且这个才是我们以后要用的。 从上面可以看到较未编译之前多了以下这些:
- IMyAidlInterface接口继承了接口android.os.IInterface;
- 一个实现了IMyAidlInterface接口的静态类Default;
- 本地端IPC的实现类Stub(里面还有个Proxy代理类),不过它是个抽象类,需要本地实现它;
- 2、创建一个MyService类
package com.wong.remoteservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBind();
}
class MyBind extends IMyAidlInterface.Stub{
@Override
public String getString() throws RemoteException {
return "服务端:Hello world";
}
}
}
本地服务内部类继承的是Binder,而现在继承的是IMyAidlInterface.Stub,就是我们刚刚建立的aidl文件。
AndroidManifest.xml配置:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wong.remoteservice">
<application>
<service android:name=".MyService">
<intent-filter>
<action android:name="android.intent.action.com.wong.remoteservice.MY_SERVICE" />
</intent-filter>
</service>
</application>
</manifest>
remoteservice项目的build.gradle构建脚本,配置成以组件来构建,而不插件:
// 而不是apply plugin: 'com.android.library'
apply plugin: 'com.android.application'
android {
...
defaultConfig {
applicationId "com.wong.remoteservice"
...
}
...
}
3、以无界面无图标运行remoteservice App
像上面这样选择app后,就可以以无图标无界面只提供MyService的app运行在手机上了。
客户端
1、首先将在服务端创建的IMyAidlInterface.aidl原封不动的复制到客户端来(注意:路径要一模一样)。复制后在客户端的目录:
build一下项目后,也会得到编译好的IMyAidlInterface.java文件,路径和在服务端是一样的,如:
2、我们在客户諯调用服务端的服务MyService
(1)因为我们要服务返回数据,所以需要使用到bindService这种方式,那么我们要先定义ServiceConnection:
private IMyAidlInterface myBind;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBind = IMyAidlInterface.Stub.asInterface(service);
try {
mTvData.setText(myBind.getString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
myBind = null;
}
};
(2)调用服务端的服务
Intent intent = new Intent();
/*从 Android 5.0开始 隐式Intent绑定服务的方式已不能使用,所以这里需要设置Service所在服务端的包名*/
intent.setPackage("com.wong.remoteservice");
intent.setAction("android.intent.action.com.wong.remoteservice.MY_SERVICE");
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
(3)运行客户端代码就可以调用到刚刚安装上的服务了。
连接本地服务和连接远程服务的代码是一样的,只是调用远程服务的是用到了AIDL技术。
以下是两个demo可以下载来运行试试。
远程服务:demo 本地服务:demo
谢谢阅读