第十章 Service与BroadCastReceiver
Service组件的作用和意义
创建、配置Service
启动、停止Service
绑定本地Service并与之通信
Service的生命周期
IntentService的功能和用法
TelephonyManager的功能和用法
监听手机短信
SMSManager的功能和用法
监听手机电话
AudioManager的功能和用法
Vibrator的功能和用法
AlarmManager的功能和用法
BroadcastReceiver组件的功能和意义
开发、配置BroadcastReceiver组件
发送广播、发送有序广播
使用BroadcastReceiver接受系统广播
Service简介
生命周期方法
IBinder onBind(Intent intent):该方法是Service子类必须实现的方法。该方法返回一个IBinder对象,应用程序可通过该对象与Service组件通信。
void onCreate():在该Service第一次被创建后将立即回调该方法
void onDestroy():在该Service被关闭前回调该方法。
void onStartCommand(Intent intent,int flags,int startId):该方法的早期版本是void onStart(Intent intent,int startId),每次客户端调用startService(Intent intent)方法启动该Service时都会回调该方法
boolean onUnbind(Intent intent):当该Service上绑定的所有客户端都断开连接时将会回调该方法
在Android中允许Service有两种方式:
通过Context的startService()方法:通过该方法启动Service,访问者与Service之间没有关联,即使访问者退出了,Service也仍然允许。
通过Context的bindService()方法:使用该方法启动Service,访问者与Service绑定在一起,访问者一旦退出,Service也终止了。
从Android 5.0开始,Google要求必须使用显示Intent启动Service组件
绑定本地Service并与之通信
当程序通过startService()和stopService()启动、关闭Service时,Service与访问者之前基本不存在太多的关联,因此Service和访问者之间也无法进行通信、交换数据。
如果Service和访问者之间需要进行方法调用或交换数据,则应该使用bindService()和unbindService()
Context的bindService()方法:
bindService(Intent intent,ServiceConnection conn,int flags)
intent:该参数通过Intent指定要启动的Service
conn:该参数是一个ServiceConnection对象,该对象用于监听访问者与Service之间的连接情况。当访问者与Service之间连接成功时将会回调该ServiceConnection对象的onServiceConnected(ComponentName name,IBinder service)方法;当Service所在的宿主进程由于异常终止或其他原因终止,导致Service与访问者之间断开连接时回调该ServiceConnection对象的onServiceDisConnected(ComponentName name)方法。
flags:指定绑定时是否自动创建Service(如果Service还未创建)。该参数可指定为0(不自动创建)或BIND_AUTO_CREATE(自动创建)
Note:当调用者主动通过UNBindService()方法断开与Service的连接时,ServiceConnection对象的onServiceDisConnected(ComponentName name)方法并不会被调用
与Service的通信就是通过Service的onBind()返回的IBinder对象,这个IBinder会传给ServiceConnection对象的onServiceConnected方法的IBinder参数。实现通信
对于Service的onBind()方法所返回的IBinder对象来说,它可被当成该Service组件所返回的代理对象,Service允许客户端通过该IBinder对象来访问Service内部的数据,这样既可实现客户端与Service之间的通信
不同启动方式下的Service的生命周期
特殊情况:如果Service已由某个客户端通过startService()启动了,接下来其他客户端调用bindService()绑定该Service,在调用unbindService()解除绑定,最后又调用bindService()再次绑定到Service,这个过程触发的生命周期方法:
onCreate() -> onStartCommand() -> onBind() -> onUnbind()(重写该方法时返回true) -> onRebind()
如果希望onRebind()方法被回调,除了需要改Service是由Activity的startService()方法启动之外,还需要Service子重写onUnbind()方法时返回true。
使用IntentService
IntentService是Service的子类,在普通Service上增加了额外的功能
普通Service:
Service不会专门开启一个单独的进程,Service与它所在应用位于同一 进程中
Service不是一条新的线程,因此不应该在Service中进行耗时操作
而IntentService弥补了这些不足:IntentService将会使用队列来管理请求Intent,每当客户端代码通过Intent请求启动IntentService时,IntentService会将该Intent加入队列中,然后开启一个新的worker线程来处理该Intent。对于异步的startService()请求,IntentService会按次序依次处理队列中的Intent,该线程保证同一时刻只处理一个Intent。由于IntentService使用新的worker线程处理Intent请求,因此IntentService不会阻塞主线程,所有IntentService可以处理耗时操作。
IntentService:
会创建单独的worker线程来处理所有的Intent请求
会创建单独的worker线程来处理onHandleIntent()方法实现的代码,因此开发者无须处理多线程问题
当所有请求处理完成后,IntentService会自动停止,因此无须调用stopSelf()来停止Service
为Service的onBind()提供默认实现,默认实现的onBind()方法返回null
为Service的onStartCommand()方法提供默认实现,该实现会将请求Intent添加到队列中。
所有扩展IntentService实现Service无须重写onBind()、onStartCommand(),只要重写onHandleIntent()即可。
电话管理器(TelephonyManager)
TelephonyManager是一个管理手机通话状态、电话网络信息的服务类,该类提供了大量的getXxx()方法来获取电话网络的相关信息
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
获取手机位置和状态需要声明必要的权限
TelephonyManager除了获取网络状态和SIM卡信息之外,还提供了一个listen(PhoneStateListener listener,int events)来监听通话状态。
短信监听器(SmsManager)
SmsManager是Android提供的另一个常见的服务,SmsManager提供了一些sendXxxMessage()方法用于发送短信,不过通常使用最多的是发送普通文本,就是,sendTextMessage()。
SmsManager sm = SmsManager.getDefault();
发送短信会用到一个PendingIntent对象,PendingIntent是对Intent的封装,一般通过调用PendingIntent的getActivity()、getService()、getBroadcastReceiver()静态方法来获取PendingIntent对象。以Intent不同的是,PendingIntent通常会传给其他应用组件,从而由其他应用程序来执行PendingIntent所包装的”Intent”。
需要添加发短信的权限
音频管理器(AudioManager)
AudioManager am = (AudioManager)getSystemService(Context.Context.AUDIO_SERVICE);
设置手机音频的方法:
adjustStreamVolume(int streamType,int direction,int flags):调整手机指定类型的声音
streamType:声音类型
STREAM_ALARM:手机闹铃的声音
STREAM_DTMF:DTMF音调的声音
STREAM_MUSIC:手机音乐的声音
STREAM_NOTIFICATION:系统提示的声音
STREAM_RING:电话铃声的声音
STREAM_SYSTEM:手机系统的声音
STREAM_VOICE_CALL:语音电话的声音
direction:对声音是增大还是减小;
flags:调整声音时的标志,例如指定FLAG_SHOW_UI,则调整声音时显示音量进度条
setMicrophoneMute(boolean on):设置是否让麦克风静音
setMode(int mode):设置声音模式,NORMAL、RINGTONE和IN_CALL
setRingerMode(int ringerMode):设置手机的电话铃声模式
RINGER_MODE_NORMAL:正常的手机铃声
RINGER_MODE_SILENT:手机铃声静音
RINGER_MODE_VIBRATE:手机震动
setSpeakerphoneOn(boolean on):设置是否打开扩音器
setStreamMute(int streamType,boolean state):将手机的指定类型调整为静音,其中streamType与上面的相同
setStreamVolume(int streamType,int index,int flags):直接设置手机的指定类型的音量值
振动器(Vibrator)
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibrate(long milliseconds):控制手机震动毫秒
vibrate(long[] pattern,int repeat):指定手机里pattern指定的模式振动。
例如:pattern为new int[400,800,1200,1600],就是指定在400ms,800ms,1200ms,1600ms这些时间点交替启动、关闭手机振动器;其中repeat指定pattern数组的索引,指定对pattern数组中从repeat索引开始的振动进行循环
cancel():关闭手机振动
手机闹钟服务(AlarmManager)
AlarmManager通常的用途是用来开发手机闹钟,但实际上它的作用不止于此。它的本质是一个全局定时器,AlarmManager可在指定时间或指定周期启动其他组件(包括Activity、Service、BroadcastReceiver).
set(int type,long triggerAtTime,PendingIntent operation):设置在triggerAtTime时间启动由operation参数指定的组件,
type:指定定时服务的类型
ELAPSED_REALTIME:指定从现在开始时间过了一定时间后启动operation所对应的组件
ELAPSED_REALTIME_WAKEUP:指定从现在开始时间过了一定时间后启动operation所对应的组件。即使系统处于休眠状态也会执行operation所对应的组件
RTC:指定当系统调用System.currentTimeMillis()方法的返回值与triggerAtTime相等时启动operation所对应的组件
RTC_WAKEUP:指定当系统调用System.currentTimeMillis()方法的返回值与triggerAtTime相等时启动operation所对应的组件。即使系统处于休眠状态也会执行operation所对应的组件。
setInexactRepeating(int type,long triggerAtTime,long interval,PendingIntent operation):设置一个非精确的周期性任务。例如,我们设置Alarm每个小时启动一次,当系统并不一定总在每个小时的开始启动Alarm服务。
setRepeating(int type,long triggerAtTime,long interval,PendingIntent operation):设置一个周期性执行的定时任务
cancel(PendingIntent operation):取消AlarmManager的定时服务。
Note:从Android 4.4(API 19)开始,AlarmManager的机制是非准确激发的,操作系统柜偏移(shift)闹钟来最小化唤醒和电池消耗。不过AlarmManager新增了两个方法支持精确激发:
setExact(int type,long triggerAtMillis,PendingIntent operation):设置闹钟将在精确的时间被激发。
setWindow(int type,long windowStartMillis,long windowLengthMillis,PendingIntent operation):设置闹钟将在精确的时间段内被激发。
对于targetSdkVersion在API 19之前的应用仍将继续使用以前的行为,所以闹钟都会使用精确激发。
广播
BroadcastReceiver本质上就是一个全局监听器,用于监听系统全局的广播消息。由此它可以非常方便的实现系统中不同组件之间的通信。
程序启动BroadcastReceiver只需两步:
创建需要启动的BroadcastReceiver的Intent
调用Context的sendBroadcast()或sendOrderBroadcast()方法启动指定的BroadcastReceiver。
当程序发出一个Broadcast Intent之后,所有匹配该Intent的BroadcastReceiver都有可能被启动
Note:其他类似OnXxxListener属于程序级别的监听器,这些监听器运行在指定程序所在的进程中,当程序退出时,onXxxListener监听器就随之关闭了。但是BroadcastReceiver属于系统级别的监听器,它拥有自己的进程,只要存在与之匹配的Intent广播出来,BroadcastReceiver就会被激发。
一旦实现了BroadcastReceiver,接下来就应该指定该BroadcastReceiver能匹配的Intent,有两种方式:
使用代码指定:调用Context的registerReceiver(BroadcastReceiver receiver,IntentFilter filter)方法指定
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_REVEIVER");
IncomingSMSReceiver receiver = new IncomingSMSReceiver();
registerReceiver(receiver,filter);
在Manifest.xml文件中配置:
<receiver android:name=".IncomingSMSReceiver">
<intent-filter >
<action android:name="android.provider.Telephony.SMS_RECEIVER"/>
</intent-filter>
</receiver>
BroadcastReceiver的onReceive()方法如果不能再10秒内执行完成,Android就会认为程序无响应,所以onReceive()方法不能执行耗时操作。
如果需要根据Broadcast执行一项耗时的操作,应该考虑通过Intent启动Service来完成,不应该考虑使用新线程去完成耗时操作,因为BroadcastReceiver本身的生命周期很短, 可能出现的情况是子线程可能还没结束,BroadcastReceiver就已经退出了。
如果BroadcastReceiver所在的进程结束了,虽然该进程内还有用户启动的新线程,但由于该进程内不包含任何活动组件,因此系统可能在内存紧张时优先结束该进程。这样就可能导致BroadcastReceiver启动的子线程无法执行完成。
普通广播(Normal Broadcast)
完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递效率高,缺点是接收者不能处理结果传递给下一个接收者,无法终止Broadcast Intent的传播
有序广播(Ordered Broadcast)
接收者将按预先声明的优先级依次接收Broadcast。优先级声明在<intent-filter.../>
元素的android:priority属性中,数值越大优先级越高,取值范围为-1000~1000,
Context提供如下两个方法用于发送广播:
sendBroadcast():发送Normal Broadcast
sendOrderedBroadcast():发送Ordered Broadcast
对于Ordered Broadcast而言,优先接收到Broadcast的接收者可以通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,然后传给下一个接收者,下一个接收者通过Bundle bundle = getResultExtras(true)
可以获取上一个接收者存入的数据。
Note:系统收到短信,发出的Broadcast属于Ordered Broadcast。如果想阻止用户受到短信,可以通过设置优先级,让自定义的BroadcastReceiver先获取到Broadcast,然后终止Broadcast。
接收系统广播
除了接收用户发送的广播外,BroadcastReceiver还有一个重要的用途:接收系统广播。
ACTION_TIME_TICK:当前时间改变,每分钟发送一次,
ACTION_TIME_CHANGED:系统时间被改变
ACTION_TIMEZONE_CHANGED:系统时区被改变
ACTION_BOOT_COMPLETED:系统启动完成
ACTION_PACKAGE_ADDED:系统添加包
ACTION_PACKAGE_CHANGED:系统包改变
ACTION_PACKAGE_REMOVED:系统包被移除
ACTION_PACKAGE_RESTARTED:系统包被重启
ACTION_PACKAGE_DATA_CLEARED:系统包数据被清空
ACTION_UID_REMOVED:UID被移除
ACTION_BATTERY_CHANGED:电量变化
ACTION_POWER_CONNECTED:系统连接电源
ACTION_POWER_DISCONNECTED:系统与电源断开
ACTION_SHUTDOWN:系统被关闭
Note:以上的Action有些只能通过代码注册的方式才能被接收到,不能在Manifest.xml中注册