第一行代碼讀書筆記 5 -- 廣播機制

本篇文章主要介紹以下幾個知識點:

  • 廣播機制的簡介;
  • 接收系統(tǒng)廣播:動態(tài)注冊與靜態(tài)注冊;
  • 發(fā)送自定義廣播;
  • 使用本地廣播;
  • 實戰(zhàn):實現(xiàn)強制下線。
圖片來源于網(wǎng)絡

5.1 廣播機制簡介

Android 中的廣播主要分兩種類型:標準廣播和有序廣播。

  • 標準廣播(Normal broadcasts)
    ??是一種完全異步執(zhí)行的廣播,在廣播發(fā)出之后,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,因此它們之間沒有任何先后順序可 言。這種廣播的效率會比較高,但同時也意味著它是無法被截斷的。標準廣播的工作流程如下:
標準廣播工作示意圖
  • 有序廣播(Ordered broadcasts)
    ??是一種同步執(zhí)行的廣播,在廣播發(fā)出之后,同一時刻只會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執(zhí)行完畢后,廣播才會繼續(xù)傳遞。所以此時的廣播接收器是有先后順序的,優(yōu)先級高的廣播接收器就可以先收到廣播消息,并且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣后面的廣播接收器就無法收到廣播消息了。有序廣播的工作流程如下:
有序廣播工作示意圖

5.2 接收系統(tǒng)廣播

Android 內(nèi)置了很多系統(tǒng)級別的廣播,我們可以在應用程序中通過監(jiān)聽這些廣播來得到各種系統(tǒng)的狀態(tài)信息。若想要接收到這些廣播,就需要使用廣播接收器。

5.2.1 動態(tài)注冊監(jiān)聽網(wǎng)絡變化

注冊廣播的方式有兩種,在代碼中注冊(動態(tài)注冊)和在 AndroidManifest.xml 中注冊(靜態(tài)注冊)。

創(chuàng)建一個廣播接收器:新建一個類,繼承 BroadcastReceiver, 并重寫父類的 onReceive() 方法。當有廣播到來時,onReceive() 方法就會得到執(zhí)行, 具體的邏輯在這個方法中處理。

接下來先通過動態(tài)注冊的方式編寫一個能夠監(jiān)聽網(wǎng)絡變化的程序,學習一下廣播接收器的基本用法。代碼如下:

/**
 * 廣播,動態(tài)監(jiān)聽網(wǎng)絡變化
 */
public class BroadcastActivity extends AppCompatActivity {

    private IntentFilter intentFilter;

    private NetworkChangeReceiver networkChangeReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);
        // 創(chuàng)建 IntentFilter 實例
        intentFilter = new IntentFilter();
        // 添加廣播值
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        // 創(chuàng)建 NetworkChangeReceiver 實例
        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) {
            // 獲取管理網(wǎng)絡連接的系統(tǒng)服務類的實例
            ConnectivityManager connectivityManager = (ConnectivityManager)
                    getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
            // 判斷網(wǎng)絡是否可用
            if (networkInfo != null && networkInfo.isAvailable()){
                ToastUtils.showShort("網(wǎng)絡可用");
            }else {
                ToastUtils.showShort("網(wǎng)絡不可用");
            }

        }
    }
}

注意事項:

  • 動態(tài)注冊的廣播接收器一定都要取消注冊才行,這里我們是在 onDestroy() 方法中通過調(diào)用 unregisterReceiver()方法來實現(xiàn)的。
  • AndroidManifest.xml 文件中加入訪問系統(tǒng)的網(wǎng)絡狀態(tài)權限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

運行程序,然后按下 Home 鍵→按下 Menu 鍵→System settings→Data usage 進入到數(shù)據(jù)使用詳情界面,關閉 Mobile Data 會彈出網(wǎng)絡不可用的提示:

禁用系統(tǒng)網(wǎng)絡

重新打開 Mobile Data 又會彈出網(wǎng)絡可用的提示:

啟用系統(tǒng)網(wǎng)絡

5.2.2 靜態(tài)注冊實現(xiàn)開機啟動

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

接下來讓程序接收一條開機廣播,收到這條廣播時在 onReceive() 方法中相應的邏輯,從而實現(xiàn)開機啟動的功能。Android Studio 創(chuàng)建廣播接收器:右擊com.wonderful.myfirstcode.chapter5(你的包名)→New→Other→Broadcast Receiver,會彈出窗口:

創(chuàng)建廣播接收器的窗口

