苏州大学计算机科学与技术学院第4章后台服务与广播消息苏州大学计算机科学与技术学院4.1Service应用4.2接收广播消息4.3相关阅读材料:Android开源库EventBus4.4本章小结苏州大学计算机科学与技术学院本章导读Service和BroadcastReceiver均属Android系统四大组件。Service只能后台运行,并且可以和其他组件进行交互。而广播是一种广泛运用的在应用程序之间传输信息的机制,BroadcastReceiver是对发送出来的广播进行过滤接收并响应的一类组件。苏州大学计算机科学与技术学院4.1Service应用4.1.1简介Service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的。Service的启动有两种方式:context.startService()和context.bindService()。苏州大学计算机科学与技术学院4.1.2进程内服务1.Service启动流程context.startService()启动流程:context.startService()→onCreate()→onStart()→Servicerunning→context.stopService()→onDestroy()→Servicestopcontext.bindService()启动流程:context.bindService()→onCreate()→onBind()→Servicerunning→onUnbind()→onDestroy()→Servicestop苏州大学计算机科学与技术学院2.Service生命周期•Service的生命周期并不像Activity那么复杂,它只继承了onCreate()、onStart()、onDestroy()三个方法。•当我们第一次启动Service时,先后调用了onCreate()、onStart()这两个方法;当停止Service时,则执行onDestroy()方法。•这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会再执行onCreate()方法,而是直接执行onStart()方法。•它可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己,只要调用一次stopService()方法便可以停止服务,无论调用了多少次的启动服务方法。苏州大学计算机科学与技术学院3.startService示例4.bindService示例详细内容参见教学视频苏州大学计算机科学与技术学院4.1.3跨进程服务1.AIDL机制由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。Android提供了AIDL工具来处理这项工作。苏州大学计算机科学与技术学院AIDL(AndroidInterfaceDefinitionLanguage)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocesscommunication,IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。苏州大学计算机科学与技术学院在Android中,每个应用程序都有自己的进程,Java中是不支持跨进程内存共享的。因此要传递对象,需要把对象解析成操作系统能够理解的数据格式,以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。在Android中,则采用AIDL(AndroidInterfaceDefinitionLanguage:接口描述语言)方式实现。苏州大学计算机科学与技术学院AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信。进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以Android采用代理类在背后实现了信息的双向转换,代理类由Android编译器生成,对开发人员来说是透明的。苏州大学计算机科学与技术学院2.选择AIDL的使用场合官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的Service,以及想在你的Service处理多线程。苏州大学计算机科学与技术学院4.2接收广播消息4.2.1简介1.广播发送者和广播接收者Android广播分为两个方面:广播发送者和广播接收者。通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器),用于异步接收广播Intent。广播Intent是通过调用Context.sendBroadcast()发送、BroadcastReceiver()接收。广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()、Context.sendStickyBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收。苏州大学计算机科学与技术学院BroadcastReceiver接收广播方式一般有两种:(1)NormalBroadcasts(正常广播),用Context.sendBroadcast()发送是完全异步的,它们都运行在一个未定义的顺序,通常是在同一时间。(2)OrderedBroadcasts(有序广播),用Context.sendOrderedBroadcast()发送每次被发送到一个receiver。苏州大学计算机科学与技术学院2.使用场景(1)同一App内部的同一组件内的消息通信(单个或多个线程之间);(2)同一App内部的不同组件之间的消息通信(单个进程);(3)同一App具有多个进程的不同组件之间的消息通信;(4)不同App之间的组件之间消息通信;(5)Android系统在特定情况下与App之间的消息通信。苏州大学计算机科学与技术学院3.实现原理(1)广播接收者BroadcastReceiver通过Binder机制向AMS(ActivityManagerService)进行注册;(2)广播发送者通过Binder机制向AMS发送广播;(3)AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;(4)消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。苏州大学计算机科学与技术学院4.实际应用中的适用性(1):同一App内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题。(2):同一App内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些较复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等。(3)(4)(5):由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。苏州大学计算机科学与技术学院4.2.2发送广播1.自定义BroadcastReceiver自定义广播接收器需要继承基类BroadcastReceivre,并实现抽象方法onReceive(context,intent)方法。广播接收器接收到相应广播后,会自动回到onReceive()方法。默认情况下,广播接收器也是运行在UI线程,因此,onReceive方法中不能执行太耗时的操作,否则将因此ANR(ApplicationNotResponding,应用没有响应)。一般情况下,根据实际业务需求,onReceive方法中都会涉及到与其他组件之间的交互,如发送Notification、启动service等。苏州大学计算机科学与技术学院下面代码片段是一个简单的广播接收器的自定义:publicclassMyBroadcastReceiverextendsBroadcastReceiver{publicstaticfinalStringTAG=MyBroadcastReceiver;@OverridepublicvoidonReceive(Contextcontext,Intentintent){Log.d(TAG,intent:+intent);Stringname=intent.getStringExtra(name);…}}苏州大学计算机科学与技术学院一个BroadcastReceiver对象只有在被调用onReceive(Context,Intent)才有效,当从该函数返回后,该对象就无效了,而结束生命周期。因此从这个特征可以看出,在所调用的onReceive(Context,Intent)函数里,不能有过于耗时的操作,不能使用线程来执行。对于耗时的操作,应该在startService中来完成。因为当得到其他异步操作所返回的结果时,BroadcastReceiver可能已经无效了。苏州大学计算机科学与技术学院2.BroadcastReceiver注册类型1)静态注册直接在AndroidManifest.xml文件中进行注册:receiverandroid:enabled=[true|false]android:exported=[true|false]android:icon=drawableresourceandroid:label=stringresourceandroid:name=stringandroid:permission=stringandroid:process=string.../receiver苏州大学计算机科学与技术学院其中,需要注意的属性有以下:(1)android:exported此broadcastReceiver能否接收其他App的发出的广播,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(2)android:name此BroadcastReceiver类名。(3)android:permission如果设置,具有相应权限的广播发送方发送的广播才能被此BroadcastReceiver所接收。(4)android:processBroadcastReceiver运行所处的进程。默认为App的进程。可以指定独立的进程。(Android四大基本组件都可以通过此属性指定自己的独立进程)。苏州大学计算机科学与技术学院常见的注册形式有:receiverandroid:name=.MyBroadcastReceiverintent-filteractionandroid:name=android.net.conn.CONNECTIVITY_CHANGE//intent-filterintent-filteractionandroid:name=android.intent.action.BOOT_COMPLETED//intent-filter/receiver苏州大学计算机科学与技术学院2)动态注册动态注册时,无须在AndroidMan