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>

注意接收開機廣播需要權限&lt;uses-permission android:name=&quot;android.permission.RECEIVE_BOOT_COMPLETED&quot;/&gt;

最后來看看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

 

 本文由用戶 IrvingWilli 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!