Service和BroadcastReceiver的那些事兒
大家都知道Service和BroadcastReceiver是android四大組件中的兩個很重要的組件,在實際項目開發中也是會經常用到的,很多時候,我們想在我們的app中做一些比較"猥瑣"的事情,又不愿意被用戶看到,這個時候我們應該怎么辦呢?
舉個栗子?
比如我們想在我們的app中每隔一段時間執行一段代碼,有童鞋可能會講,很簡單啊,我們只要在service中去執行這些耗時操作,并且用AlarmManager去定時處理一些事情呀!先不說AlarmManager這其中的坑,假如用戶關機重啟了呢?這個時候就輪到BroadcastReceiver出場了.
我們先來看看BroadcastReceiver和Service的用法,后面我會針對這個例子給出代碼.
BroadcastReceiver
我們先來說說BroadcastReceiver.Android系統中各種事件一直頻繁的發生著,比如:WIFI的有無,電話的呼入,短信的接收,電量的有無等等....那BroadcastReceiver意思為廣播接收者,顧名思義,它可以接受app或者是系統發出的廣播.它在我們開發中還是會經常使用的.
android的廣播機制主要分為標準廣播和有序廣播,廣播的注冊分為靜態注冊和動態注冊,在app中我們可以接收系統廣播,發送和接收我們自定義的廣播.
標準廣播
標準廣播是一種異步執行和接收的廣播,只要app或系統發出這條廣播,那么在其他的應用程序中在同一時刻都可以接收這條廣播,而且沒有順序.
有序廣播
顧名思義,有序廣播是一種有先后順序的廣播,它同步執行,在一個時刻只能有一個接收到廣播,同時它還有優先順序,而且有序廣播可以被截斷,一旦被截斷,后面的廣播接收器則無法接受到廣播.至于如何定義其優先級,后文會詳細介紹.
接收系統廣播
開發中,我們的app中可能有一個功能,比如我們要讓我們app中的播放視頻功能在wifi下自動播放,而在其他狀況需要用戶點擊確認之后播放,這個時候就要用到BroadcastReceiver了
動態注冊接收
動態注冊即在代碼中進行注冊,我們來看一下代碼
首先我們創建一個NetworkChangeReceiver繼承子BroadcastReceiver
class NetWorkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isAvailable()) {
int netWorkType = networkInfo.getType();
if (netWorkType == ConnectivityManager.TYPE_MOBILE) {
Toast.makeText(context, "network is available and you are now in mobile network", Toast.LENGTH_SHORT).show();
} else if (netWorkType == ConnectivityManager.TYPE_WIFI) {
Toast.makeText(context, "network is available and you are now in WIFI", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
}
}
}
之后我們需要去注冊這個廣播,這里我們直接在activity中注冊
mNetWorkChangeReceiver = new NetWorkChangeReceiver();
registerReceiver(mNetWorkChangeReceiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));
一定要注意的是在activity destroy的時候我們一定要反注銷廣播
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mNetWorkChangeReceiver);
}
靜態注冊
靜態注冊我們來實現開機自啟動,這一塊后面我會在文章剛開始講的例子中實現,這里就不在多說了.
發送自定義廣播
發送自定義標準廣播
發送自定義的標準廣播又分為動態注冊發送和靜態注冊發送.
動態注冊
創建CustomReceiver ,我們來看看主要代碼,注冊和發送廣播的代碼
mCustomReceiver = new CustomReceiver();
registerReceiver(mCustomReceiver, new IntentFilter(Constant.CUSTOM_RECEIVER));
Intent intent = new Intent(Constant.CUSTOM_RECEIVER);
sendBroadcast(intent);
靜態注冊
靜態注冊直接在manifest文件里面注冊
<receiver android:name=".CustomReceiver">
<intent-filter>
<action android:name="andy.lee.broadcastReceiver.customReceiver"/>
</intent-filter>
</receiver>
代碼中直接發送廣播
Intent i = new Intent(Constant.CUSTOM_RECEIVER);
sendBroadcast(i);
源碼在 github 詳細的大家可以去github上面看
發送自定義有序廣播
發送有序廣播大致步驟和標準廣播一樣,只需要吧 sendBroadcast() 方法改為 sendOrderBroadcast() 其余注冊方式和標準廣播一樣,需要注意的是有序廣播在manifest中的注冊順序即為接收順序,大家可以通過設置注冊時候的順序來控制接收順序,另外再接受有序廣播之后可以通過 abortbroadcast() 方法來截斷廣播,這樣的話后面的廣播接收器就不能接受廣播了
要注意的是,onReceive()方法中不能執行耗時操作,且不允許另開線程
IntentService
我們來說說IntentService,IntentService不是android系統提供的唯一服務,但是可能是我們最常使用的一種服務,它是一種non-sticky服務,non-sticky服務在服務認為已經完成的時候停止服務,他的用法也非常簡單.
下面就來實現上面我們所說的"猥瑣"的事情,我們想在app啟動的時候執行service中的某個方法,且每隔15s執行一次,用戶關機重啟時,服務會重啟
AlarmManager
說到定時執行某種操作,我們肯定首先想到了alarmManager, alarmManager中的setRepeating允許系統定時執行某種操作,此方法要傳入一個PendingIntent,我們來看一下代碼
Intent intent = new Intent(context, MyIntentService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), POLL_INTERVAL, pendingIntent);
關于PendingIntent我在此不多做解釋,大家可以去官網上查閱api文檔,第二個參數為開始時間,取當前系統手機撿,第三個參數為間隔時間,這里我們設置的是15s
好了,來看下效果:
圖1
我們看到每隔15s去打印日志信息.ok正確
但是接下來就遇到了一個問題,我在android 6.0上面使用的時候,打印日志信息如下:
圖2
大家看到是每隔60s打印一次日志,后來去看android 6.0的源碼發現,原來在源碼中設置了最小時間為60s
圖3
至于這個更多的解決方案,大家可以參考 官方文檔
下面來解決下一個問題,如何讓在用戶開機的時候啟動service呢,這里就用到了我上面提到的靜態注冊接受系統廣播了
我們來看一下代碼:
創建BootReceiver
/**
* andy.lee.intentservicealarm
* 監聽設備重啟之后,啟動service
* Created by andy on 16-12-27.
*/
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
boolean isOn = sp.getBoolean(Constant.IS_SERVICE_START, false);
MyIntentService.setServiceAlarm(context, isOn);
}
}
在manifest中注冊
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
注意接收開機廣播需要權限<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
最后來看看service里面的代碼:
public static void setServiceAlarm(Context context, boolean isOn) {
Intent intent = new Intent(context, MyIntentService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (isOn) {
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), POLL_INTERVAL, pendingIntent);
} else {
manager.cancel(pendingIntent);
pendingIntent.cancel();
}
來自:http://www.jianshu.com/p/cf7f911c2a8e