Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android四大組件之BroadcastReceiver

Android四大組件之BroadcastReceiver

編輯:關於Android編程

廣播機制簡介

Android中的廣播主要可以分為兩種類型,標准廣播有序廣播

標准廣播(Normal broadcasts)是一種完全異步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,因此它們之間沒有任何先後順序可言。這種廣播的效率會比較高,但同時也意味著它是無法被截斷的。標准廣播的工作流程如圖所示。

\

 

有序廣播(Orderedbroadcasts)則是一種同步執行的廣播,在廣播發出之後,同一時刻 只會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播才會繼續傳遞。所以此時的廣播接收器是有先後順序的,優先級高的廣播接收器就可以先收到廣播消息,並且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣後面的廣播接收器 就無法收到廣播消息了。有序廣播的工作流程如圖所示。

\

 

注冊廣播的方式一般有兩種,在代碼中注冊和在 AndroidManifest.xml中注冊,其中前者也被稱為動態注冊,後者也被稱為靜態注冊

需要注意的是,不要在onReceive()方法中添加過多的邏輯或者進行任何的耗時操作,因為在廣播接收器中不允許開啟線程的,當 onReceive()方法運行了較長時間而沒有結束時,程序就會報錯。 因此廣播接收器更多的是扮演一種打開程序其他組件的角色,比如創建一條狀態欄通知,或者啟動一個服務。

動態注冊監聽網絡變化

那麼該如何創建一個廣播接收器呢?其實只需要新建一個類,讓它繼承自BroadcastReceiver, 並重寫父類的 onReceive()方法就行了。這樣當有廣播到來時,onReceive()方法就會得到執行, 具體的邏輯就可以在這個方法中處理。

那我們就先通過動態注冊的方式編寫一個能夠監聽網絡變化的程序,借此學習一下廣播 接收器的基本用法吧。新建一個 BroadcastTest項目,然後修改 MainActivity中的代碼,如下所示:

public class Main2Activity extends AppCompatActivity {

    private IntentFilter intentFilter;
    private NetworkChangeReceiver networkChangeReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(networkChangeReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(networkChangeReceiver);
    }

    class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {

            Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
        }
    }
}

可以看到,我們在 MainActivity 中定義了一個內部類 NetworkChangeReceiver,這個類是繼承自 BroadcastReceiver的,並重寫了父類的onReceive()方法。這樣每當網絡狀態發生變化時,onReceive()方法就會得到執行,這裡只是簡單地使用 Toast提示了一段文本信息。

然後觀察 onCreate()方法,首先我們創建了一個 IntentFilter的實例,並給它添加了一個值為 android.net.conn.CONNECTIVITY_CHANGE 的 action,為什麼要添加這個值呢?因為當網絡狀態發生變化時,系統發出的正是一條值為 android.net.conn.CONNECTIVITY_ CHANGE 的廣播,也就是說我們的廣播接收器想要監聽什麼廣播,就在這裡添加相應的 action就行了。接下來創建了一個 NetworkChangeReceiver的實例,然後調用registerReceiver()方法進行注冊,將 NetworkChangeReceiver 的實例和IntentFilter 的實例都傳了進去,這樣 NetworkChangeReceiver就會收到所有值為android.net.conn.CONNECTIVITY_CHANGE的廣 播,也就實現了監聽網絡變化的功能。

最後要記得,動態注冊的廣播接收器一定都要取消注冊才行,這裡我們是在 onDestroy() 方法中通過調用 unregisterReceiver()方法來實現的。

靜態注冊實現開機啟動

動態注冊的廣播接收器可以自由地控制注冊與注銷,在靈活性方面有很大的優勢,但是它也存在著一個缺點,即必須要在程序啟動之後才能接收到廣播,因為注冊的邏輯是寫在 onCreate()方法中的。那麼有沒有什麼辦法可以讓程序在未啟動的情況下就能接收到廣播呢?這就需要使用靜態注冊的方式了。

這裡我們准備讓程序接收一條開機廣播,當收到這條廣播時就可以在 onReceive()方法裡執行相應的邏輯,從而實現開機啟動的功能。新建一個 BootCompleteReceiver 繼承自 BroadcastReceiver,代碼如下所示:

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
    }
}

可以看到,這裡不再使用內部類的方式來定義廣播接收器,因為稍後我們需要在 AndroidManifest.xml中將這個廣播接收器的類名注冊進去。在 onReceive()方法中,還是簡單地使用 Toast彈出一段提示信息。 然後修改 AndroidManifest.xml文件,代碼如下所示:

……
    
    
    
    ……
        
            
		
            
        
    
 

終於,標簽內出現了一個新的標簽,所有靜態注冊的廣播接收器都是在這裡進行注冊的。它的用法其實和標簽非常相似,首先通過 android:name 來指定具體注冊哪一個廣播接收器,然後在標簽裡加入想要接收的廣播就行了,由於Android系統啟動完成後會發出一條值為android.intent.action.BOOT_COMPLETED的廣 播,因此我們在這裡添加了相應的action。

另外,監聽系統開機廣播也是需要聲明權限的,可以看到,我們使用 標簽又加入了一條 android.permission.RECEIVE_BOOT_COMPLETED權限。

需要注意的是,不要在 onReceive()方法中添加過多的邏輯或者進行任何的耗時操作,因為在廣播接收器中是不允許開啟線程的,當 onReceive()方法運行了較長時間而沒有結束時,程序就會報錯。 因此廣播接收器更多的是扮演一種打開程序其他組件的角色,比如創建一條狀態欄通知,或者啟動一個服務。

