这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战
文章目录
AIDL介绍
AIDL 是 Android 系统中跨进程间通信的接口定义语言。
使用 AIDL 可以实现跨进程间通信,实现方式是使用 A 应用程序绑定 B 应用程序的 Service,基于绑定 Service 的方式来实现跨进程的组件之间的通信。
在跨进程的通信中,使用了 Service 提供绑定的应用程序,通常称之为“服务端”,而绑定了其他应用的 Service 的应用程序,称之为“客户端”。
开发步骤
1、【Server】创建 WorkService 类,继承自android.app.Service,并在 AndroidManifest.mxl 中注册
2、【Server】创建IMusicPlayer.aidl接口,并在接口中定义那些由 Service 实现功能,由 Activity 调用的方法。
3、【Server】WorkService创建内部类InnerBinder,继承自IMusicPlayer.Stub,实现抽象方法。在onBind()方法中,创建InnerBinder的对象,并作为该方法的返回值。
4、【Client】MainActivity创建内部类InnerServiceConnection,实现android.content.ServiceConnection接口
5、【Client】MainActivity当需要绑定 Service 时,调用bindService()方法,第1个参数是 Intent 对象,用于指定被激活的 Service 组件;第2个参数是ServiceConnection对象,使用InnerServiceConnection的对象即可,第3个参数使用常量BIND_AUTO_CREATE
6、【Server】在AndroidManifest.mxl 中设置WorkService 可被隐式激活
7、【Client】新建与服务端 aidl 所在包同名的包,并且将服务端的 aidl 文件复制到客户端
8、【Client】MainActivity声明IMusicPlayer类型的全局变量player,并在InnerBinderConnection的onServiceConnected()方法中,调用IMusicPlayer.Stub.asInterface(iBinder)方法对player进行赋值,方法的参数就是onServiceConnected()方法的第二个参数
9、【Client】MainActivity当需要调用 Service中的方法时,直接使用 player 调用即可
10、【Client】MainActivity重写onDestroy()方法,在该方法中调用unbindService()方法解除与 Service 的绑定,并且,该语句必须在super.onDestroy()之前
按照步骤进行开发
创建两个项目 ServerProject 和 ClientProject 分别写服务端和客户端的代码。
【ServerProject】
1、创建 WorkService 类
public class WorkService extends Service {
public WorkService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
并在 AndroidManifest 中注册
<service
android:name=".WorkService"
android:enabled="true"
android:exported="true"/>
2、创建 AIDL 接口
在 MainActivity 同级目录下创建 IMusicPlayer.aidl
interface IMusicPlayer {
void play();
void pause();
}
这样会在 main 目录下生成一个相同包名的IMusicPlayer.aidl
我们运行一下程序,会在 build 文件夹下找到一个 IMusicPlayer.aidl
打开这个文件,我们会看到
Stub extends android.os.Binder。所以下一步 WorkService 创建的 InnerBinder 就不继承 Binder 了,可以直接继承 IMusicPlayer.Stub
3、WorkService 中创建 InnerBinder
public class WorkService extends Service {
public WorkService() {
}
@Override
public IBinder onBind(Intent intent) {
InnerBinder binder = new InnerBinder();
return binder;
}
private class InnerBinder extends IMusicPlayer.Stub {
@Override
public void play() throws RemoteException {
Log.d("AIDL", "[Server]WorkService$InnerBinder play()");
}
@Override
public void pause() throws RemoteException {
Log.d("AIDL", "[Server]WorkService$InnerBinder pause()");
}
}
}
ClientProject
4、5 MainActivity 绑定 Service
布局文件 activity_main 中增加一个 Button 用来绑定 Service,再增加两个按钮分别用来调用 AIDL 的两个方法。
<Button
android:id="@+id/btn_bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="绑定Service" />
<Button
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="调用play()" />
<Button
android:id="@+id/btn_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="调用pause()方法" />
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private View btnBindService;
private View btnBindService;
private Button btn_play;
private Button btn_pause;
private InnerServiceConnection conn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBindService = findViewById(R.id.btn_bind);
btn_play = findViewById(R.id.btn_play);
btn_pause = findViewById(R.id.btn_pause);
btnBindService.setOnClickListener(this);
btn_play.setOnClickListener(this);
btn_pause.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_bind:
Intent intent = new Intent("com.example.serviceapplication.WORK_SERVICE");
conn = new InnerServiceConnection();
bindService(intent,conn,BIND_AUTO_CREATE);
break;
case R.id.btn_play:
break;
case R.id.btn_pause:
break;
default:
break;
}
}
private class InnerServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
6、WorkService 设置为可被隐式激活
其中按钮的点击事件中,由于要激活的是 ServerProject 中的 WorkService,所以,需要在【ServiceProject】的 AndroidManifest.xml 中配置WorkService是之可以被隐式激活
<service
android:name=".WorkService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.serviceapplication.WorkService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
7、把服务端的 aidl 文件夹复制到客户端相同位置
把 ServerProject 中的 main下的 aidl 文件夹复制到 ClientProject 中同样的位置
然后运行下程序
8、9、10 Client 端 MainActivity 中代码修改
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnBindService;
private Button btn_play;
private Button btn_pause;
private InnerServiceConnection conn;
private IMusicPlayer player;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBindService = findViewById(R.id.btn_bind);
btn_play = findViewById(R.id.btn_play);
btn_pause = findViewById(R.id.btn_pause);
btnBindService.setOnClickListener(this);
btn_play.setOnClickListener(this);
btn_pause.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_bind:
Intent intent = new Intent();
intent.setAction("com.example.serviceapplication.WorkService");
intent.setPackage("com.example.serviceapplication");
conn = new InnerServiceConnection();
bindService(intent, conn, BIND_AUTO_CREATE);
break;
case R.id.btn_play:
try {
player.play();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.btn_pause:
try {
player.pause();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
private class InnerServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
player = IMusicPlayer.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
@Override
protected void onDestroy() {
//解除与service的绑定
unbindService(conn);
super.onDestroy();
}
}
首先运行【ServerProject】,确保程序安装到手机上即可。然后运行【ClientProject】,点击页面两个按钮,观察【ServerProject】日志:
可以看到,点击【ClientProject】中的按钮,调用了【ServerProject】中的方法。
如果我们在【ServerProject】中的 IMusicPlayer.aidl 中增加1个方法getDuration()
interface IMusicPlayer {
void play();
void pause();
int getDuration();
}
然后运行【ServerProject】,WorkService 的 InnerBinder 类会报错,因为我们需要重写getDuration()
private class InnerBinder extends IMusicPlayer.Stub {
......
@Override
public int getDuration() throws RemoteException {
Log.d("AIDL","[Server]WorkService$InnerBinder getDuration()-->9527");
return 9527;
}
}
这时候,【ClientProject】仍然可以调用play()和pause()方法,但是不能调用新增的getDuration()方法。因此我们必须更新【ClientProject】的 IMusicPlayer.aidl,使其跟【ServerProject】中的保持一致
为了测试,在【ClientProject】的布局中增加1个按钮测试调用这个方法
<Button
android:id="@+id/btn_getDuration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="调用getDuration()方法" />
MainActivty
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
......
private Button btn_getDuration;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
......
btn_getDuration = findViewById(R.id.btn_getDuration);
btn_getDuration.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
......
case R.id.btn_getDuration:
try {
int duration = player.getDuration();
Log.d("AIDL","[Client] getDuration()-->"+duration);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
......
}
首先运行【ServerProject】,确保安装到手机,然后运行【ClientProject】,先绑定 Service,然后点击getDuration()方法的按钮,观察Log
D/AIDL: [Server]WorkService$InnerBinder getDuration()-->9527
D/AIDL: [Client] duration:9527
报错请注意
1、点击绑定 SERVICE,5.0 以上会报错,所以上边 MainActivity 中的代码已经修改了相关绑定 Service 的代码,关于解释,具体可以看下一篇文章。
2、如果你的手机是 Android 11,MainActivity 中的 player 变量可能为空。具体解决办法,可以查看下一篇文章。