這里把廣播接收器命名為 BootCompleteReceiver,Exported 表示是否允許此廣播接收本程序以外的廣播,Enabled 表示是否啟用這個廣播接收器。

點擊 Finish 完成創(chuàng)建后,修改 BootCompleteReceiver 中的代碼:

public class BootCompleteReceiver extends BroadcastReceiver {
    
    @Override
    public void onReceive(Context context, Intent intent) {
        // 執(zhí)行相應的邏輯
        ToastUtils.showShort("開機完成");
    }
}

靜態(tài)的廣播接收器一定要在 AndroidManifest.xml 文件中注冊才可使用,剛使用 Android Studio 創(chuàng)建的廣播接收器已經(jīng)自動幫我們完成注冊了,如下:

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

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

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
        . . .

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

</manifest>

接下來修改 AndroidManifest.xml 文件中的標簽 <receiver> 中的內(nèi)容,使 BootCompleteReceiver 接收開機廣播:

<receiver
    android:name=".chapter5.BootCompleteReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
       <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

由于系統(tǒng)啟動后會發(fā)出一條值為 android.intent.action.BOOT_COMPLETED 的廣播,因此在 <intent-filter> 標簽中添加相應的 action。另外,別忘了聲明監(jiān)聽系統(tǒng)開機廣播的權限:

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

這樣程序可以接收到開機廣播了,將模擬器關閉并重啟,如下:

接收系統(tǒng)開機廣播

注意事項:不要在 onReceive() 方法中添加過多的邏輯或者進行耗時操作,因為廣播接收器中是不允許開啟線程的。

5.3 發(fā)送自定義廣播

接下來學習如何在應用程序中發(fā)送自定義廣播,通過實踐的方式來看一下標準廣播和有序廣播的區(qū)別。

5.3.1 發(fā)送標準廣播

在發(fā)送廣播之前,先新建一個廣播接收器 MyBroadcastReceiver 來接收自定義廣播,收到廣播時彈提示,代碼如下:

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        ToastUtils.showShort("收到自定義廣播");
    }
}

然后修改 AndroidManifest.xml 文件中的標簽 <receiver> 中的內(nèi)容,讓 MyBroadcastReceiver 收到一條值為 com.wonderful.myfirstcode.MY_BROADCAST 的廣播:

<receiver
    android:name=".chapter5.MyBroadcastReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
       <action android:name="com.wonderful.myfirstcode.MY_BROADCAST" />
    </intent-filter>
</receiver>

接下來在布局文件中添加一個按鈕作為發(fā)送廣播的觸發(fā)點:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_send_broadcast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發(fā)送廣播"/>

</RelativeLayout>

然后修改 activity 中的代碼,在按鈕點擊事件中加入發(fā)送自定義廣播邏輯,如下:

public class BroadcastActivity extends AppCompatActivity {

    . . .

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);

        Button btn_send_broadcast= (Button) findViewById(R.id.btn_send_broadcast);
        btn_send_broadcast.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 構建 Intent 對象
                Intent intent = new Intent("com.wonderful.myfirstcode.MY_BROADCAST");
                // 發(fā)送廣播
                sendBroadcast(intent);
            }
        });
      . . .
    }

   . . .
}

運行程序,點擊按鈕,效果如下:

接收到自定義廣播

這樣就完成了發(fā)送自定義廣播的功能。另外,由于廣播是使用 Intent 進行傳遞的,因此你還可以在 Intent 中攜帶一些數(shù)據(jù)傳遞給廣播接收器。

5.3.1 發(fā)送有序廣播

廣播是一種可以跨進程的通信方式,這一點從前面接收系統(tǒng)廣播的時候可以看出來。因此在我們應用程序內(nèi)發(fā)出的廣播,其他的應用程序應該也是可以收到的。為了驗證這一點,我們需要再新建一個 BroadcastTest2 項目。

項目創(chuàng)建好后,定義一個接收上一小節(jié)中的自定義廣播的廣播接收器 AnotherBroadcastReceiver,如下:

public class AnotherBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "另外一個廣播接收器收到廣播了!", Toast.LENGTH_SHORT).show();
    }
}

然后修改 AndroidManifest.xml 文件中的標簽 <receiver> 中的內(nèi)容,讓 AnotherBroadcastReceiver 同樣收到一條值為 com.wonderful.myfirstcode.MY_BROADCAST 的廣播:

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