發送自定義廣播

發送標准廣播:

在發送廣播之前,我們還是需要先定義一個廣播接收器來准備接收此廣播才行,不然發出去也是白發。因此新建一個MyBroadcastReceiver繼承自BroadcastReceiver,代碼如下所示:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
    }
}

然後在 AndroidManifest.xml中對這個廣播接收器進行注冊:


    ……
    
        ……
        
            
                
            
        
    

這裡在布局文件中定義了一個按鈕,用於作為發送廣播的觸發點。然後修改 MainActivity 中的代碼,如下所示:

public class MainActivity extends Activity {
    ……
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest. MY_BROADCAST");
                sendBroadcast(intent);
            }
        });
        ……
    }
    ……
}

可以看到,我們在按鈕的點擊事件裡面加入了發送自定義廣播的邏輯。首先構建出了一 個Intent對象,並把要發送的廣播的值傳入,然後調用了 Context的 sendBroadcast()方法將廣播發送出去,這樣所有監聽 com.example.broadcasttest.MY_BROADCAST這條廣播的廣播接收器就會收到消息。此時發出去的廣播就是一條標准廣播。

發送有序廣播:

廣播是一種可以跨進程的通信方式,這一點從前面接收系統廣播的時候就可以看出來了。因此在我們應用程序內發出的廣播,其他的應用程序應該也是可以收到的

不過到目前為止,程序裡發出的都還是標准廣播,現在我們來嘗試一下發送有序廣播。關閉 BroadcastTest2項目,然後修改 MainActivity中的代碼,如下所示:

public class MainActivity extends Activity {
    ……
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest. MY_BROADCAST");
                sendOrderedBroadcast(intent, null);
            }
        });
        ……
    }
    ……
}

可以看到,發送有序廣播只需要改動一行代碼,即將sendBroadcast()方法改成 sendOrderedBroadcast()方法。sendOrderedBroadcast()方法接收兩個參數,第一個參數仍然是 Intent,第二個參數是一個與權限相關的字符串,這裡傳入 null就行了。現在重新運行程序,並點擊 SendBroadcast按鈕,你會發現,兩個應用程序仍然都可以接收到這條廣播。

那麼該如何設定廣播接收器的先後順序呢?當然是在注冊的時候進行設定的了,修改 AndroidManifest.xml中的代碼,如下所示:


    ……
    
        ……
        
            
                
            
        
    

可以看到,我們通過 android:priority屬性給廣播接收器設置了優先級,優先級比較高的廣播接收器就可以先收到廣播。這裡將 MyBroadcastReceiver的優先級設成了 100,以保證它一定會在 AnotherBroadcastReceiver之前收到廣播。

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceive", Toast.LENGTH_SHORT).show();
        abortBroadcast();
    }
}

如果在 onReceive()方法中調用了abortBroadcast()方法,就表示將這條廣播截斷後面的廣播接收器將無法再接收到這條廣播。現在重新運行程序,並點擊一下 SendBroadcast按鈕, 你會發現,只有 MyBroadcastReceiver 中的 Toast 信息能夠彈出,說明這條廣播經過 MyBroadcastReceiver之後確實是終止傳遞了。

本地廣播:

前面我們發送和接收的廣播全部都是屬於系統全局廣播即發出的廣播可以被其他任何的任何應用程序接收到,並且我們也可以接收來自於其他任何應用程序的廣播。這樣就很容 易會引起安全性的問題,比如說我們發送的一些攜帶關鍵性數據的廣播有可能被其他的應用程序截獲,或者其他的程序不停地向我們的廣播接收器裡發送各種垃圾廣播。

為了能夠簡單地解決廣播的安全性問題,Android引入了一套本地廣播機制,使用這個機制發出的廣播只能夠在應用程序的內部進行傳遞,並且廣播接收器也只能接收來自本應用程序發出的廣播,這樣所有的安全性問題就都不存在了。 本地廣播的用法並不復雜,主要就是使用了一個 LocalBroadcastManager來對廣播進行管理,並提供了發送廣播和注冊廣播接收器的方法。

優勢:

1. 可以明確地知道正在發送的廣播不會離開我們的程序,因此不需要擔心機密數據洩漏的問題。

2. 其他的程序無法將廣播發送到我們程序的內部,因此不需要擔心會有安全漏洞的隱患。

3. 發送本地廣播比起發送系統全局廣播將會更加高效。

本地廣播的用法並不復雜,主要就是使用了一個 LocalBroadcastManager來對廣播進行管理,並提供了發送廣播和注冊廣播接收器的方法。下面我們就通過具體的實例來嘗試一下 它的用法,修改 MainActivity中的代碼,如下所示:

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();
        }
    }
}

其實這基本上就和我們前面所學的動態注冊廣播接收器以及發送廣播的代碼是一樣。只不過現在首先是通過LocalBroadcastManagergetInstance()方法得到了它的一個實例,然後在注冊廣播接收器的時候調用的是 LocalBroadcastManager 的registerReceiver()方法,在發送廣播的時候調用的是LocalBroadcastManager的sendBroadcast() 方法,僅此而已。

另外還有一點需要說明,本地廣播是無法通過靜態注冊的方式來接收的。其實這也完全可以理解,因為靜態注冊主要就是為了讓程序在未啟動的情況下也能收到廣播,而發送本地廣播時,我們的程序肯定是已經啟動了,因此也完全不需要使用靜態注冊的功能。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved