你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
首页
热门
推荐
精选
登录
|
注册
Android 进程间通信——AIDL
立即下载
用AI写一个
金额:
2
元
支付方式:
友情提醒:源码购买后不支持退换货
立即支付
我要免费下载
发布时间:2019-10-25
29人
|
浏览:7565次
|
收藏
|
分享
技术:Android4.4+jdk1.7
运行环境:Android4.4、Android7.0
概述
Android多个进程间通过AIDL进行通信。
详细
> 原文地址:http://blog.csdn.net/vnanyesheshou/article/details/79047650 AIDL(Android Interface Definition Language)——进程间通信的一种机制。它允许您定义客户端和服务端通过使用进程间通信(IPC)进行通信的编程接口。在Android上,一个进程无法正常访问另一个进程的内存。所以说,他们需要将他们的对象分解成操作系统能够理解的原语,并且把这些对象放在你的边界上。编写这些代码非常繁琐,所以Android使用AIDL来处理它。 ###1 使用AIDL的必要条件 - 只有当你需要来自**不同应用的客户端**通过IPC(进程间通信)通信来访问你的服务时,并且想在服务里处理多线程的业务,这时就需要使用AIDL。 - 如果你不需要同时对几个应用进程IPC操作,你最好通过实现Binder接口来创建你的接口。 - 如果你仍需要执行IPC操作,但不需要处理多线程,使用Messenger来实现接口即可。 ###2 AIDL的使用 使用Java编程语言语法在.aidl文件中定义您的AIDL接口,然后将其保存在承载服务的应用程序和任何其他绑定到该服务的应用程序的源代码(在src /目录中)。 当应用程序构建包含.aidl文件时,Android SDK工具将生成一个基于.aidl文件的IBinder接口,并将其保存在项目的gen /目录中。 该服务必须适当地实现IBinder接口。 然后,客户端应用程序可以绑定到服务并从IBinder调用方法来执行IPC。 使用AIDL 创建绑定的服务,具体步骤: 1. **
创建.aidl文件
** 这个文件用方法签名来定义编程接口。 2. **
实现接口
** Android SDK工具根据你的.aidl文件以Java编程语言生成一个接口 。这个接口有一个名为Stub的内部抽象类,它继承了Binder并实现了AIDL接口中的方法。你必须继承这个 Stub类并实现这些方法。 3. **
将接口公开给客户端
** 实现一个服务并重写onBind() 来返回你的Stub类的实现。 ####2.1 创建.aidl文件 AIDL使用简单的语法,可以用一个或多个方法(可以接收参数和返回值)来声明接口。参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。 必须使用Java编程语言构建.aidl文件。 每个.aidl文件都必须定义一个接口,并且只需要接口声明和方法签名。 默认情况下,AIDL支持以下数据类型: - Java编程语言中的所有基本类型(如int,long,char,boolean等) - String - CharSequence - List List中的所有元素都必须是支持的数据类型之一,或者是您声明的其他AIDL生成的接口或可接受的元素之一。 列表可以选择性地用作“通用”类(例如List
)。 对方收到的实际具体类始终是一个ArrayList,尽管生成的方法是使用List接口。 - Map Map中的所有元素都必须是此列表中受支持的数据类型之一,或者是您声明的其他AIDL生成的接口或可接受元素之一。 通用映射(如Map <String,Integer>形式的映射)不被支持。对方接收的实际具体类总是一个HashMap,尽管该方法是使用Map接口生成的。 对于上面没有列出的每种附加类型,即使它们在与接口相同的包中定义,也必须包含一条import语句。 在定义服务接口时,注意: - 方法可以采用零个或多个参数,并返回一个值或void。 - 所有非原始参数都需要一个指向数据的方向标签。in,out或者inout(见下面的例子)。基本数据默认是in的,不能以其他方式。 **警告:**您应该将方向限制在真正需要的地方,因为编组参数非常昂贵。 - 包含在.aidl文件中的所有代码注释都包含在生成的IBinder接口中(导入和包装语句之前的注释除外)。 - 只支持方法; 您不能在AIDL中公开静态字段。 如下是一个.aidl 例子。IRemoteService.aidl ``` package com.zpengyong.aidl; interface IRemoteService { void sendMessage(in String str); boolean play(); boolean pause(); boolean stop(); } ``` 只需将.aidl文件保存在项目src/目录中,SDK工具会在项目gen/目录中生成IBinder接口文件。生成的文件名与.aidl文件名相匹配,但带有.java扩展名(例如IRemoteService.aidl结果IRemoteService.java)。 ####2.2 实现接口 IRemoteService.java接口文件包含一个名为Stub的类 ,它继承了Binder ,实现了IRemoteService接口,并声明.aidl文件中的所有方法。 Stub还定义了一些辅助方法,最值得注意的是asInterface(),它接受一个IBinder(通常是传递给客户端的onServiceConnected()回调方法中的参数),并返回stub接口的一个实例。 要实现从.aidl生成的接口,请继承生成的Binder接口(例如IRemoteService.Stub),并实现从.aidl文件继承的方法。 下面是示例: ``` private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){ public void sendMessage(String str){ Log.i(TAG,"message str:"+str +",thread:"+Thread.currentThread()); Message msg = new Message(); msg.what = MSG_RECEIVE_MESSAGE; msg.obj = str; mHandler.sendMessage(msg); } public boolean play(){ mService.play(); return true; } public boolean pause(){ mService.pause(); return true; } public boolean stop(){ mService.stop(); return true; } }; ``` 现在mBinder是Stub类的一个实例(一个Binder),它定义了服务的RPC接口。 在下一步中,这个实例被暴露给客户,以便他们可以与服务交互。
在实现AIDL接口时,您应该注意一些规则
: - 传入的调用并不保证在主线程中执行,所以需要从头开始考虑多线程,并将服务正确地构建为线程安全的。 - 默认情况下,RPC调用是同步的。如果您知道该服务需要超过几毫秒才能完成请求,则不应该从活动的主线程调用该服务,因为它可能会挂起应用程序(Android可能会显示“应用程序不响应”对话框,应该通常从客户端的一个单独的线程调用它们。 - 抛出的任何异常都将被发回给调用者。 ####2.3 将接口公开给客户端 为了暴露你的服务的接口,扩展Service并实现onBind()返回实现生成的Stub的类的实例。 这里是一个示例服务,将IRemoteService示例接口公开给客户端。 ``` public class AIDLService extends Service { @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { // Return the interface return mBinder; } private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){ public void sendMessage(String str){ Log.i(TAG,"message str:"+str +",thread:"+Thread.currentThread()); Message msg = new Message(); msg.what = MSG_RECEIVE_MESSAGE; msg.obj = str; mHandler.sendMessage(msg); } public boolean play(){ mService.play(); return true; } public boolean pause(){ mService.pause(); return true; } public boolean stop(){ mService.stop(); return true; } }; } ``` 现在,当一个客户端(比如一个activity)调用bindService()连接到这个服务时,客户端的onServiceConnected()回调会收到mBinder(服务onBind() 方法返回的 实例)。 客户端还必须能够访问接口类,所以如果客户端和服务在不同的应用程序中,那么客户端的应用程序必须在其src/目录中拥有该.aidl文件的副本(这会生成android.os.Binder 接口 - 为客户端提供对AIDL方法的访问)。 当客户端收到onServiceConnected()回调,得到IBinder,它必须调用 IRemoteService.Stub.asInterface(service)转换成IRemoteService类型。例如: ``` private IRemoteService mIRemoteService; private ServiceConnection mConnection = new ServiceConnection() { // 当与服务端连接成功时,回调该方法。 @Override public void onServiceConnected(ComponentName name, IBinder service) { //转换 mIRemoteService = IRemoteService.Stub.asInterface(service); } // 当与服务端连接异常断开时,回调该方法。 @Override public void onServiceDisconnected(ComponentName name) { mIRemoteService = null; } }; ``` ### 3 调用IPC方法 以下是调用类必须用来调用AIDL定义的远程接口的步骤: 1. 将.aidl文件包含在项目src /目录中。 2. 声明一个IBinder接口的实例(基于AIDL生成)。 3. 实现**ServiceConnection.** 4. 调用**Context.bindService()**,传入你的ServiceConnection实现。 5. 在onServiceConnected()实现中,将收到一个IBinder实例。 调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回的参数强制转换为YourInterfaceName类型。 6. 调用你在接口上定义的方法。 您应该始终捕获连接断开时引发的DeadObjectException异常; 这将是远程方法抛出的唯一异常。 7. 要断开连接,调用Context.unbindService()。 如下: ``` package com.zpengyong.aidlclient; import com.zpengyong.aidl.IRemoteService; import com.zpengyong.aidl.IRemoteServiceCallback; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.text.Editable; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener { private final static String TAG = "MainActivity"; private TextView mStateText, mMusicState; private Button mBtnHello, mBtnBind, mBtnStart, mBtnPause, mBtnStop; private EditText mTextMessage; private IRemoteService mIRemoteService; private final int STATE_DISCONNECTED = 1; private final int STATE_CONNECTING = 2; private final int STATE_CONNECTED = 3; private final int STATE_DISCONNECTING = 4; //与服务端的连接状态 private int mBindState = STATE_DISCONNECTED; private ServiceConnection mConnection = new ServiceConnection() { // 当与服务端连接成功时,回调该方法。 @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG, "onServiceConnected"); mIRemoteService = IRemoteService.Stub.asInterface(service); mStateText.setText("connected"); mBindState = STATE_CONNECTED; mBtnBind.setText("解绑"); try { mIRemoteService.registerCallback(mIRemoteServiceCallback); } catch (RemoteException e) { e.printStackTrace(); } } // 当与服务端连接异常断开时,回调该方法。 @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG, "onServiceDisconnected"); mIRemoteService = null; mStateText.setText("disconnected"); mBindState = STATE_DISCONNECTED; mBtnBind.setText("绑定"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mStateText = (TextView) findViewById(R.id.connectState); mBtnHello = (Button) findViewById(R.id.sendMessage); mBtnBind = (Button)findViewById(R.id.bind); mBtnStart = (Button)findViewById(R.id.start_play); mBtnPause = (Button)findViewById(R.id.pause); mBtnStop = (Button)findViewById(R.id.stop_play); mBtnHello.setOnClickListener(this); mBtnStart.setOnClickListener(this); mBtnPause.setOnClickListener(this); mBtnStop.setOnClickListener(this); mBtnBind.setOnClickListener(this); mTextMessage = (EditText) findViewById(R.id.message); mMusicState = (TextView)findViewById(R.id.musicState); } private void bind() { mBindState = STATE_CONNECTING; Intent intent = new Intent(); // Android 5.0 以上显示绑定服务 intent.setComponent(new ComponentName("com.zpengyong.aidl", "com.zpengyong.aidl.AIDLService")); // 绑定服务 this.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); mStateText.setText("connecting"); } private void unbind() { mBindState = STATE_DISCONNECTING; try { mIRemoteService.unregisterCallback(mIRemoteServiceCallback); } catch (RemoteException e) { e.printStackTrace(); } mStateText.setText("disconnecting"); //解除与Service的连接 unbindService(mConnection); mBindState = STATE_DISCONNECTED; mStateText.setText("disconnected"); mBtnBind.setText("绑定"); mIRemoteService = null; } @Override protected void onDestroy() { super.onDestroy(); if(mBindState != STATE_DISCONNECTED){ unbind(); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.sendMessage: String str = mTextMessage.getText().toString(); if(str == null ||str.length() == 0) return; if(mIRemoteService == null) return; try { mIRemoteService.sendMessage(str); } catch (RemoteException e1) { e1.printStackTrace(); } break; case R.id.bind: if(mBindState == STATE_DISCONNECTED){ bind(); }else if(mBindState == STATE_CONNECTED){ unbind(); } break; case R.id.start_play: if(mIRemoteService == null) return; try { boolean ret = mIRemoteService.play(); Log.i(TAG, "play ret="+ret); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.pause: if(mIRemoteService == null) return; try { boolean ret = mIRemoteService.pause(); Log.i(TAG, "pause ret="+ret); } catch (RemoteException e) { e.printStackTrace(); }; break; case R.id.stop_play: if(mIRemoteService == null) return; try { boolean ret = mIRemoteService.stop(); Log.i(TAG, "stop ret="+ret); } catch (RemoteException e) { e.printStackTrace(); } break; default: break; } } } ``` 效果图如下: ![这里写图片描述](/contentImages/image/rep/ff6xzoZmsaVPsGR4VyY.jpg) ###4 服务端回调客户端 如上的列子中只有客户端调用服务端的方法,并不能服务端调用客户端。 在之前的IRemoteService.aidl文件中添加接口 ``` package com.zpengyong.aidl; import com.zpengyong.aidl.IRemoteServiceCallback; interface IRemoteService { void registerCallback(in IRemoteServiceCallback cb); void unregisterCallback(in IRemoteServiceCallback cb); void sendMessage(in String str); boolean play(); boolean pause(); boolean stop(); } ``` IRemoteServiceCallback.aidl中添加服务端调用客户端的接口。 该文件服务度和客户端都需要包含该文件。 ``` package com.zpengyong.aidl; interface IRemoteServiceCallback { void stateChange(int value); } ``` 1 客户端实现回调接口 要实现从IRemoteServiceCallback.aidl生成的接口,请继承生成的Binder接口(IRemoteServiceCallback.Stub),并实现从IRemoteServiceCallback.aidl文件继承的方法。 ``` private IRemoteServiceCallback mIRemoteServiceCallback = new IRemoteServiceCallback.Stub() { @Override public void stateChange(int value) throws RemoteException { Log.i(TAG, "stateChange value="+value); if(value == 1){ mMusicState.setText("开始播放"); }else if(value == 2){ mMusicState.setText("暂停播放"); }else if(value == 3){ mMusicState.setText("停止播放"); }else if(value == 4){ mMusicState.setText("播放出错"); }else { mMusicState.setText("unknown"); } } }; ``` 2 注册回调 客户端bindservice成功后会回调onServiceConnected,客户端可以获取到mIRemoteService,可以调用远端的放,这时可以通过调用远端方法注册回调接口实例。 ``` private ServiceConnection mConnection = new ServiceConnection() { // 当与服务端连接成功时,回调该方法。 @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG, "onServiceConnected"); mIRemoteService = IRemoteService.Stub.asInterface(service); mStateText.setText("connected"); mBindState = STATE_CONNECTED; mBtnBind.setText("解绑"); try { //注册回调。 mIRemoteService.registerCallback(mIRemoteServiceCallback); } catch (RemoteException e) { e.printStackTrace(); } } } ``` 3 服务端保存回调接口 由于AIDl支持多个客户端绑定,并处理并发请求。所以这里要将回调接口存到列表中,避免后注册的将前面注册的回调接口覆盖。 ``` //aidl支持多个客户端绑定,并且处理并发进程间通信,所以这里要存列表中。 final RemoteCallbackList
mCallbackList = new RemoteCallbackList
(); private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){ public void registerCallback(IRemoteServiceCallback cb){ if(cb != null)mCallbackList.register(cb); } public void unregisterCallback(IRemoteServiceCallback cb){ if(cb != null)mCallbackList.unregister(cb); } 。。。 } ``` 4 服务器调用客户端方法 遍历回调list,分别调用其stateChange方法,实现服务端调用客户端,实现双方通信。 ``` private void callstateChange(int value){ //遍历保存的IRemoteServiceCallback,发送状态改变的消息。 int num = mCallbackList.beginBroadcast(); for(int i=0; i
本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
感谢
5
手机上随时阅读、收藏该文章 ?请扫下方二维码
相似例子推荐
评论
作者
zpengyong
15
例子数量
3604
帮助
125
感谢
评分详细
可运行:
4.5
分
代码质量:
4.5
分
文章描述详细:
4.5
分
代码注释:
4.5
分
综合:
4.5
分
作者例子
基于FFmpeg的音频编码(PCM数据编码成AAC android)
Android基于TCP的局域网聊天通信
Android 进程间通信——Service、Messenger
AsyncTask 异步任务基本使用-下载视频
Android IntentService使用
Android HandlerThread详解
Eclipse集成ijkplayer并实现本地和网络视频播放等
Android WiFi开发
Android -传统蓝牙通信聊天
Android 进程间通信——AIDL
Android -BLE蓝牙小DEMO
Android蓝牙——HID开发
Android8.0及以上系统 WiFi热点 版本适配
Android WiFi直连 双向通信
Android蓝牙A2DP连接实现