咦,Oreo怎麼收不到廣播了?

本文已同步發送到微信公衆號:猿溼Xoong

忙啊~最近好忙呀。html

忙的我連SystemUI系列的文章推動向蝸牛同樣慢~java

這篇文章偷個閒,記錄下Android8.0上的廣播限制。android

最近在基於Android 8.1的系統項目中有用到靜態註冊廣播去監聽廣播。但是不論我是普通的將Apk install進去抑或是高貴的push到對應的system/priv-app/目錄下,都收不到這個廣播。心態,DUANG,炸了。安全

後來靈光一閃,扒出記憶角落的Android7.0的廣播限制,趕忙Google一下。原來如此,恍然大悟:**Android8.0後,當App targetSDK >= 26,幾乎禁止了全部的隱式廣播的靜態註冊監聽。**特在此記錄,防止我之後又提莫的忘記了。 微信

本篇文章主要講述如下內容,還請拿起小板凳,帶好零食,前來觀賞:網絡

  • Android廣播科普
  • Android8.0的後臺限制
  • 具體廣播限制和對應赦免清單
  • 適配/解決方法

科普科普廣播知識

來來來,先科普下,廣播兩種監聽/接收註冊方式和兩種類型,拿小本本記下來,記住了!app

註冊方式ide

  • 靜態註冊:也稱爲清單註冊,就是在AndroidManifest.xml中註冊的廣播。此類廣播接收器在應用還沒有啓動的時候就能夠接收到相應廣播。
  • 動態註冊:也稱爲運行時註冊,也就是在Service或者Activity組件中,經過Context.registerReceiver()註冊廣播接收器。此類廣播接收器是在應用已啓動後,經過代碼進行註冊。

兩種類型ui

  • 顯式廣播(Explicit Broadcast):發送的Intent是顯示Intent的廣播。經過指定Intent組件名稱來實現的,它通常用在知道目標組件名稱的前提下,去調用如下方法。意圖明確,指定了要激活的組件是哪一個組件,通常是在相同的應用程序內部實現的。
Intent.setComponent()
Intent.setClassName()
Intent.setClass()
new Intent(A.this,B.class)
複製代碼
  • 隱式廣播(Implicit Broadcast):經過Intent Filter來實現的,它通常用在沒有明確指出目標組件名稱的前提下。Android系統會根據隱式意圖中設置的動做(action)、類別(category)、數據(URI和數據類型)找到最合適的組件來處理這個意圖。通常是用於在不一樣應用程序之間。

Android8.0的後臺執行限制

注意是針對targetSDK >= 26的應用,也就是說,targetSDK小於26的話,暫不受影響this

在Oreo中,爲了進一步提高用戶體驗,進一步節省功耗,對應用在後臺運行時能夠執行的操做又進一步施加了限制。

  • 後臺服務限制:處於空閒狀態時,限制應用的後臺服務。例如:經過靜態註冊接收開機廣播(假設你的設備沒作定製,能收到~),並在onReceive方法中啓動一個Service,在API 26上,是不容許且會報錯的。固然,對於前臺服務,這種限制是不存在的。官方說法是:前臺服務更容易引發用戶注意。

  • 廣播限制:除了有限的例外以外,應用沒法使用清單註冊(靜態註冊)的方式來接收隱式廣播

    • 但對於這些隱式廣播,能夠經過運行時註冊(動態註冊)的方式註冊。
    • 對於顯式廣播,則依然能夠經過清單註冊(靜態註冊)的方式監聽

這裏多說一句,Android手機的卡頓,很大程度是因爲應用濫用且自私的使用各類手段(權限濫用,廣播註冊,後臺服務常駐等)保活或作一些PY事情。Google顯然很早就意識到這一點,並從Android 6.0 開始就逐步引入各類限制,好比運行時權限和Doze。

具體廣播限制和對應赦免清單

若是應用註冊了廣播接收器,那麼每次發送廣播後,應用的廣播接收器就會消耗資源,如RAM,CPU等。若是有不少應用對系統事件廣播註冊廣播接收器,這....,就會很卡的嘛!

因此從Android 7.0 (API 級別 24)開始,就對廣播作了一些限制:

  • API24及以上應用,靜態註冊的廣播接收器沒法監聽網絡變化:android.net.conn.CONNECTIVITY_CHANGE
  • 在Android7.0設備上,App沒法發送或者接收ACTION_NEW_PICTURE和ACTION_NEW_VIDEO廣播。

只不過,在Android8.0上,又進一步的加強了限制,除了如下隱式廣播外,其餘全部隱式廣播均沒法經過在AndroidManifest.xml中註冊監聽。參考官網

// Android 8.0 上不限制的隱式廣播
/** 開機廣播 Intent.ACTION_LOCKED_BOOT_COMPLETED Intent.ACTION_BOOT_COMPLETED */
"保留緣由:這些廣播只在首次啓動時發送一次,而且許多應用都須要接收此廣播以便進行做業、鬧鈴等事項的安排。"

/** 增刪用戶 Intent.ACTION_USER_INITIALIZE "android.intent.action.USER_ADDED" "android.intent.action.USER_REMOVED" */
"保留緣由:這些廣播只有擁有特定系統權限的app才能監聽,所以大多數正常應用都沒法接收它們。"
    
