天天看點

學習 | Android開發之詳解廣播機制

學習 | Android開發之詳解廣播機制

轉載請标明出處:http://blog.csdn.net/junzaivip/article/details/56836218,本文出自【junzaivip部落格】

案例最後效果:

學習 | Android開發之詳解廣播機制

廣播的類型:

标準廣播

标準廣播是完全異步的廣播, 也就是當廣播發出之後, 所有的廣播接收器機會都會在同一時刻接收到這條廣播, 标準廣播無法被攔截。

有序廣播

有序廣播是同步執行的廣播, 也就是當廣播發出之後, 同一時刻隻會有一個廣播接收器能夠收到這條廣播消息, 依次傳遞。優先級高的廣播接收器可以優先接收廣播消息, 同時前面的廣播接收器還可以截取正在傳遞的廣播, 這樣後面的廣播就無法收到廣播消息了。

接收系統廣播

Android内置了很多系統級别的廣播, 比如:手機開機完成後會發出一條廣播, 電池電量發生變化會發出一條廣播, 時間或者時區發生變化會發出一條廣播等等。如果希望接收這些廣播, 就需要使用廣播接收器, 下面來看具體用法。

動态注冊監聽網絡變化

注冊廣播一般有兩種方式:

動态注冊:在代碼中注冊

靜态注冊:在AndroidManifest.xml中注冊

首先我們來嘗試一下動态注冊:

隻需要建立一個類BroadcaseDemo, 讓它繼承自Broadcast-Receiver,并重寫父類的onReceive()方法就行了. onReceive()用來監聽廣播的到來, 具體邏輯也可以在這個方法中處理.

MainActivity.java

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

說明: NetWorkChangeReceiver ()繼承BroadcastReceiver, 并重寫了父類的onReceive()方法. 這樣每當網絡發生變化的時候, onReceive()方法就會得到執行.

onCreate()方法, 我們首先建立一個IntentFilter的執行個體, 并未它添加了一個值為android.net.conn.CONNECTIVITY_CHANGE的action, 為什麼要添加這個呢?因為當網絡發生變化的時候, 系統發出的正是一條值為android.net.conn.CONNECTIVITY_CHANGE的廣播. 也就是說我們的廣播接收器想要監聽什麼廣播, 就在這裡添加相應的action.

接下來建立一個NetWorkChangeReceiver的執行個體, 然後調用registerReceiver()方法進行注冊.

切記: 動态注冊的廣播接收器一定要取消注冊才行, 這裡我們是在onDestroy()方法中調用unregisterReceiver()方法來實作.

運作結果:

學習 | Android開發之詳解廣播機制

下面繼續進行優化, 直接判斷之後告訴使用者, 目前環境是否有網.

MainActivity.java

class NetWorkChangeReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            // 擷取ConnectivityManager執行個體, 這是一個系統服務類, 專門用于管理網絡連接配接
            ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
            // 調用getActiveNetworkInfo()得到NetworkInfo的執行個體
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
            // 通過isAvailable()方法判斷目前是否有網絡
            if(networkInfo!= null && networkInfo.isAvailable()){
                Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
            }
        }
    }
           

注意: Android系統為了保護使用者裝置的安全和隐私, 做了嚴格的規定: 如果程式需要進行一些對使用者來說比較敏感的操作, 就必須在配置檔案AndroidManifest.xml中聲明權限才可以, 否者程式會直接崩潰. 比如這裡通路系統的網絡狀态就是需要聲明權限的, 是以我們需要添加下面的代碼.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.junzaivip.broadcastdemo">
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
...
</manifest>
           
效果示範:
學習 | Android開發之詳解廣播機制
靜态注冊實作開機啟動

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

準備實作一個開機啟動的功能, 我們準備讓程式接收一條開機廣播, 當收到這條廣播時, 就可以在onReceive()方法裡執行相應的邏輯.

下面我們使用AS的快捷方式來建立一個廣播接收器.

學習 | Android開發之詳解廣播機制