現(xiàn)在把項目 BroadcastTest2 安裝到模擬器上,然后重新回到上一個項目的界面上,點擊發(fā)送廣播按鈕,就會彈兩次提示信息,如圖:

兩個程序都收到自定義廣播 1
兩個程序都收到自定義廣播 2

到目前為止,發(fā)送的都是標準廣播,接下來嘗試一下發(fā)送有序廣播。 回到前面的項目,修改 Activity 中的代碼,如下:

public class BroadcastActivity extends AppCompatActivity {

    . . .

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);

        Button btn_send_broadcast = (Button) findViewById(R.id.btn_send_broadcast);
        btn_send_broadcast.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 構建 Intent 對象
                Intent intent = new Intent("com.wonderful.myfirstcode.MY_BROADCAST");
                // 發(fā)送標準廣播
                //sendBroadcast(intent);
                // 發(fā)送有序廣播
                sendOrderedBroadcast(intent, null);
                
            }
        });

        . . .
    }

    . . .
}

發(fā)送有序廣播 sendOrderedBroadcast() 方法接收兩個參數(shù),第一個是 Intent,第二個是與權限相關的字符串,在這傳 null 就行了。當然現(xiàn)在運行程序點擊按鈕兩個應用程序還是會收到廣播,接下來還需要修改 AndroidManifest.xml 文件中的標簽<receiver>中的代碼,設定廣播接收器的先后順序:

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

上述代碼,通過 android:priority 屬性給廣播接收器設置了優(yōu)先級,優(yōu)先級比較高的廣播接收器就可以先收到廣播。把 MyBroadcastReceiver 的優(yōu)先級設成了 100,保證它一定會在 AnotherBroadcastReceiver 之前收到廣播。

接下來,修改 MyBroadcastReceiver 中的代碼,設置是否允許廣播繼續(xù)傳遞。在 onReceive() 方法中調(diào)用 abortBroadcast() 方法,表示將這條廣播截斷,后面的廣播接收器將無法再接收到這條廣播。如下:

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        ToastUtils.showShort("收到自定義廣播");
        // 將這條廣播攔截
        abortBroadcast();
    }
}

現(xiàn)在重新運行程序,就只有 MyBroadcastReceiver 可以接收到廣播彈提示信息了。

5.4 使用本地廣播

本地廣播只能夠在應用程序的內(nèi)部進行傳遞,并且廣播接收器也只能接收來自本應用程序發(fā)出的廣播。本地廣播的用法并不復雜,主要使用了一個 LocalBroadcastManager 來對廣播進行管理,并提供了發(fā)送廣播和注冊廣播接收器的方法。

通過具體的實例來嘗試一下它的用法,修改 Activity 中的代碼:

public class BroadcastActivity extends AppCompatActivity {

    private IntentFilter intentFilter;

    private LocalReceiver localReceiver;

    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);

        // 獲取實例
        localBroadcastManager = LocalBroadcastManager.getInstance(this);

        Button btn_send_broadcast = (Button) findViewById(R.id.btn_send_broadcast);
        btn_send_broadcast.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 構建 Intent 對象
                Intent intent = new Intent("com.wonderful.myfirstcode.LOCAL_BROADCAST");
                // 發(fā)送有序廣播
                localBroadcastManager.sendBroadcast(intent);

            }
        });

        // 創(chuàng)建 IntentFilter 實例
        intentFilter = new IntentFilter();
        // 添加廣播值
        intentFilter.addAction("com.wonderful.myfirstcode.LOCAL_BROADCAST");
        // 創(chuàng)建 LocalReceiver 實例
        localReceiver = new LocalReceiver();
        // 注冊本地廣播監(jiān)聽器
        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) {
            ToastUtils.showShort("收到本地廣播");
        }
    }
}

基本上就和前面的動態(tài)注冊廣播接收器以及發(fā)送廣播的代碼是一樣。
??運行效果如下:

接收本地廣播

另外還有一點需要說明,本地廣播是無法通過靜態(tài)注冊的方式來接收的。

最后盤點一下使用本地廣播的優(yōu)勢:

  • 可以明確知道正在發(fā)送的廣播不會離開我們的程序,不需要擔心機密數(shù)據(jù)泄漏的問題。

  • 其他的程序無法將廣播發(fā)送到我們程序的內(nèi)部,不需要擔心會有安全漏洞的隱患。

  • 發(fā)送本地廣播比起發(fā)送系統(tǒng)全局廣播將會更加高效。

