文章目录
- 问题1:Service Intent must be explicit
- 问题2:AIDL连接不上
- 问题3:AIDL找不到自定义类
- AIDL支持的默认数据类型
- AIDL中使用自定义数据类型
- 资源下载
学习【达内课程】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() {
}
public IBinder onBind(Intent intent) {
InnerBinder binder = new InnerBinder();
return binder;
}
private class InnerBinder extends IMusicPlayer.Stub {
public void play() throws RemoteException {
Log.d("AIDL", "[Server]WorkService$InnerBinder play()");
}
public void pause() throws RemoteException {
Log.d("AIDL", "[Server]WorkService$InnerBinder pause()");
}
public int getDuration() throws RemoteException {
Log.d("AIDL", "[Server]WorkService$InnerBinder getDuration()-->9527");
return 9527;
}
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文件
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 {
......
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 类复制到客户端。
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方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
......
btnGetMusic = findViewById(R.id.btn_getMusic);
......
btnGetMusic.setOnClickListener(this);
}
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()
方法的按钮
观察日志
资源下载
源码下载