文章目录

学习​​【达内课程】AIDL(上)​​中我们遇到了几个坑。这里来说一下。

问题1:Service Intent must be explicit

​解决办法​

最开始我们在【ClientProject】中绑定 Service 中代码是这样写的:

Intent intent = new Intent("com.example.serviceapplication.WORK_SERVICE");
conn = new InnerServiceConnection();
bindService(intent,conn,BIND_AUTO_CREATE);

运行程序,点击绑定按钮就会这个错,所以根据解决办法,我们改为:

Intent intent = new Intent();
intent.setAction("com.example.serviceapplication.WorkService");
intent.setPackage("com.example.serviceapplication");
conn = new InnerServiceConnection();
bindService(intent, conn, BIND_AUTO_CREATE);

其中​​setAction()​​ 后的字符串是【ServerProject】中 AndroidManifest.xml 中的字符串相同。

其中 ​​setPackage()​​ 后边的字符串是 【ServerProject】的包名。

问题2:AIDL连接不上

如果你的手机是 Android 11,【ClientProject】MainActivity 中的 ​​player​​​ 变量可能为空。也就是点击绑定 Service 的时候,如果你 debug 会发现​​InnerServiceConnection​​​类中的​​onServiceConnected​​方法始终没走。

解决办法1:改成 api 29

解决办法2:

如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,并且需要与应用(自动可见的应用除外)交互,请在您应用的清单文件中添加 ​​<queries>​​​ 元素。在 ​​<queries>​​ 元素中,按软件包名称、按 intent 签名或按提供程序授权指定其他应用

在 client 端 AndroidManifest.xml 添加 queries 包。例如咱们的这个项目,就需要在【ClientProject】的 AndroidManifest.xml 中添加

<queries>
<package android:name="com.example.serviceapplication" />
</queries>

后边 ​​name​​ 的值就是 【ServerProject】的包名。

问题3:AIDL找不到自定义类

这个问题在下面自定义类的时候会遇到。解决办法:
在 app 下的 ​​​build.gradle​​​ 中 ​​android​​ 的节点下添加

sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}

ServerProject 和 ClientProject 都需要添加。

解决了这些问题,我们接着学习 AIDL 的其他内容。

AIDL支持的默认数据类型

AIDL 接口中默认支持的数据类型有
1、除了​​​short​​​之外的基本数据类型
2、​​​String​​​和​​CharSequence​​​ 3、​​List​​和​​Map​

AIDL中使用自定义数据类型

如果需要在 AIDL 中使用自定义的数据类型,必须实现​​Parcelable​​接口

开发步骤
1、自定义类,实现 ​​​Parcelable​​​ 接口,例如 Music.java
2、创建 Music.java 对应的​​​aidl​​​文件,即​​Music.aidl​​​,使用与 Music.java 相同的 ​​package​​​ 语句声明所在包,并使用​​parcelable Music;​​语法声明这是一个 Parcelable 类型的数据

在使用 Music 类型的 ​​aidl​​​ 接口文件中,使用​​import​​​语句显式的导包,导入 Music 类型,注意:无论​​aidl​​接口文件与 Music 是否在同一个包中,都必须导包。

3、将服务端的 aidl 接口文件、Music 的 java 文件、Music 的 aidl 全部复制到客户端

举个栗子
例如,我们要支持自定义的 Music 类。我们按照上面的步骤开发。

1、我们自定义一个Music类,并实现 ​​Parcelable​​接口

我们把这个类放到和 IMusicPlayer.aidl 同级的目录下。

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()");
}

@Override
public int getDuration() throws RemoteException {
Log.d("AIDL", "[Server]WorkService$InnerBinder getDuration()-->9527");
return 9527;
}

@Override
public Music getMusic() throws RemoteException {
Music music = new Music();
music.title = "Stronger";
music.artist = "Glee";
music.duration = 203000;
return music;
}
}
}

2、创建 Music.java 对应的​​aidl​​​文件:​​Music.aidl​

创建IMusic.aidl文件
【达内课程】AIDL(下)_java

package com.example.serviceapplication;

parcelable Music;

在 IMusicPlayer.aidl中,导包,导入 Music.aidl。写一个返回值为 Music 的方法进行测试。

// IMusicPlayer.aidl
package com.example.serviceapplication;

import com.example.serviceapplication.Music;

interface IMusicPlayer {
void play();
void pause();
int getDuration();
Music getMusic();
}

运行程序会报错,因为 WorkService 中需要重写新的方法

private class InnerBinder extends IMusicPlayer.Stub {
......

@Override
public Music getMusic() throws RemoteException {
Music music = new Music();
music.title = "Stronger";
music.artist = "Glee";
music.duration = 203000;
return music;
}
}
}

3、把【ServerProject】中的 IMusicPlayer.aidl、Music.aidl、Music 类复制到客户端。
【达内课程】AIDL(下)_AIDL_02
activity_main 中增加1个按钮用来测试 getMusic() 这个方法。

<Button
android:id="@+id/btn_getMusic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="调用getGetMusic()方法" />

MainActivity

private View btnGetMusic;//调用服务端的getMusic方法

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

......
btnGetMusic = findViewById(R.id.btn_getMusic);
......
btnGetMusic.setOnClickListener(this);
}

@Override
public void onClick(View view) {
switch (view.getId()) {
......
case R.id.btn_getMusic:
try {
Music music = player.getMusic();
Log.d("AIDL","[Client] getMusic()-->歌曲名:"+music.title+";演唱者:"+music.artist+";歌曲时长;"+music.duration);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}

运行 Server 和 Client,点击绑定 Service 按钮,然后点击​​getMusic()​​​方法的按钮
观察日志
【达内课程】AIDL(下)_android_03

资源下载

​源码下载​