为什么使用AIDL
AIDL和Messenger都可以实现跨进程通信,Messenger底层也是基于AIDL的。使用AIDL而不使用Messenger的情况是:允许从不同应用的客户端访问你的Service且你的Service需要处理多线程的情况,因为Messenger中的消息默认是串行执行的。还有一点就是:使用Messenger主要是为了传递消息,很多时候需要跨进程调用服务端的方法,这种情况Messenger就无法做到了,可以使用AIDL实现跨进程的方法的调用。
AIDL主要分为客户端和服务端实现:
1、服务端实现:
1、创建aidl文件:在Android Studio当中创建一个文件夹aidl,然后再找个文件夹中创建一个包,在这个包里面创建一个aidl文件,如下所示。在接口中定义了三个方法。当重新Make Project,会在Android Studio的工程目录:build/generated/source/aidl/debug/aidl包名 目录下面生成对应的.java文件。这个.java文件就是对Binder的封装,使得我们不需要直接对Binder进行操作。
// IRemoteService.aidl
package com.easyliu.demo.aidlremotedemo;
// Declare any non-default types here with import statements
interface IRemoteService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int getPid();
int add(int a,int b);
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
2、创建一个Service,在Service当中使用上面的aidl接口创建一个IBinder对象,然后在Service的onBind()方法中返回这个IBinder对象即可。如下所示:
package com.easyliu.demo.aidlremotedemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
public class RemoteService extends Service {
private static final String TAG = RemoteService.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public int getPid() throws RemoteException {
return Process.myPid();
}
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
};
}
3、然后记得在Manifest文件里面进行注册,不过Android Studio会自动生成,这一点很方便。在这里指定了一个action,这样别的进程就可以通过Intent来查找此Service。注意,当给Service加上Intent-filter之后,其android:exported默认就为true了,这样别的APP就可以访问了。同时,设置了android:process属性,让Service运行在不同的进程。这跟运行在两个APP当中效果是一样的,所以可以直接放在一个APP当中进行测试。
<service
android:name=".RemoteService"
android:process=":remote"
android:enabled="true">
<intent-filter>
<action android:name="com.easyliu.demo.aidlremotedemo.RemoteService" />
</intent-filter>
</service>
2、客户端实现:
客户端的代码比较简单,在布局文件当中创建两个文本输入框、一个按钮和一个文本显示控件。点击按钮调用远程服务端的方法执行加法操作,然后在文本控件中显示。
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.easyliu.demo.aidlremotedemo.MainActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_mainActivity_inputA"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:hint="input a number a"
android:inputType="number" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_mainActivity_inputB"
android:layout_below="@+id/et_mainActivity_inputA"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:hint="input a number b"
android:inputType="number" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add"
android:id="@+id/btn_mainActivity_add"
android:layout_below="@+id/et_mainActivity_inputB"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:textSize="20sp"
android:layout_marginTop="20dp"
android:id="@+id/tv_sum"
android:layout_below="@+id/btn_mainActivity_add"
android:layout_centerHorizontal="true" />
</RelativeLayout>
主Activity:
package com.easyliu.demo.aidlremotedemo;
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.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private AdditionServiceConnection mServiceConnection;
private EditText et_mainActivity_inputA;
private EditText et_mainActivity_inputB;
private Button btn_mainActivity_add;
private TextView tv_sum;
private boolean mIsBound;
private IRemoteService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
@Override
protected void onStart() {
super.onStart();
doBindService();
}
/**
* init Views
*/
private void initViews() {
et_mainActivity_inputA = (EditText) findViewById(R.id.et_mainActivity_inputA);
et_mainActivity_inputB = (EditText) findViewById(R.id.et_mainActivity_inputB);
tv_sum = (TextView) findViewById(R.id.tv_sum);
btn_mainActivity_add = (Button) findViewById(R.id.btn_mainActivity_add);
btn_mainActivity_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if (mService != null) {
int sum = mService.add(Integer.parseInt(et_mainActivity_inputA.getText().toString()),
Integer.parseInt(et_mainActivity_inputB.getText().toString()));
tv_sum.setText(String.valueOf(sum));
} else {
tv_sum.setText("请先绑定服务!");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
/**
* bind service
*/
private void doBindService() {
mServiceConnection = new AdditionServiceConnection();
Intent intent = new Intent(RemoteService.class.getName());
intent.setPackage("com.easyliu.demo.aidlremotedemo");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
/**
* unbind service
*/
private void doUnbindService() {
if (mIsBound) {
unbindService(mServiceConnection);
mServiceConnection = null;
mIsBound = false;
}
}
/**
* ServiceConection
*/
class AdditionServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IRemoteService.Stub.asInterface((IBinder) service);
mIsBound = true;
try {
//设置死亡代理
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
tv_sum.setText("Servie Conected!");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mIsBound = false;
tv_sum.setText("Servie DisConected!");
}
}
/**
* 监听Binder是否死亡
*/
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mService == null) {
return;
}
mService.asBinder().unlinkToDeath(mDeathRecipient, 0);
mService = null;
//重新绑定
doBindService();
}
};
@Override
protected void onStop() {
super.onStop();
doUnbindService();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
在主Activity当中,在onStart方法当中bindService,然后在onStop方法当中unBindService。当然你可以在onCreate方法当中bindService,然后在onDestory方法unBindService,看具体需求。也可以手动进行绑定和解绑。
同时给返回的IBinder对象设置了一个死亡代理,当远端Service由于某种原因死亡的时候,就会调用此回调方法,我们就可以在此方法当中进行一些操作,比如,重新bindService等。
当然,也可以在onServiceDisconnected里面重新连接Service,只是这个方法运行在主线程,可以访问主UI,而DeathRecipient接口的binderDied回调方法中不能访问主UI。
3、验证AIDL功能
当执行打开主界面->回到home->再进入主界面,打印的log如下所示,说明远程绑定成功。当回到桌面的时候,会调用Activity的onStop方法,在里面会解除绑定。由于这里只有一个客户端绑定到Service,所以当绑定解除的时候就销毁了。关于绑定服务的详细简介,请参考:Bound Service
<span style="font-size:12px;">06-18 21:59:11.313 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onBind
06-18 21:59:36.842 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onUnbind
06-18 21:59:36.842 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onDestroy
06-18 21:59:51.743 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onBind</span><strong style="font-size: 24px;">
</strong>
下面来看一下执行效果
启动Activity,输入两个数字1和7,点击ADD按钮,得到数字8,说明成功调用远程服务端的方法。
然后当按home键退回桌面再打开,界面如下所示,说明当返回Activity的时候调用了onStart方法,重新bindService。