IPC机制之基本介绍
Posted awkflf11
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPC机制之基本介绍相关的知识,希望对你有一定的参考价值。
综述
IPC(interprocess communication)是指进程间通信,也就是在两个进程间进行数据交互。不同的操作系统都有他们自己的一套IPC机制。例如在Linux操作系统中可以通过管道、信号量、消息队列、内存共享、套接字等进行进程间通信。那么在Android系统中我们可以通过Binder来进行进程间的通信。当然除了Binder我们还可以使用Socket来进行进程间的通信。
既然需要进程通信,那么就必须有多个进程。当然,在两个应用交互中必然出现多进程的情况。若是在一个应用中呢?我们可以通过给四大组件在androidMenifest中为他们指定android:process属性来实现不同的组件在不同进程中运行。下面就来介绍一下Android中进程间通信的实现方式。
AIDL简介
AIDL是 Android Interface Definition Language的缩写。AIDL 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行 IPC的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。
AIDL用法
首先我们创建一个AIDL文件,在AndroidStudio中当我们创建一个AIDL文件时会自动为我们创件一个AILD文件夹,用于存放AIDL文件。创建完之后重新rebuild会自动生成aidl实现类。
在下面的例子当中,我们将Service单独作为一个应用在系统中运行,在另一个用于访问Service的client也单独作为一个应用运行在系统中。这样保证了两个程序分别运行在两个进程中。并且使用了butterknife进行控件绑定。
AIDL简单用法
演示
在Service中我们对客户端传来的两个整数做了一次加法运算并返回到客户端中。
AIDL代码
// ICalculate.aidl
package com.ljd.aidl;
// Declare any non-default types here with import statements
interface ICalculate {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int add(int first, int second);
}
服务端代码
package com.ljd.aidl.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import com.ljd.aidl.ICalculate;
public class CalculateService extends Service {
public CalculateService() {
}
private Binder mBinder = new ICalculate.Stub(){
@Override
public int add(int first, int second) throws RemoteException {
return first + second;
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
客户端代码
package com.ljd.aidl.activity;
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.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.ljd.aidl.ICalculate;
import com.ljd.aidl.client.R;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class Demo1Activity extends AppCompatActivity {
private final String TAG = "DEMO1";
//是否已经绑定service
private boolean mIsBindService;
private ICalculate mCalculate;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"bind success");
Toast.makeText(Demo1Activity.this,"bind service success",Toast.LENGTH_SHORT).show();
mCalculate = ICalculate.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//重新绑定Service防止系统将服务进程杀死而产生的调用错误。
bindService();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo1);
ButterKnife.bind(this);
mIsBindService = false;
}
@Override
protected void onDestroy() {
unbindService();
ButterKnife.unbind(this);
super.onDestroy();
}
@OnClick({ R.id.bind_demo1_btn,R.id.unbind_demo1_btn,R.id.calculate_btn})
public void onClickButton(View v) {
switch (v.getId()){
case R.id.bind_demo1_btn:
bindService();
break;
case R.id.unbind_demo1_btn:
Toast.makeText(this,"unbind service success",Toast.LENGTH_SHORT).show();
unbindService();
break;
case R.id.calculate_btn:
if (mIsBindService && mCalculate != null ){
try {
int result = mCalculate.add(2,4);
Log.d(TAG,String.valueOf(result));
Toast.makeText(this,String.valueOf(result),Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show();
}
break;
}
}
private void bindService(){
Intent intent = new Intent();
intent.setAction("com.ljd.aidl.action.CALCULATE_SERVICE");
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
mIsBindService = true;
}
private void unbindService(){
if(mIsBindService){
mIsBindService = false;
unbindService(mConnection);
}
}
}
AIDL高级用法
对于上面的例子,在AIDL接口中只是使用了一些Java的基本类型,对于AIDL文件并不是所有的类型都是可用的,那么在AIDL中究竟有哪些类型可以使用呢?
AIDL语法规则
默认情况下AIDL支持以下数据类型:
- 所有Java的基本数据类型(例如: int, long,double, char, boolean等)
- String和CharSequence
- List:AIDL实际接收到的是ArrayList,并且List里面所有元素都必须被AIDL支持
- Map: AIDL实际接收到的是HashMap,并且Map里面所有元素都必须被AIDL支持
如果不是上面所述类型,我们必须要显示import进来,即使他们在同一个包中。当我们使用自定义的对象时必须实现Parcelable接口,Parcelable为对象序列化接口,效率比实现Serializable接口高。并且新建一个与该类同名的AIDL文件,声明他为Parcelable类型。
我们定义AIDL接口还需要注意以下几点:
- 方法可以有多个或没有参数,可以有返回值也可以为void
- 在参数中,除了基本类型以外,我们必须为参数标上方向in, out, 或者 inout
- 在AIDL文件中只支持方法,不支持静态常量
演示
在计算机商店中需要采购笔记本进行销售,在服务端中我们添加两台笔记本,在客户端中我们为商店加购一台dell笔记本。
实体类代码
我们首先构建一个计算机实体类,包含笔记本的id,品牌,型号,并且实现Parcelable接口,在AndroidStudio中会为我们自动构造代码。
package com.ljd.aidl.entity;
import android.os.Parcel;
import android.os.Parcelable;
public class ComputerEntity implements Parcelable{
public int computerId; //id
public String brand; //品牌
public String model; //型号
public ComputerEntity(int computerId, String brand, String model) {
this.brand = brand;
this.computerId = computerId;
this.model = model;
}
protected ComputerEntity(Parcel in) {
computerId = in.readInt();
brand = in.readString();
model = in.readString();
}
public static final Creator<ComputerEntity> CREATOR = new Creator<ComputerEntity>() {
@Override
public ComputerEntity createFromParcel(Parcel in) {
return new ComputerEntity(in);
}
@Override
public ComputerEntity[] newArray(int size) {
return new ComputerEntity[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(computerId);
dest.writeString(brand);
dest.writeString(model);
}
}
AIDL代码
在AIDL中对实体类进行声明,包名和文件名必须与实体类一致。在AndroidStudio中新建一个与实体类同名的AIDL文件会报错,需要先用一个其它名字,然后修改与实体类名一致即可。
package com.ljd.aidl.entity;
//包名必须和对用实体类的包名一致
// Declare any non-default types here with import statements
parcelable ComputerEntity;
添加两个接口分别为添加一台笔记本和获取全部笔记本,在该文件中使用到了ComputerEntity类,显示的import进来。
package com.ljd.aidl;
import com.ljd.aidl.entity.ComputerEntity;
// Declare any non-default types here with import statements
interface IComputerManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void addComputer(in ComputerEntity computer);
List<ComputerEntity> getComputerList();
}
服务端代码
package com.ljd.aidl.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.ljd.aidl.IComputerManager;
import com.ljd.aidl.entity.ComputerEntity;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ComputerService extends Service {
private CopyOnWriteArrayList<ComputerEntity> mComputerList = new CopyOnWriteArrayList<>();
public ComputerService() {
}
private final IComputerManager.Stub mBinder = new IComputerManager.Stub() {
@Override
public void addComputer(ComputerEntity computer) throws RemoteException {
mComputerList.add(computer);
}
@Override
public List<ComputerEntity> getComputerList() throws RemoteException {
return mComputerList;
}
};
@Override
public void onCreate() {
super.onCreate();
mComputerList.add(new ComputerEntity(0,"apple","macbookpro"));
mComputerList.add(new ComputerEntity(1,"microsoft","surfacebook"));
mComputerList.add(new ComputerEntity(2,"dell","XPS13"));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
注意:在该类中使用了CopyOnWriteArrayList,CopyOnWriteArrayList能够自动进行线程同步。可是在AIDL中接收和返回的只能是ArrayList,其实AIDL支持的是抽象的List,在Binder中会按照List访问数据并最终形成一个ArrayList,所以在AIDL中返回的还是一个ArrayList。
客户端代码
package com.ljd.aidl.activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
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.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.ljd.aidl.IComputerManager;
import com.ljd.aidl.client.R;
import com.ljd.aidl.entity.ComputerEntity;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class Demo2Activity extends AppCompatActivity{
@Bind(R.id.show_linear)
LinearLayout mShowLinear;
private boolean mIsBindService;
private IComputerManager mRemoteComputerManager;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if(mRemoteComputerManager != null){
mRemoteComputerManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mRemoteComputerManager = null;
bindService();
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIsBindService = true;
Toast.makeText(Demo2Activity.this,"bind service success",Toast.LENGTH_SHORT).show();
mRemoteComputerManager = IComputerManager.Stub.asInterface(service);
try {
mRemoteComputerManager.asBinder().linkToDeath(mDeathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteComputerManager = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo2);
ButterKnife.bind(this);
mIsBindService = false;
}
@Override
protected void onDestroy() {
unbindService();
ButterKnife.unbind(this);
super.onDestroy();
}
@OnClick({R.id.bind_demo2_btn,R.id.unbind_demo2_btn,R.id.test_demo2_btn,R.id.clear_demo2_btn})
public void onClickButton(View v) {
switch (v.getId()){
case R.id.bind_demo2_btn:
bindService();
break;
case R.id.unbind_demo2_btn:
Toast.makeText(this,"unbind service success",Toast.LENGTH_SHORT).show();
unbindService();
break;
case R.id.test_demo2_btn:
if (!mIsBindService || mRemoteComputerManager == null){
Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show();
return;
}
try {
List<ComputerEntity> computerList = mRemoteComputerManager.getComputerList();
for (int i =0;i<computerList.size();i++){
String str = "computerId:" + String.valueOf(computerList.get(i).computerId) +
" brand:" + computerList.get(i).brand +
" model:" + computerList.get(i).model ;
TextView textView = new TextView(this);
textView.setText(str);
mShowLinear.addView(textView);
}
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.clear_demo2_btn:
mShowLinear.removeAllViews();
break;
}
}
private void bindService(){
Intent intent = new Intent();
intent.setAction("com.ljd.aidl.action.COMPUTER_SERVICE");
mIsBindService = bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
private