Android基础笔记(十二)- 使用AIDL来进行跨进程通信
来源:互联网 发布:mac版chrome翻墙插件 编辑:程序博客网 时间:2024/06/01 08:02
- 绑定服务调用服务里方法的过程
- 音乐盒小案例
- 利用服务注册特殊广播接收者
- 使用AIDL来进行跨进程通信
绑定服务调用服务里方法的过程
整个Activty绑定Service并调用其中方法的过程可以体现为下面的一张图,其中的核心是通过借助中间人IBinder
来达到调用Service中方法的目的。。
- 接下来在明确一下调用过程的代码步骤:
- ①首先服务里有一个方法需要被调用
- ②定义一个中间人对象(继承
Bidner
类的内部类MyBinder
) - ③在
onBind
方法中把我们自己定义的中间人返回MyBinder
- ④在Activity里面调用
bindService()
方法,获取我们的中间人对象 - ⑤间接利用中间人对象调用服务里面的方法。
音乐盒小案例
既然我们已经清楚了,在Activity如何调用Service中的方法,那么就练习一下吧。
在我们日常中,都使用过音乐APP,它们都可以在后台中继续播放音乐,那么我们也模仿一下,当然其中的播放、暂停、继续播放功能都用Log来代替。
另外,在之前的代码中,在ServiceConnection
的onServiceConnected()
方法中,每次都是把参数service
,向下转型为Service内部的MyBinder
对象,这样灵活性又不高、耦合性又强,所以采用接口的方式进行解耦。
整体而言还是非常简单的,我们来看下代码。
为了更方便的调用服务里的方法,我们抽取了一个接口:
public interface IService { // 调用服务中的play方法 public void callPlay(); // 调用服务中的pause方法 public void callPause(); // 调用服务中的replay方法 public void callReplay();}
在Service的内部,有着play、pause、replay等三个内部方法,创建一个私有的内部类,继承Binder并实现IService,然后在onBind()
方法中,返回此对象。
public class MusicService extends Service { @Override public IBinder onBind(Intent intent) { return new MyBinder(); } public void play() { System.out.println("播放音乐"); } public void pause() { System.out.println("暂停播放"); } public void replay() { System.out.println("继续播放音乐"); } private class MyBinder extends Binder implements IService { @Override public void callPlay() { // 调用服务内部的方法 play(); } @Override public void callPause() { pause(); } @Override public void callReplay() { replay(); } }}
在MainActivity中也是比较简单,我们直接在onCreate()
开启服务并绑定服务,并通过按钮调用的相应IServiec
接口中的方法。
public class MainActivity extends Activity { private ServiceConnection conn; private IService iService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent service = new Intent(this, MusicService.class); // 启动服务 startService(service); // 绑定服务 conn = new MyServiceConnection(); bindService(service, conn, Context.BIND_AUTO_CREATE); } public void play(View v) { iService.callPlay(); } public void pause(View v) { iService.callPause(); } public void replay(View v) { iService.callReplay(); } private class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { iService = (IService) service; } @Override public void onServiceDisconnected(ComponentName name) { } }}
测试结果如下:
看到这里你可能就有疑问了,与之前没有什么太大的区别呀,唯一的区别就是使用了IService
接口解除了Activity和Service通信的耦合。
不错,这就是关键点。这里要说明一个概念,远程服务和本地服务的区别。
在Android中,远程服务就是运行在其他应用(进程)里面的服务 ,而本地服务就是运行在自己应用(进程)里面的服务。它们都是运行在同一个手机里。
这就涉及到了IPC进程间通信了,Google已经为我们提供了解决进程间通信的方法,叫做AIDL,而其中就是使用类似IService
的接口来达到目的的。更为详细的在下面讲述。
利用服务注册特殊广播接收者
是时候把服务和广播结合起来了,这样功能才会更强大。而就广播而言,这里用到了动态注册广播的方法,说白了就是通过代码注册一个广播。
我们可以使用registerReceiver()
方法动态注册一个广播;另一个方面,什么才叫做特殊的广播接受者呢?一般来说,像电池电量低,屏幕锁屏等使用频率非常高的称为特殊广播接受者,它们只能够使用代码的方式进行注册。
那为什么这些广播需要在服务中进行动态注册呢? 试想一下,我们在普通应用中直接注册一个屏幕锁屏的广播接收者,当这个应用开启时肯定可以接收到屏幕锁屏的广播事件的。但是,当应用程序退出后,应用变成空进程,也就接收不到广播了。而在服务中动态注册屏幕锁屏事件,只要我们的Service不死,那么便一直可以接收到广播。
接下来就做一个在服务中动态注册锁屏广播事件的小案例。
还是先从界面开始,两个按钮,并在MainActivity中实现了对应的点击方法。
对应的代码也很简单:
public void start(View v) { Intent service = new Intent(this, ScreenService.class); startService(service);}public void stop(View v) { Intent service = new Intent(this, ScreenService.class); stopService(service);}
接下来写一个接收广播的类,既然我们打算在服务中动态进行注册,那么便不需要再清单文件中声明了。
public class ScreenReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if ("android.intent.action.SCREEN_OFF".equals(action)) { System.out.println("接收到锁屏广播"); }else if ("android.intent.action.SCREEN_ON".equals(action)) { System.out.println("接收到解锁广播"); } }}
接下来创建一个服务,并在服务中动态注册广播接收者。
public class ScreenService extends Service { private BroadcastReceiver receiver; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); // 接收广播的对象 receiver = new ScreenReceiver(); // 意图过滤器,添加关心的动作 IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.SCREEN_OFF"); filter.addAction("android.intent.action.SCREEN_ON"); // 注册广播 registerReceiver(receiver, filter); System.out.println("注册了屏幕锁屏的广播接收者"); } @Override public void onDestroy() { super.onDestroy(); // 解除注册 unregisterReceiver(receiver); System.out.println("反注册了屏幕锁屏的广播接收者"); }}
至此,整个小案例的操作就完毕了。看一下测试图:
使用AIDL来进行跨进程通信
在上面我们已经说过远程服务和本地服务的区别,那么如何才能让Activity与一个远程Service建立关联呢?这就要使用AIDL来进行跨进程通信了(IPC - Inter Process Communication 进程间通信)。
而AIDL(Android Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
说话可能太苍白,还是用一个示例来演示吧。我们平常都玩过手机斗地主,当我们买欢乐豆是,可能会用到支付宝的服务。
那么我们就用买欢乐豆使用支付宝支付,来作为我们的小案例吧。
既然是远程服务,也就是不同进程间通信,我们需要创建两个Android工程,一个工程名叫支付宝,另一个叫斗地主。支付宝是服务端,我们先从服务端开始。
在支付宝工程中,我们先像之前的例子一样,创建一个服务,写一个继承Binder的内部类,并使用onBind()
方法,返回它的实例。然后再向服务中添加一个pay()
的方法。既然我们调用服务内部的方法,为了解耦方便,我们再抽取出一个接口IService
,里面有一个callPay
方法。AliPayService代码如下:
public class AliPayService extends Service { @Override public IBinder onBind(Intent intent) { // 返回Binder实例 return new MyBinder(); } // 用于支付的方法 public boolean pay(String name, String password, int money) { if ("biezhihua".equals(name) && "biezhihua".equals(password) && money > 4000) { return true; } return false; } // 内部类 private class MyBinder extends Binder implements IService { @Override public boolean callPay(String name, String password, int money) { return pay(name, password, money); } }}
抽取出来的IService接口代码如下:
public interface IService { public boolean callPay(String name, String password, int money);}
步骤到这里,还和之前一点区别都没有。既然我们这个服务要提供给外部使用,就需要用到Google给我们提供的AIDL技术。接下来我们一步一步的将原始的本地服务,修改成可远程调用的服务。
先将我们IService.java
文件修改为IService.aidl
并将其中的权限修饰符去掉。代码如下:
package com.bzh.alipay;interface IService { boolean callPay(String name, String password, int money);}
修改后的效果如图所示,你会发现当我们修改完成后,eclipse帮我们自在的在gen
的com.bzh.alipay
包下生成了一个IService.java
文件。
我们打开看看,在第二行看到了这样一句话This file is auto-generated. DO NOT MODIFY.,它是自动生成的,不要去修改它。
我们已经把接口修改成AIDL文件了,那么我们的AliPayService
类中的内部类MyBinder
也要随之改变,改为继承Stub
,这个Stub
就是自动生成的IService.java
中的一个内部类。
修改后的代码如下,区别也不是很大:
private class MyBinder extends Stub { @Override public boolean callPay(String name, String password, int money) throws RemoteException { return pay(name, password, money); }}
最后,既然我们的服务需要提供给别人调用,那么便需要使用到隐式意图了,在清单文件中为服务配一个隐式意图,这样我们就可以在其他工程中触发这个服务了,代码如下:
<service android:name="com.bzh.alipay.AliPayService" > <intent-filter> <action android:name="com.bzh.pay" > </action> </intent-filter> </service>
接下来到斗地主工程中,为了使用远程服务,我们需要把AIDL文件和其所在包整个都拷贝过来,拷贝过来后,一会在gen
中自动生成相应的代码。效果如下:
最后,我们需要在MainActivity中响应按钮,调用远程服务内部的方法了。其中,比较重要的两步分别是:
- 指定绑定服务时意图过滤器的动作为
com.bzh.pay
- 在MyServiceConnection的
onServiceConnected()
方法中,使用IService.Stub.asInterface(service)
语句将IBinder
转换成AIDL接口,方便在pay()
方法中调用远程服务的内部方法。
代码如下:
public class MainActivity extends Activity { ServiceConnection conn; private IService iService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 指定所绑定服务的隐式意图动作 Intent service = new Intent(); service.setAction("com.bzh.pay"); conn = new MyServiceConnection(); // 绑定服务 bindService(service, conn, Context.BIND_AUTO_CREATE); } public void pay(View v) { try { boolean callPay = iService.callPay("biezhihua", "biezhihua", 6000); if (callPay) { Toast.makeText(getApplicationContext(), "支付成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "支付失败", Toast.LENGTH_SHORT).show(); } } catch (RemoteException e) { e.printStackTrace(); } } private class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { iService = IService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } }}
测试图如下:
谢谢各位观赏!
- Android基础笔记(十二)- 使用AIDL来进行跨进程通信
- 使用AIDL来进行跨进程通信
- Android:使用AIDL来进行跨进程通信
- 【Android学习之路】使用AIDL进行跨进程通信
- Android AIDL跨进程通信基础
- 【Android开发艺术探索】IPC机制(四)-使用AIDL进行跨进程通信
- Android中使用aidl跨进程通信
- Android使用Aidl实现跨进程通信
- Android跨进程通信-AIDL的使用
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- 关于大型网站技术演进的思考(十)--网站静态化处理—动静整合方案(2)
- 一道面试题的解决办法
- springMVC笔记
- POJ lake counting(简单的dfs)
- tar.xz文件如何解压
- Android基础笔记(十二)- 使用AIDL来进行跨进程通信
- linx 线程切换的一些感悟
- 数据库作业一 姓名:肖波 学号:2013211527
- LeetCode - Rotate Array
- Google C++ 风格指南 - 中文版
- 求 2/1+3/2+5/3+8/5+13/8+…的前20项和
- Android FoldingLayout 折叠布局 原理及实现(二)
- hdoj 2120 Ice_cream's world I 【并查集判断成环数】
- 有序和无序数组的二分搜索算法+select第k小的元素