可以看到我們将廣播接收器命名為BootCompleteReceiver, Exported屬性表示是否允許這個廣播接收器接收本程式以外的廣播, Enabled屬性表示是否啟用這個廣播接收器, 預設是勾選的, 然後點選finish完成建立.

修改完之後的代碼如下:

public class BootCompleteReceive extends BroadcastReceiver {
    public BootCompleteReceive() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
    }
}
           

代碼非常簡單, 我們隻是在onReceive()方法中使用Toast彈出一段提示資訊.

另外, 靜态的廣播接收器一定要在AndroidManifest.xml檔案中注冊才可以使用, 不過由于我們是使用的Android Studio的快捷方式建立的廣播接收器, 是以注冊這一步已經被自動完成了. 我們一起去看一看.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.junzaivip.broadcastdemo">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".BootCompleteReceive"
            android:enabled="true"
            android:exported="true"></receiver>
    </application>

</manifest>
           

說明: 可以看到在</application>的标簽内出現了一個新的标簽<receiver>, 所有的靜态廣播接收器都是在這裡進行注冊的, 它的用法其實和<activity>标簽非常相似, 也是通過android:name來指定具體哪一個廣播接收器, 而android:enabled 和android:exported屬性則是根據我們剛才勾選的狀态自動生成的.

不過目前BootCompleteReceive還是不能接收到開機廣播, 我們需要對AndroidManifest.xml檔案進行修改才行, 如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.junzaivip.broadcastdemo">
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".BootCompleteReceive"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
            </intent-filter>

        </receiver>
    </application>

</manifest>
           

主要更新了下面的檔案:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<intent-filter>

<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>

</intent-filter>

說明: 由于Android系統啟動完成後, 會發出android.permission.RECEIVE_BOOT_COMPLETED的廣播, 是以我們在<intent-filter>标簽裡添加了相應的action. 另外監聽系統開機廣播也是需要聲明權限的, 是以添加了一條<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>權限.

當然我們程式中隻是簡單使用了Toast提示了一段文本資訊, 當你真正在項目中使用的時候, 就可以添加自己的邏輯.

需要注意的是, 不要在onReceive()方法中添加過多的邏輯或者進行任何的耗時操作, 因為在廣播接收器中是不允許開啟線程的. onReceive()方法中進行了較長時間而沒有結束時, 程式就會報錯.

廣播接收器, 更多的是扮演一種打開程式或者其他元件的角色, 比如建立一條狀态欄通知, 或者啟動一個服務等.

發送自定義廣播

前面的例子已經了解了接收系統廣播, 接下來我們一起來學習在應用程式中發送自定義廣播.

發送标準廣播

首先定義一個廣播接收器用來接收廣播, 代碼如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.junzaivip.broadcastdemo">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.junzaivip.broadcastdemo.MY_BROADCAST"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>
           

可以看到, 這裡讓MyBroadcastReceiver接收一條值為com.junzaivip.broadcastdemo.MY_BROADCAST的廣播, 是以待會兒在發送廣播的時候, 就需要發出一條這樣的廣播.

接下來修改activity_main.xml中的代碼, 如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/send_cast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="@string/send_broadcast"/>
</LinearLayout>
           

在布局中定義了一個按鈕, 用于發送廣播. 然後修改MainActivity中的代碼.

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button sendButton = (Button) findViewById(R.id.send_cast);
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("com.junzaivip.broadcastdemo.MY_BROADCAST");
                sendBroadcast(intent);
            }
        });
    }
           

說明: 按鈕的點選事件裡, 加入了自定義廣播的邏輯. 首先建構出一個對象Intent對象, 并把要發送廣播的值傳入, 然後調用sendBroadcast()方法将廣播發送出去, 這樣所有監聽com.junzaivip.broadcastdemo.MY_BROADCAST這條廣播的廣播接收器就會收到消息. 此時發出去的廣播就是一條标準廣播. 由于廣播是通過Intent進行傳遞, 是以還可以在Intent中添加一些資料傳遞給廣播接收器.

運作效果:

學習 | Android開發之詳解廣播機制
擴充 通過Intent來傳遞和接收資料方式:

通過intent來傳遞資料(字元串):

Intent intent = new Intent();

intent.setClass(DetailActivity.this, HistoryActivity.class);

intent.putExtra("","");

intent.putExtra("","");

intent.putExtra("","");

startActivity(intent);

通過intent來傳遞對象, 則在javabean需要實作序列化接口

import java.io.Serializable;

public class DetailModel implements Serializable{

}

Intent intent = new Intent();

intent.setClass(LeaderScheduleClientDetailActivity.this, LeaderScheduleContactHistoryActivity.class);

Bundle bundle = new Bundle();

bundle.putSerializable("contactHistorys",item);

intent.putExtras(bundle);

startActivity(intent);

接收intent傳遞過來的字元串:

true_exhibitionID = getIntent().getStringExtra("true_exhibitionID");

接收intent傳遞過來的對象

Bundle bundle = getIntent().getExtras();

model = (GroupModel) bundle.getSerializable("groupModel");

發送有序廣播

因為廣播是一種可以跨程序的通信方式, 是以我們應用程式内發出的廣播, 其他的應用程式應該也是可以收到的.

下面我們就來一個例子, 通過第一個程式發送一個廣播, 打開另外一個程式, 并且在第二個程式中顯示自定義内容.

在另外一個程式中添加AnotherBroadcastReceiver, 代碼如下:

public class AnotherBroadcastReceiver extends BroadcastReceiver {
    public AnotherBroadcastReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent();
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.setClass(context,MainActivity.class);
        context.startActivity(i);
        Toast.makeText(context, "另外一個廣播", Toast.LENGTH_SHORT).show();
    }
}
           

在AndroidManifest.xml中配置:

<receiver
            android:name=".AnotherBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.junzaivip.broadcastdemo.MY_BROADCAST"/>
            </intent-filter>
</receiver>
           

說明: Intent i = new Intent();

i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

i.setClass(context,MainActivity.class);

context.startActivity(i); 這一段就是核心代碼, 用來打開另外一個程式.

這樣就可以通過廣播打開另外一個應用程式了, 也可以顯示内容. 下面是效果示範:

學習 | Android開發之詳解廣播機制

這樣就強有力的證明了, 我們的應用程式發出的廣播是可以被其他應用程式接收到的.

不過到目前為止, 程式裡發出的廣播都還是标準廣播, 現在我們來嘗試一下發送有序廣播.

重新回到broadcastDemo項目, 然後修改MainActivity中的代碼, 如下:

将 sendBroadcast(intent); 修改為 sendOrderedBroadcast(intent,null);

可以看到發送有序廣播隻需要改動一行代碼, 即将sendBroadcast()方法改成sendOrderedBroadcast()方法.

sendOrderedBroadcast()方法接收兩個參數, 第一個參數仍然是Intent, 第二個參數是一個與權限相關的字元串, 這裡傳入null就行了. 現在重新運作程式, 并點選Send Broadcast按鈕, 你會發現兩個應用程式都還是可以接收到這條廣播.

看上去貌似沒什麼差別, 這個時候廣播接收器是有先後順序的. 而且前面的廣播接收器還可以将廣播截斷, 以阻止其繼續傳播.

通過android:priority屬性給廣播接收器設定優先級, 優先級比較高的廣播就可以先收到廣播. 下面給它設定了100, 以保證它一定會會在另一個之前收到廣播. 同時它也有權限來決定是否允許廣播繼續傳遞.

AndroidManifest.xml

<receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100">
                <action android:name="com.junzaivip.broadcastdemo.MY_BROADCAST"/>
            </intent-filter>
        </receiver>
           

下面我們來配置讓第一個應用程式阻止繼續傳遞.

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "歡迎關注公衆号:史慧君, 測試接收一個自定義廣播", Toast.LENGTH_SHORT).show();
        abortBroadcast();
    }
}
           

說明:

abortBroadcast();表示将這條廣播階段, 後面的廣播接收器将無法再繼續接收這條廣播.



繼續閱讀