5.5 廣播的最佳實踐——實現(xiàn)強制下線功能

又到了實戰(zhàn)環(huán)節(jié)了。此次的需求是:強制下線后在界面上彈出一個對話框,讓用戶無法進行任何其他操作,必須要點擊對話框中的確定按鈕, 然后回到登錄界面。

借助本次所學的廣播知識,可以輕松實現(xiàn)這一功能。

首先,創(chuàng)建一個 ActivityCollector 類用于管理所有的活動:

/**
 * 活動管理器
 * Created by KXwon on 2016/12/9.
 */

public class ActivityCollector {
    // 通過一個List來緩存活動
    public static List<Activity> activities = new ArrayList<Activity>();

    /**
     * 用于向List中添加一個活動
     * @param activity
     */
    public static void addActivity(Activity activity) {
        activities.add(activity);
    }

    /**
     * 用于從List中移除活動
     * @param activity
     */
    public static void removeActivity(Activity activity) {
        activities.remove(activity);
    }

    /**
     * 將List中存儲的活動全部銷毀掉
     */
    public static void finishAll() {
        for (Activity activity : activities) {
            if (!activity.isFinishing()) {
                activity.finish();
            }
        }
    }
}

然后創(chuàng)建 BaseActivity 類作為所有活動的父類(忽略一些不必要的內(nèi)容,主要看 ActivityCollector 相關的):

/**
 * 基類
 * Created by KXwon on 2016/12/9.
 */

public abstract class BaseActivity extends AppCompatActivity {

    protected Context mContext;
    protected Unbinder mBinder;

    /**
     * 初始化布局id
     * @return 布局id
     */
    protected abstract int initLayoutId();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(initLayoutId());

        Log.d("BaseActivity", getClass().getSimpleName());// 知曉當前是在哪一個活動
        ActivityCollector.addActivity(this);// 將當前正在創(chuàng)建的活動添加到活動管理期里

        mContext = this;
        mBinder = ButterKnife.bind(this);

    }


    @Override
    protected void onDestroy() {
        // 取消綁定
        mBinder.unbind();
        super.onDestroy();
        // 將一個馬上要銷毀的活動從管理器里移除
        ActivityCollector.removeActivity(this);
    }
}

接下來創(chuàng)建一個登錄界面,新建 LoginActivity,編輯 activity_login.xml 如下:

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

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:gravity="center"
            android:textSize="18sp"
            android:text="賬號:"/>

        <EditText
            android:id="@+id/et_account"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical"/>
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:gravity="center"
            android:textSize="18sp"
            android:text="密碼:"/>

        <EditText
            android:id="@+id/et_password"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical"/>
    </LinearLayout>

    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_margin="10dp"
        android:text="登錄"/>

</LinearLayout>

接著修改 LoginActivity 中的代碼:

public class LoginActivity extends BaseActivity {

    private EditText et_account, et_password;
    private Button btn_login;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        et_account = (EditText) findViewById(R.id.et_account);
        et_password = (EditText) findViewById(R.id.et_password);
        btn_login = (Button) findViewById(R.id.btn_login);

        btn_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String account = et_account.getText().toString();
                String password = et_password.getText().toString();
                // 若賬號是 wonderful 且密碼是 123456,就認為登錄成功
                if (account.equals("wonderful") && password.equals("123456")){
                    // 登錄成功跳轉(zhuǎn)到主界面
                    IntentUtils.myIntent(LoginActivity.this,ForceOfflineActivity.class);
                    finish();
                }else {
                    ToastUtils.showShort("賬號或密碼無效!");
                }
            }
        });

    }

    @Override
    protected int initLayoutId() {
        return R.layout.activity_login;
    }
}

登錄成功后跳轉(zhuǎn)到 ForceOfflineActivity 主界面,在主界面加入一個強制下線的功能即可,其布局 activity_force_offline.xml 代碼為:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">

    <Button
        android:id="@+id/btn_force_offline"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發(fā)送強制下線廣播"/>
    
</RelativeLayout>

只有一個按鈕,用于觸發(fā)強制下線功能,然后修改 ForceOfflineActivity 中的代碼:

public class ForceOfflineActivity extends BaseActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Button btn_force_offline = (Button) findViewById(R.id.btn_force_offline);
        btn_force_offline.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("com.wonderful.myfirstcode.FORCE_OFFLINE");
                // 發(fā)送強制下線廣播
                sendBroadcast(intent);
            }
        });
    }

    @Override
    protected int initLayoutId() {
        return R.layout.activity_force_offline;
    }
}