/** 時區、ALARM變化 "android.intent.action.TIME_SET" Intent.ACTION_TIMEZONE_CHANGED AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED */
"保留緣由:時鐘應用可能須要接收這些廣播,以便在時間或時區變化時更新鬧鈴"

/** 語言區域變化 Intent.ACTION_LOCALE_CHANGED */
"保留緣由:只在語言區域發生變化時發送,並不頻繁。 應用可能須要在語言區域發生變化時更新其數據。"

/** Usb相關 UsbManager.ACTION_USB_ACCESSORY_ATTACHED UsbManager.ACTION_USB_ACCESSORY_DETACHED UsbManager.ACTION_USB_DEVICE_ATTACHED UsbManager.ACTION_USB_DEVICE_DETACHED */
"保留緣由:若是應用須要瞭解這些 USB 相關事件的信息,目前還沒有找到可以替代註冊廣播的可行方案"

/** 藍牙狀態相關 BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED BluetoothDevice.ACTION_ACL_CONNECTED BluetoothDevice.ACTION_ACL_DISCONNECTED */
"保留緣由:應用接收這些藍牙事件的廣播時不太可能會影響用戶體驗"

/** Telephony相關 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED TelephonyIntents.SECRET_CODE_ACTION TelephonyManager.ACTION_PHONE_STATE_CHANGED TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED */
"保留緣由:設備製造商 (OEM) 電話應用可能須要接收這些廣播"

/** 帳號相關 AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION */
"保留緣由:一些應用須要瞭解登陸賬號的變化,以便爲新賬號和變化的賬號設置計劃操做"

/** 應用數據清除 Intent.ACTION_PACKAGE_DATA_CLEARED */
"保留緣由:只在用戶顯式地從 Settings 清除其數據時發送,所以廣播接收器不太可能嚴重影響用戶體驗"
    
/** 軟件包被移除 Intent.ACTION_PACKAGE_FULLY_REMOVED */
"保留緣由:一些應用可能須要在另外一軟件包被移除時更新其存儲的數據;對於這些應用,還沒有找到可以替代註冊此廣播的可行方案"

/** 外撥電話 Intent.ACTION_NEW_OUTGOING_CALL */
"保留緣由:執行操做來響應用戶打電話行爲的應用須要接收此廣播"
    
/** 當設備全部者被設置、改變或清除時發出 DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED */
"保留緣由:此廣播發送得不是很頻繁;一些應用須要接收它,以便知曉設備的安全狀態發生了變化"
    
/** 日曆相關 CalendarContract.ACTION_EVENT_REMINDER */
"保留緣由:由日曆provider發送,用於向日歷應用發佈事件提醒。由於日曆provider不清楚日曆應用是什麼,因此此廣播必須是隱式廣播。"
    
/** 安裝或移除存儲相關廣播 Intent.ACTION_MEDIA_MOUNTED Intent.ACTION_MEDIA_CHECKING Intent.ACTION_MEDIA_EJECT Intent.ACTION_MEDIA_UNMOUNTED Intent.ACTION_MEDIA_UNMOUNTABLE Intent.ACTION_MEDIA_REMOVED Intent.ACTION_MEDIA_BAD_REMOVAL */
"保留緣由:這些廣播是做爲用戶與設備進行物理交互的結果:安裝或移除存儲卷或當啓動初始化時(當可用卷被裝載)的一部分發送的,所以它們不是很常見,而且一般是在用戶的掌控下"

/** 短信、WAP PUSH相關 Telephony.Sms.Intents.SMS_RECEIVED_ACTION Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION 注意:須要申請如下權限才能夠接收 "android.permission.RECEIVE_SMS" "android.permission.RECEIVE_WAP_PUSH" */
"保留緣由:SMS短信應用須要接收這些廣播"
複製代碼

呼,終於列完了,以上。能夠說寫的比官網還全~

建議收藏一波防止之後用的到哈。

解決方法

按照官方推薦,對於隱式廣播,經過如下方法進行替換。

  • 動態經過調用 Context.registerReceiver()註冊廣播接收器而不是在清單中聲明接收器。
  • 使用JobScheduler

我選擇動態註冊的方式來處理這個問題。

好了,關於Oreo的廣播限制的嘮嗑就先嘮到這裏。

最近受到一位小夥伴的啓發,獲得一句話:努力的人,運氣和機遇每每都不會差!

共勉!

最後,歡迎關注微信公衆號:猿溼Xoong
開心的進行Android高質量乾貨分享

掃碼關注喔

參考連接

[1] Android Oreo 後臺執行限制 https://developer.android.com/about/versions/oreo/background#broadcasts

[2] Android Oreo Implicit Broadcast Exceptions https://developer.android.com/guide/components/broadcast-exceptions

[3] Android中顯式和隱式intent的特色和區別 https://blog.csdn.net/u014177843/article/details/50596863

[4] Android O行爲變動--隱式廣播限制 https://blog.csdn.net/hqocshheqing/article/details/76850164

相關文章
相關標籤/搜索