广播——broadcast
-
- 廣播機制簡介
- 廣播分類
- 標準廣播
- 有序廣播
- 接收系統廣播
- 動態注冊監聽網絡變化
- 靜態注冊實現開機啟動
- 注意耗時操作
- 發送自定義廣播
- 發送有序廣播
- 發送有序廣播步驟
- 優先級
- 使用本地廣播
- 出現原因
- 本地廣播使用
- 優點
- 廣播優先級
- 基本原則
- ordered 廣播
- 非 ordered 廣播
- 只能動態接受廣播源碼分析
- 目的
- 參考httpblogcsdnnetgemmemarticledetails8859493
- 廣播注冊過程分析
- 靜態注冊 Receiver 的流程
- 動態注冊 Receiver 的流程
- 廣播發送過程分析
- 分析
- 總結
- 參考
- 自己動手
- 廣播機制簡介
廣播機制簡介
廣播是一種可以跨進程的通信方式(比如:接收系統廣播)。
Android 廣播不關心接收者是否收到處理或者如何處理廣播,可以說是一種單向的通知。Android 通過 BraodcastReceiver 來監聽系統發出的廣播,不同的 BraodcastReceiver 通過設置不同的 fliter 來區分監聽廣播的類型。有些廣播的監聽需要相應的權限。
廣播分類
標準廣播
標準廣播:完全異步執行的廣播,在廣播發生之后,所有廣播同一時間接收到廣播消息,無任何先后順序,效率高,但無法被截斷。
有序廣播
有序廣播:同步執行的廣播,廣播發出后,同一時刻只有一個廣播接收器能收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢后,廣播才會繼續傳遞,廣播接收器有先后順序,優先級高的先收到,且可以截斷,后面的無法接收。
接收系統廣播
BraodcastReceiver 必須經過注冊才能具有監聽功能,注冊的方式有兩種:
廣播注冊方式:在代碼中注冊,又稱動態注冊。以及在AndroidManifest.xml清單文件中注冊。
動態注冊監聽網絡變化
(1)創建廣播接收器
新建一個內部類,讓他繼承BroadcastReceiver,并重寫父類的onReceive()方法。
(2)代碼注冊廣播。
(3)代碼舉例
添加獲取網絡狀態的權限。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>靜態注冊實現開機啟動
(1)動態注冊的缺點:必須在程序開啟時才能接收廣播。
(2)所以,靜態注冊解決了這個問題。
靜態注冊步驟:
- 新建一個類繼承BroadcastReceiver,實現onReceiver(),不再是內部類。
- 在AndroidManifest.xml中注冊,此時是作為四大組件,用標簽,加上等,注意也要加上相關需要的權限。
BootCompleteReceiver .java
public class BootCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();}} 權限<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> 注冊 <receiver android:name=".BootCompleteReceiver" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver>注意耗時操作
不要在onReceive()中添加過多的邏輯或者進行耗時操作。因為廣播接收器不允許開啟線程,當onReceive()運行較長時間而沒結束時,程序會報錯。8s左右即可,超過就要考慮轉移處理。(比如在這里面啟動一個服務,在服務中進行操作?)。
發送自定義廣播
intent構造函數傳入action作為參數(String)。同時,用intent還可以攜帶數據。
注意:在這之前的廣播都屬于標準廣播
發送有序廣播
發送有序廣播步驟
- sendOrderBroadcast(intent, null)
- 注冊時,進行設置優先級。或者通過調用 IntentFilter 對象的 setPriority()進行設置。
- 截斷:優先級高的接收器中調用abortBroadcast(),后面的接收器就接收不到了。
優先級
- 范圍:-1000~1000,但是實際最大值是可以達到int的最大值2147483647。
使用本地廣播
出現原因
前面發送和接收的廣播均屬于系統全局廣播,即發出的廣播可以被其他任何程序接收到,且我們也可以接收其他程序的廣播。這就導致了安全性問題(比如關鍵數據或者是垃圾廣播)。
所以,引入本地廣播機制,只能在應用程序內部進行傳遞,本地廣播機制中的廣播接收器也只能接收來自本應用發出的廣播。
本地廣播使用
localBroadcastManager = LocalBroadcastManager.getInstance(this);
代碼:
優點
本地廣播是無法通過靜態注冊的方式來接收的。其實這也完全
可以理解,因為靜態注冊主要就是為了讓程序在未啟動的情況下也能收到廣播,而發送本地
廣播時,我們的程序肯定是已經啟動了,因此也完全不需要使用靜態注冊的功能。
最后我們再來盤點一下使用本地廣播的幾點優勢吧。
1. 可以明確地知道正在發送的廣播不會離開我們的程序,因此不需要擔心機密數據泄
漏的問題。
2. 其他的程序無法將廣播發送到我們程序的內部,因此不需要擔心會有安全漏洞的隱
患。
3. 發送本地廣播比起發送系統全局廣播將會更加高效。
廣播優先級
基本原則
接收無序廣播的接收器接收到廣播的順序是有序的(由優先級決定順序)
接收無序廣播的接收器也一樣可以設置優先級的
動態注冊廣播優先級高于靜態注冊廣播
同等優先級的動態接收器,先注冊的先接收
同等優先級的靜態接收器,接收廣播的順序與 String[] java.io.File.list()順序一致
Ps:這里有一點需要注意的是,同等優先級的靜態接收器的接收順序具有不確定性,原因就是File.list()的方法返回的順序具有不確定性,如果需要查看某接收器的接收順序,最好是試驗大量的 apk 名。
ordered 廣播
假設有如下優先級的 5 個接收器
1.動態 A(優先級=1)
2.動態 B(優先級=2)
3.動態 C(優先級=2)
4.靜態 D(優先級=1)
5.靜態 E(優先級=2)
并且 B 先于 C 注冊
那么實際接收順序應為
B C E A D
也就是說,如果靜態接收器的優先級高于動態接收器的優先級,那么還是靜態接收器先接收到廣播(比如接收 SMS 廣播)
非 ordered 廣播
動態接收器高優先級>動態接收器低優先級>靜態接收器高優先級>靜態接收器低優先級
只能動態接受廣播源碼分析
有些廣播,我們無法用靜態接收器接收,比如 ACTION_SCREEN_ON,當屏幕被點亮的時候系統發送此廣播。
void com.android.server.PowerManagerService.initInThread() Java 代碼 void initInThread() { ?? mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF); mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); ?? }Intent 中都設置了 Intent.FLAG_RECEIVER_REGISTERED_ONLY,所以,如果要接收,必須動態注冊廣播接收器 ACTION_SCREEN_OFF 也是如此。類似的還有:ACTION_TIME_TICK、ACTION_BATTERY_CHANGED。
不能靜態注冊的廣播:
android.intent.action.SCREEN_ON
android.intent.action.SCREEN_OFF
android.intent.action.BATTERY_CHANGED
android.intent.action.CONFIGURATION_CHANGED
android.intent.action.TIME_TICK
目的
很多病毒程序為了保證自己被終止后能夠再次運行,都會在xml中注冊一些系統廣播,企圖利用這些系統高頻廣播來實現自啟動。
比如在老版本的android系統中,病毒程序可以通過監聽TIME_TICK來啟動自己的service后臺運行,做一些隱秘的工作,而且就算自己被kill掉了,也能很快重新啟動。
而一旦這些系統廣播加了flag FLAG_RECEIVER_REGISTERED_ONLY,這些病毒程序就沒轍了。google的這一改動無疑提升了android系統的安全性。
參考:http://blog.csdn.net/gemmem/article/details/8859493
廣播注冊過程分析
靜態注冊 Receiver 的流程
靜態 receiver 的注冊是由 PackageManagerService 開機的時候負責初始 PMS 在開機的時候會對系統一些目錄逐個掃描,解析 apk 文件。靜態廣播接收器就是在 PMS 做這件事情的時候順便處理的。
PMS 會解析 apk 的 manifest 文件,查找這里注冊的 receiver,然后加載到內存中。
PMS 初始化掃描目錄的順序:
system/framework
system/app
vendor/app
data/appd
rm/app-private
我們看到了 PMS 如何在初始化的時候如何解析 manifest 并把其中的 element 存放到內存中的其中receiver 保存到了 owner 的成員變量 receivers 中,owner 的類型是
android.content.pm.PackageParser.Package 也就是說 scanPackageLI 返回結果就是已經包含了manifest 信息的 Package 對象。
動態注冊 Receiver 的流程
動態注冊最終會調用到 AMS 中的 registerReceiver 函數,最終所有動態注冊的 receiver 都保存到 AMS 的成員變量 mReceiverResolver 中。靜態廣播和動態廣播如何注冊的,我們已經全部分析完了。靜態廣播是 PackageManagerService負責,保存到其成員變量 mReceivers 中,動態廣播是 ActivityManagerService 負責,保存到其成員變量 mReceiverResolver 中。
廣播發送過程分析
分析
Context 中的 sendBroadCast 函數的實現是在 ContextImpl 中,和發送廣播相關的有如下六個函數: void android.app.ContextImpl.sendBroadcast(Intent intent) void android.app.ContextImpl.sendBroadcast(Intent intent, String receiverPermission) void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission) void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) void android.app.ContextImpl.sendStickyBroadcast(Intent intent) void android.app.ContextImpl.sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)可以分為 3 組:1 普通廣播;2 Ordered 廣播;3 Sticky 廣播。不論哪種,最后都會由 AMS 處, 如果是非 ordered 廣播,那么 mParallelBroadcasts 將存儲所有動態接收器,然后合并的時候,mParallelBroadcasts 設置為 null,所以不會合并到 receivers 中,如果是 ordered 廣播,那么mParallelBroadcasts 將合并到 receivers 中,然后,不管是哪種廣播,最后都是調用scheduleBroadcastsLocked 繼續處理,最終到 processNextBroadcast 函數上。廣播是否有序,即通過 Boolean 變量 ordered 進行設置。
總結
發送過程情況分為兩種(scheduleBroadcastsLocked),ordered 廣播和非 ordered 廣播、非ordered 廣播。先處理動接收器,然后處理靜態接收器 ordered 廣播同時處理動態接收器和靜態接收器先將動態接收器與靜態接收器合并,保持著與優先級相同的順序,優先級高的在前面,否則順序不變。靜態接收器與動態接收器優先級相同的話,動態接收器在前。
參考
http://www.2cto.com/kf/201408/325326.html
自己動手
控制activity
public class ActivityControl {public static List<Activity> activityList = new ArrayList<Activity>();public static void addActivity(Activity activity){activityList.add(activity);}public static void rmActivity(Activity activity){activityList.remove(activity);}public static void finishAllActivity(){int length = activityList.size();for(int i = 0; i < length; i++ ){activityList.get(i).finish();}} }強制下線(靜態注冊)
offBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(DownActivity.this, OffLineBroadcast.class);sendBroadcast(intent);}}); //注意,在activity外部跳轉activity注意加上FLAG:Intent.FLAG_ACTIVITY_NEW_TASK public class OffLineBroadcast extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {ActivityControl.finishAllActivity();Intent i = new Intent(context, MainActivity.class);i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(i);} } 清單文件注冊 <receiver android:name=".OffLineBroadcast"></receiver>或者在啟動廣播的時候用intent(action)
Intent intent = new Intent("com.zzz.offLine");sendBroadcast(intent);這種情況下清單文件注冊注意加上action
<receiver android:name=".OffLineBroadcast"><intent-filter><action android:name="com.zzz.offLine"></action></intent-filter> </receiver>增加對話框提示
public class OffLineBroadcast extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {final Context theContext = context;AlertDialog.Builder builder = new AlertDialog.Builder(context);builder.setTitle("Warning");builder.setMessage("You are forced to be offline, please try to login again.");builder.setCancelable(false);builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {ActivityControl.finishAllActivity();Intent i = new Intent(theContext, MainActivity.class);i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);theContext.startActivity(i);}});AlertDialog alertDialog = builder.create();//需要設置AlertDialog的類型,保證在廣播接收器中可以正常彈出。alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);alertDialog.show();;} }===============================
強制下線(動態注冊)
動態注冊:廣播是內部類,并且要注意廣播的注冊和解除,觸發廣播用intent(action),系統廣播的觸發intent不需要我們寫。
總結
以上是生活随笔為你收集整理的广播——broadcast的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: display、visibility、o
- 下一篇: Mac dubbo注册到服务非常慢5秒一