0. 前言
不论是Android还是其他操作系统,都会有自己的IPC机制,所谓IPC(Inter-Process Communication)即进程间通信。首先线程和进程是很不同的概念,线程是CPU调用的最小单元,进程一般在PC和移动设备上指一个程序或者一个应用,一个进程可以包含多个线程。
IPC方式有很多,在Android中常用的IPC方式包括Bundle、文件、Messenger、AIDL、ContentProvider和Socket等方式。本篇主要讲解使用AIDL的方式。
1. AIDL
AIDL(Android Interface Definition Language)是一种接口定义语言。在跨进程通信时,客户端只需要使用一个代理,服务器只需要实现一个继承Binder并实现定义接口的Stub内部类。两者不需要知道对方的实现细节,所以说两者都是实现了同一个方法,只不过是客户端调用并通过跨进程真正在远程服务中执行,而大量的跨进程过程都是相同的,为了简化使用的成本Android使用了AIDL,只需要定义一个接口,就会自动生成客户端和服务器的跨进程实现代码,而如果想实现类似验证之类的操作,只需要在Stub的onTransact中进行相应的实现就可。
和Messenger相比,AIDL最大的不同是具备多线程处理能力。AIDL的使用主要分为以下三个步骤:
(1)创建.aidl文件,定义接口;
(2)在代码中实现接口,Android SDK会根据aidl文件生成接口,接口内部有一个名为Stub内部抽象类,这个类继承了Binder类并实现aidl文件中定义的接口,我们需要拓展Stub类并实现里面的抽象方法;
(3)复写Service的onBind(),返回Stub类的实现,将接口暴露给客户端。
2. AIDL的简单使用示例
2.1 创建AIDL文件
本节将介绍一个最简单的AIDL使用示例,完成一个跨进程的远程加法计算过程。首先创建AIDL文件,并定义接口。在Android Studio中建立AIDL文件会默认在mian目录下创建aidl文件夹。这里创建了ImyAidlInterface接口以及add()方法。返回值类型为int。
// IMyAidlInterface.aidl
package com.example.ipc_aidl;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
int add(int num1,int num2);
}
该文件需要客户端和服务端各一份,这里为了简便示例在同一工程下进行,只需要一份即可,服务端使用android:process开启新进程,效果和两个工程下的通信是一样的。
2.2 生成java文件
编译一下工程,会在/build/generated/source/目录下生成一个aidl文件夹,且文件夹下的目录如下图所示,该java文件表示我们的aidl文件已经创建成功。
2.3 实现接口
声明接口后要实现接口,编写一个服务端,实例化IMyAidlInterface.Stub(),实现具体方法,并在onBind()中返回该Binder实例供客户端使用。
package com.example.ipc_aidl;
/**
* 服务端
* 实现接口并将其暴露给客户端
* Created by SEU_Calvin on 2017/3/25.
*/
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class IRemoteService extends Service {
private IBinder iBinder = new IMyAidlInterface.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
return num1 + num2;
}
};
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
}
最后别忘了在Manifest.xml中声明该服务,有一次不小心把该服务写在了<application>标签外面,然后客户端bindService()死活就是返回false,蠢的不行QAQ。
<service android:name=".IRemoteService"
android:process=":remote">
</service>
2.4 客户端调用接口
最后是Activity中的逻辑:
package com.example.ipc_aidl;
/**
* 客户端
* 客户端调用远程接口
* Created by SEU_Calvin on 2017/3/25.
*/
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity{
private EditText et_num1;
private EditText et_num2;
private EditText edit_show_result;
private Button btn_count;
private int mNum1;
private int mNum2;
private int mTotal;
private IMyAidlInterface iMyAidlInterface;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//服务绑定成功后调用,这里的service就是服务端onBind返回的iBinder
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定服务
Intent intent = new Intent(MainActivity.this,IRemoteService.class);
//或者使用下面的方式设置Intent
//intent.setComponent(new ComponentName("com.example.ipc_aidl","com.example.ipc_aidl.IRemoteService"));
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
et_num1 = (EditText)findViewById(R.id.et_num1);
et_num2 = (EditText)findViewById(R.id.et_num2);
edit_show_result = (EditText)findViewById(R.id.result);
btn_count = (Button)findViewById(R.id.btn_count);
btn_count.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mNum1 = Integer.parseInt(et_num1.getText().toString());
mNum2 = Integer.parseInt(et_num2.getText().toString());
try {
if(iMyAidlInterface == null){
Toast.makeText(MainActivity.this,"iMyAidlInterface is null",Toast.LENGTH_SHORT).show();
}else{
mTotal = iMyAidlInterface.add(mNum1,mNum2);
}
} catch (RemoteException e) {
e.printStackTrace();
}
edit_show_result.setText(mTotal+"");
}
});
}
@Override
protected void onDestroy(){
super.onDestroy();
unbindService(serviceConnection);
}
}
首先执行bindService()方法绑定服务端的远程服务,绑定成功后会回调serviceConnection中的onServiceConnected()方法。在该方法中获取到服务端返回的Binder实例,并赋值给ImyAidlInterface类型的成员变量,调用它的add()方法即可调用远程服务实现两个加数的相加运算。
参考源码地址点击下载。请大家多点赞支持~
有的朋友可能会说这种服务也太简单了吧,有没有更高级一点的用法呢,下一篇会介绍使用AIDL传递自定义的对象。