上述代碼,在按鈕的點擊事件里面發(fā)送了一條廣播,廣播的值為 com.wonderful.myfirstcode.FORCE_OFFLINE,這條廣播就是用于通知程序強制用戶下線的。也就是說強制用戶下線的邏輯并不是寫在 ForceOfflineActivity 里的,而是寫在接收這條廣播的廣播接收器里面,這樣強制下線的功能就不會依附于任何的界面,不管是在程序的任何地方,只需要發(fā)出這樣一條廣播,就可以完成強制下線的操作了。

毫無疑問,要在 BaseActivity 中動態(tài)注冊一個廣播接收器,因為所有的活動都繼承 BaseActivity 的,修改 BaseActivity 如下:

/**
 * 基類
 * Created by KXwon on 2016/12/9.
 */

public abstract class BaseActivity extends AppCompatActivity {

    protected Context mContext;
    protected Unbinder mBinder;

    private ForceOfflineReceiver receiver;

    /**
     * 初始化布局id
     * @return 布局id
     */
    protected abstract int initLayoutId();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(initLayoutId());

        Log.d("BaseActivity", getClass().getSimpleName());// 知曉當前是在哪一個活動
        ActivityCollector.addActivity(this);// 將當前正在創(chuàng)建的活動添加到活動管理期里

        mContext = this;
        mBinder = ButterKnife.bind(this);

    }

    @Override
    protected void onResume() {
        super.onResume();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.wonderful.myfirstcode.FORCE_OFFLINE");
        receiver = new ForceOfflineReceiver();
        registerReceiver(receiver,intentFilter);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (receiver != null){
            unregisterReceiver(receiver);
            receiver = null;
        }
    }

    @Override
    protected void onDestroy() {
        // 取消綁定
        mBinder.unbind();
        super.onDestroy();
        // 將一個馬上要銷毀的活動從管理器里移除
        ActivityCollector.removeActivity(this);
    }

    class ForceOfflineReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(final Context context, Intent intent) {
            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setTitle("警告");
            builder.setMessage("你已被下線,請重新登錄");
            // 設置不可取消
            builder.setCancelable(false);
            // 設置點擊事件
            builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    // 銷毀所有活動
                    ActivityCollector.finishAll();
                    // 重新啟動 LoginActivity
                    IntentUtils.myIntent(context, LoginActivity.class);
                }
            });
            builder.show();
        }
    }
}

上述代碼中,重寫了 onResume()onPause() 這兩個生命周期的函數(shù),然后分別在這兩個方法里注冊和取消注冊了 ForceOfflineReceiver,之所以這樣寫而不是在 onCreate()onDestroy() 方法里注冊和取消注冊是因為我們始終需要保證只有處于棧頂?shù)幕顒硬拍芙邮盏较戮€廣播,非棧頂活動不必要接收這條廣播,寫在 onResume()onPause() 方法里很好的解決了這個問題,當活動失去棧頂位置時就會自動取消廣播接收器的注冊。

現(xiàn)在,所有強制下線的邏輯處理完了,接下來運行程序,首先進入登錄界面:

登陸界面

輸入正確的賬號和密碼后,點擊登錄進入主界面:

主界面

這時點擊按鈕,就會發(fā)出一條強制下線的廣播,ForceOfflineReceiver 接收到這條廣播后會彈出一個提示對話框,這時就無法對其他界面的任何元素進行操作,只能點擊 OK 按鈕,重新回到登錄界面,如下:

強制下線提示

關于廣播機制的學習就到這,下篇文章將進入持久化技術的學習。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,711評論 19 139
  • 第5章 全局大喇叭-詳解廣播機制 了解網(wǎng)絡通信原理的應該會知道,在一個IP網(wǎng)絡范圍中,最大的IP地址是被保留作為廣...
    努力生活的西魚閱讀 752評論 0 1
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,361評論 25 708
  • 小敏再次敲門進來的時候,李汴剛洗漱完畢從衛(wèi)生間出來。 “對不起,剛剛忘了問你什么時候走?”小敏問他。 ...
    囍舍閱讀 372評論 0 0
  • 如果我愛你,而你正巧也愛我———— 那你生病的時候,我會去照顧你,陪著你到好。 你騎車的時候,我會要你小心一點,還...
    再見吧喵小姐i閱讀 739評論 0 1

友情鏈接更多精彩內(nèi)容