編輯:關於Android編程
廣播是一種可以跨進程的通信方式(比如:接收系統廣播)。
Android 廣播不關心接收者是否收到處理或者如何處理廣播,可以說是一種單向的通知。Android 通過 BraodcastReceiver 來監聽系統發出的廣播,不同的 BraodcastReceiver 通過設置不同的 fliter 來區分監聽廣播的類型。有些廣播的監聽需要相應的權限。
標准廣播:完全異步執行的廣播,在廣播發生之後,所有廣播同一時間接收到廣播消息,無任何先後順序,效率高,但無法被截斷。
有序廣播:同步執行的廣播,廣播發出後,同一時刻只有一個廣播接收器能收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播才會繼續傳遞,廣播接收器有先後順序,優先級高的先收到,且可以截斷,後面的無法接收。
BraodcastReceiver 必須經過注冊才能具有監聽功能,注冊的方式有兩種:
廣播注冊方式:在代碼中注冊,又稱動態注冊。以及在AndroidManifest.xml清單文件中注冊。
(1)創建廣播接收器
新建一個內部類,讓他繼承BroadcastReceiver,並重寫父類的onReceive()方法。
(2)代碼注冊廣播。
(3)代碼舉例
public class MainActivity extends Activity {
IntentFilter intentFilter;
MyBroadcastReceiver myReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
myReceiver = new MyBroadcastReceiver();
//注冊廣播
registerReceiver(myReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
Toast.makeText(MainActivity.this, "i am change" + networkInfo.getState(), Toast.LENGTH_LONG).show();
}
}
}
添加獲取網絡狀態的權限。
(1)動態注冊的缺點:必須在程序開啟時才能接收廣播。
(2)所以,靜態注冊解決了這個問題。
靜態注冊步驟:
BootCompleteReceiver .java
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
}
}
權限
注冊
不要在onReceive()中添加過多的邏輯或者進行耗時操作。因為廣播接收器不允許開啟線程,當onReceive()運行較長時間而沒結束時,程序會報錯。8s左右即可,超過就要考慮轉移處理。(比如在這裡面啟動一個服務,在服務中進行操作?)。
Intent intent = new Intent(
"com.example.broadcasttest.MY_BROADCAS");
sendBroadcast(intent);
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceive",
Toast.LENGTH_SHORT).show();
}
}
//這裡的action是自定義的,不是android自帶
注意:在這之前的廣播都屬於標准廣播
前面發送和接收的廣播均屬於系統全局廣播,即發出的廣播可以被其他任何程序接收到,且我們也可以接收其他程序的廣播。這就導致了安全性問題(比如關鍵數據或者是垃圾廣播)。
所以,引入本地廣播機制,只能在應用程序內部進行傳遞,本地廣播機制中的廣播接收器也只能接收來自本應用發出的廣播。
public class MainActivity extends Activity {
private IntentFilter intentFilter;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(
"com.example.broadcasttest.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localReceiver);
}
class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received local broadcast",
Toast.LENGTH_SHORT).show();
}
}
}
本地廣播是無法通過靜態注冊的方式來接收的。其實這也完全
可以理解,因為靜態注冊主要就是為了讓程序在未啟動的情況下也能收到廣播,而發送本地
廣播時,我們的程序肯定是已經啟動了,因此也完全不需要使用靜態注冊的功能。
最後我們再來盤點一下使用本地廣播的幾點優勢吧。
1. 可以明確地知道正在發送的廣播不會離開我們的程序,因此不需要擔心機密數據洩
漏的問題。
2. 其他的程序無法將廣播發送到我們程序的內部,因此不需要擔心會有安全漏洞的隱
患。
3. 發送本地廣播比起發送系統全局廣播將會更加高效。
接收無序廣播的接收器接收到廣播的順序是有序的(由優先級決定順序)
接收無序廣播的接收器也一樣可以設置優先級的
動態注冊廣播優先級高於靜態注冊廣播
同等優先級的動態接收器,先注冊的先接收
同等優先級的靜態接收器,接收廣播的順序與 String[] java.io.File.list()順序一致
Ps:這裡有一點需要注意的是,同等優先級的靜態接收器的接收順序具有不確定性,原因就是File.list()的方法返回的順序具有不確定性,如果需要查看某接收器的接收順序,最好是試驗大量的 apk 名。
假設有如下優先級的 5 個接收器
1.動態 A(優先級=1)
2.動態 B(優先級=2)
3.動態 C(優先級=2)
4.靜態 D(優先級=1)
5.靜態 E(優先級=2)
並且 B 先於 C 注冊
那麼實際接收順序應為
B C E A D
也就是說,如果靜態接收器的優先級高於動態接收器的優先級,那麼還是靜態接收器先接收到廣播(比如接收 SMS 廣播)
動態接收器高優先級>動態接收器低優先級>靜態接收器高優先級>靜態接收器低優先級
有些廣播,我們無法用靜態接收器接收,比如 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。
很多病毒程序為了保證自己被終止後能夠再次運行,都會在xml中注冊一些系統廣播,企圖利用這些系統高頻廣播來實現自啟動。
比如在老版本的android系統中,病毒程序可以通過監聽TIME_TICK來啟動自己的service後台運行,做一些隱秘的工作,而且就算自己被kill掉了,也能很快重新啟動。
而一旦這些系統廣播加了flag FLAG_RECEIVER_REGISTERED_ONLY,這些病毒程序就沒轍了。google的這一改動無疑提升了android系統的安全性。
靜態 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 對象。
動態注冊最終會調用到 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 廣播同時處理動態接收器和靜態接收器先將動態接收器與靜態接收器合並,保持著與優先級相同的順序,優先級高的在前面,否則順序不變。靜態接收器與動態接收器優先級相同的話,動態接收器在前。
1、 進程的地址空間在32位操作系統中,進程的地址空間為0到4GB,示意圖如下: 圖1 這裡主要說明一下Stack和Heap:Stack空
前言之前因為項目需求,其中使用到了圖片的單擊顯示取消,圖片平移縮放功能,昨天突然想再加上圖片的旋轉功能,在網上看了很多相關的例子,可是沒看到能同時實現我想要的
年Google發布了新的工具鏈 - Jack(Java Android 編譯工具)和Jill(Jack中間庫鏈接器),它們用於替換存在的javac+dx工具集。本文我將試
一.通知(Notification)的相關概念Notification是一種具有全局效果的通知,它展示在屏幕的頂端,首先會表現為一個圖標的形式,當用戶向下滑動的時候,展示