使用AIDL,客户端调用和Service回调,以及一些需要注意的细节
来源:互联网 发布:mac怎么关闭软件 编辑:程序博客网 时间:2024/06/06 14:32
前言
- 模拟一个简单的音乐播放demo(未实现真实的音乐播放器),使用
AIDL
和Service
进行跨进程通讯,并总结。 - 本文资料来源网络公开资源,并根据个人实践纯手打整理,如有错误请随时指出。
- 本文主要用于个人积累及分享,文中可能引用其他技术大牛文章(仅引用链接不转载),如有侵权请告知必妥善处理。
AIDL介绍
AIDL
和Java
比较类似,但更简单,因为AIDL
只是作为两个原本互不影响的进程之间相互“沟通”的工具,基本只做数据的传递,不使用AIDL做逻辑处理。AIDL
支持的数据类型有:- 基本类型:
byte、short、int、long、float、double、boolean、char
- Java常用数据类型:
String、CharSequence、List、Map
.aidl
文件的interface类型(接口类,用于进程间互相调用、回调).aidl
文件的parcelable类型(数据类,需要配合同包路径的.java
数据类,这个.java
数据类需要实现Parcelable
序列化)
特别说明:
String、CharSequence类型的参数的定向tag默认为in,并且只能是 in- 基本类型:
AIDL
文件一般有两大类:数据类,接口类(包含两类:交互接口,回调接口)
AIDL使用和配置
自定义AIDL文件的存放路径,如
src/main/java/
包下任意位置,创建一个aidl
文件夹并任意创建一个
.aidl
文件在
build.gradle
中配置aidl.srcDirs
在aidl文件夹中,任意创建一个
aidl
文件,并点击Android Studio
工具栏“Build–>Make Project”或“clean Project”,等候编译完成,将会在app\build\generated\source\aidl\debug\
下自动生成对映的.java
文件,此文件请勿做任何修改
至此,AIDL文件已可正常编译
示例分析
模拟一个简单的音乐播放demo,但这里不会真正得实现音乐播放功能,只是实现App进程(客户端)
和单独的Service进程(播放服务端)
之间的交互和回调。
AIDL
AIDL数据文件
在aidl包中创建一个数据文件Music.aidl
package com.sp.personal.music.aidl;parcelable Music;
在同包内创建Music.java文件,Music类必须implements Parcelable。
- 目前比较好用的
Parcelable
自动生成代码插件可通过“File–>Settings–>Plugins–>Browse repositories…”,输入搜索“Android Parcelable code generator”,安装即可。 - 这里要注意的是,
Music.java
需要和Music.aidl
在同一个包里 Music.java
文件就不贴代码了,比较通常的一个数据类。
AIDL接口以及回调文件
AIDL回调文件
供Service
进程(播放服务端)回调的接口
定义一个PlayCallback.aidl
文件
package com.sp.personal.music.aidl;interface PlayCallback { //state:1,onPrepare;2,onStart;3,onPlaying;4,onPause;5,onEnd void stateNow(int state, String songId, int position);}
AIDL接口文件
供App进程(客户端)调用的接口AIDLPlayService.aidl
package com.sp.personal.music.aidl;import com.sp.personal.music.aidl.Music;import com.sp.personal.music.aidl.PlayCallback;interface AIDLPlayService{ void prepare(in Music music); void start(); void seekTo(String id, int position); void pause(String id); void addPlayCallback(PlayCallback playCallback); void removePlayCallback(PlayCallback playCallback);}
这里需要注意的是:
* import,是必须的,无论是否在同一个包中
* 自定义类型需要指定其参数“定向”,这里Music music
实例由App进程(客户端)
传递过来,则指定为in
服务端(Service)
用于播放音乐的单独进程的Service
,以及绑定AIDL
接口到Service
提供一个Service,并指定这个Service在单独的进程执行:
<service android:name=".code.playctrl.PlayService" android:process=":remote"/>
(使用remote
是自定义的,可以随意。冒号:
这个前缀是必须的,它将把这个名字附加到你的包所运行的标准进程名字的后面作为新的进程名称)
PlayService.java
public class PlayService extends Service { @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { //将AIDLPlayService作为IBinder返回 return mIBinder; } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { super.onRebind(intent); } @Override public void onDestroy() { super.onDestroy(); } //创建一个RemoteCallbackList用来管理PlayCallback,用来通过Broadcast发送回调到客户端 RemoteCallbackList<PlayCallback> remoteCallbackList = new RemoteCallbackList<>(); //服务端用来接收客户端的请求 AIDLPlayService.Stub mIBinder = new AIDLPlayService.Stub() { @Override public void prepare(Music music) throws RemoteException { //接收到准备播放的音乐对象 } @Override public void start() throws RemoteException { //接收到客户端的请求,开始播放 } @Override public void pause(String id) throws RemoteException { //接收到客户端的请求,暂停播放 //暂停播放的测试:暂停后客户端将收到已暂停的回调通知 pausePlay(id); } @Override public void addPlayCallback(PlayCallback playCallback) throws RemoteException { if (playCallback != null) { //客户端设置回调,这里使用RemoteCallbackList注册这个PlayCallback remoteCallbackList.register(playCallback); } } @Override public void removePlayCallback(PlayCallback playCallback) throws RemoteException { if (playCallback != null) { //客户端注销回调,这里使用RemoteCallbackList注销这个PlayCallback remoteCallbackList.unregister(playCallback); } } @Override public void seekTo(String id, int position) throws RemoteException { //接收到客户端的请求,播放到指定位置(或时间) } }; //举例,测试回调,将在客户端代码中打印 private void pausePlay(String id) { //假如已知目前播放的位置是:position=100(秒) int position = 100; playCallback(4, id, position); } /** *调用回调到客户端 * @param state 1,onPrepare;2,onStart;3,onPlaying;4,onPause;5,onEnd * @param songId * @param position */ private void playCallback(int state, String songId, int position) { //准备开始调用最近注册的Callback并且返回,已注册playCallback数(在客户端注册),目前是1个 int N = remoteCallbackList.beginBroadcast(); for (int i = 0; i < N; i++) { try { remoteCallbackList.getBroadcastItem(i).stateNow(state, songId, position); } catch (RemoteException e) { e.printStackTrace(); } } //必须的,和beginBroadcast()成对出现 mPlayCallback.finishBroadcast(); }}
客户端(App进程)
Application中启动Service,并获取到AIDLPlayService对象
public class PMApplication extends Application { public static AIDLPlayService aidlPlayService; @Override public void onCreate() { super.onCreate(); initPlayService(); } private void initPlayService() { Intent intent = new Intent(this, PlayService.class); this.startService(intent); this.bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { aidlPlayService = AIDLPlayService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { aidlPlayService = null; } }, Context.BIND_AUTO_CREATE); }}
Activity(或其他View)添加回调,并调用pause
方法测试回调
try { PMApplication.aidlPlayService.addPlayCallback(new PlayCallback.Stub() { @Override public void stateNow(int state, String songId, int position) throws RemoteException { //测试是否收到回调,打印 Log.d("Play","state=" + state + ";songId=" + songId + ";position=" + position); } }); PMApplication.aidlPlayService.pause("123456");} catch (RemoteException e) { e.printStackTrace();}
- 切记
addPlayCallback
只能调用一次,如不需要使用该回调时,需要注销这个回调removePlayCallback
,否则可能导致回调的信息混乱或之前已完成的回调信息再次回调。其中的原因是RemoteCallbackList
中的消息机制,允许注册多个回调并存(请见RemoteCallbackList
源码)。
结语
以上经过测试,服务端可以收到客户端的请求,并可以回调给客户端数据,已完成了基本的跨进程AIDL
数据交互。如一般的音乐播放器完全可以按照这样的交互流程进行处理。
- 使用AIDL,客户端调用和Service回调,以及一些需要注意的细节
- 使用css需要注意的一些细节
- 关于AIDL一些需要注意的地方
- lua 一些需要注意的细节
- C++ 一些需要注意的细节
- C++ 一些需要注意的细节
- Javascript - 一些需要注意的细节
- nginx一些需要注意的细节
- Json对象需要注意的一些细节。
- SolrCloud 译文以及一些使用注意细节
- 使用redis缓存数据需要注意的问题以及个人的一些思考和理解
- ~ 使用redis缓存数据需要注意的问题以及个人的一些思考和理解
- 使用redis缓存数据需要注意的问题以及个人的一些思考和理解
- 使用Spring的Service注入Dao接口需要注意的细节问题
- 使用Spring的Service注入Dao接口需要注意的细节问题
- Jquery中的一些细节(一)--------------->Jquery加载调用多个函数(方法)需要注意的细节
- 使用OpenCV需要注意的小细节
- JavaScript使用需要注意的细节
- Java学习路线图
- codeforces 850B Arpa and a list of numbers
- mysql学习----索引
- jquery $.ajax status为200 却调用了error方法
- ION框架学习(一)
- 使用AIDL,客户端调用和Service回调,以及一些需要注意的细节
- Java获取代理地址和端口
- 利用生产者消费者模式实现HTTP接口的异步调用
- 使用openlayers 3 在线加载天地图及GeoServer发布的地图
- linux下mysql开启远程访问权限及防火墙开放3306端口
- Jenkins+git+webhook自动触发部署和测试任务
- matlab2c使用c++实现matlab函数系列教程-harmmean函数
- Android studio 错误: 程序包 不存在
- MySQL索引实战经验总结