第1章4つの主要コンポーネント(4つ)のBroadcastReceiver

第1章4つのコンポーネント

4番目のコンポーネントBroadcastReceiver

(1)定義

ブロードキャストはグローバルリスナーであり、Androidの4つの主要コンポーネントの1つに属しています。Androidブロードキャストは、ブロードキャスト送信側とブロードキャスト受信側の2つの役割に分けられます。

(2)機能

アプリから送信されたブロードキャストメッセージを聞いて受信し、応答する

(3)アプリケーションシナリオ

3.1)Androidの異なるコンポーネント間の通信(アプリケーション内/異なるアプリケーション間の通信を含む)
3.2)マルチスレッド通信
3.3)特定の状況におけるAndroidシステムとの通信例:電話が着信したとき、ネットワークが利用可能なとき

(4)実施原則

Androidのブロードキャストは、デザインパターンのオブザーバーパターンを使用します。メッセージベースのパブリッシュ/サブスクライブイベントモデルです
。Androidは、ブロードキャストの送信側と受信側を切り離し、システムの統合と拡張を容易にし
ます。モデルには3つの役割があります
。メッセージサブスクライバー(ブロードキャストレシーバー)
2.メッセージパブリッシャー(ブロードキャストパブリッシャー)
3.メッセージセンター(AMS、アクティビティマネージャーサービス)
ここに画像の説明を挿入
オブザーバーパターン:
オブザーバーデザインパターンは、オブジェクトの1対多の組み合わせを定義します。関係。オブジェクトの状態が変化すると、それに依存するすべてのオブジェクトに通知され、自動的に更新されます。

(5)使用プロセス

[付録2:ボードキャストレシーバーの使用]

5.1カスタムブロードキャストレシーバーBroadcastReceiver

BroadcastReceivre基本クラスを継承するには、抽象メソッドonReceive()メソッド
1をオーバーライドする必要があります。ブロードキャストレシーバーは、対応するブロードキャストを受信すると、自動的にonReceive()メソッド
2をコールバックします。一般に、onReceiveメソッドは、次のような他のコンポーネントとの相互作用を含みます。通知の送信、サービスの開始など。3
.デフォルトでは、ブロードキャストレシーバーはUIスレッド実行されるため、onReceive()メソッドは時間のかかる操作を実行できません。それ以外の場合は、ANRが発生します。

// 继承BroadcastReceivre基类
public class mBroadcastReceiver extends BroadcastReceiver {
    // 复写onReceive()方法
    // 接收到广播后,则自动调用该方法
    @Override
    public void onReceive(Context context, Intent intent) {
        //写入接收广播后的操作
    }
}

以下は、ネットワークステータスの変化を監視するブロードキャストレシーバーです。

    class NetworkChangeReceiver extends BroadcastReceiver{//广播接收器类

        @Override
        public void onReceiver(Context context,Intent intent){

            //这里需要权限,需要在AndroidManifest.xml中进行网络访问权限申请:
            //<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
            ConnectivityManager connectionManager = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();

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

    }

5.2放送受信機の登録

登録には、静的登録と動的登録の2つの方法があります。

(1)静的登録

使用:AndroidManifest.xmlのタグで宣言する
機能:常駐、コンポーネントのライフサイクルの影響を受けません(アプリケーションが閉じた後、情報ブロードキャストがある場合、プログラムは引き続きシステムによって呼び出されます)、電力を消費し、メモリを占有します
アプリケーションシナリオ:常に放送を監視する必要がある

<receiver
    android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
    //默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:label="string resource"
    //继承BroadcastReceiver子类的类名(广播具体标识)
    android:name=".mBroadcastReceiver"
    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
    android:permission="string"
    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
    android:process="string" >

    //用于指定此广播接收器将接收的广播类型
// IntentFilter翻译成中文就是“意图过滤器”,主要用来过滤隐式意图。当用户进行一项操作的时候,Android系统会根据配置的 “意图过滤器” 来寻找可以响应该操作的组件,服务。
    //本示例中给出的是用于接收网络状态改变时发出的广播
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

このアプリが初めて起動すると、システムはmBroadcastReceiverクラスを自動的にインスタンス化してシステムに登録します。

(2)動的登録

使用:コードでContext.registerReceiver()メソッドを呼び出す
機能:非常駐、柔軟、コンポーネントのライフサイクルに従います(コンポーネントの終了、ブロードキャストの終了。したがって、コンポーネントが終了する前にブロードキャストレシーバーを削除する必要があります)
アプリケーションシナリオ
2.1)ソースコード

    // 选择在Activity生命周期方法中的onResume()中注册
    @Override
    protected void onResume(){
        super.onResume();
        // 1. 实例化BroadcastReceiver子类 &  IntentFilter(意图过滤器,接受广播类型)
        mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        // 2. 设置接收广播的类型(网络变化广播)
        intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
        // 3. 动态注册:调用Context的registerReceiver()方法
        registerReceiver(mBroadcastReceiver, intentFilter);
    }
// 注册广播后,要在相应位置记得销毁广播
// 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
    @Override
    protected void onPause() {
        super.onPause();
        //销毁在onResume()方法中的广播
        unregisterReceiver(mBroadcastReceiver);
    }
}

2.2)ダイナミックブロードキャストは、アクティビティのonResume()に登録し、onPause()でキャンセルするのが最適です。

  1. ダイナミックブロードキャストの登録はキャンセルする必要があります。キャンセルしないと、メモリリークが発生します。登録の繰り返しやキャンセルは許可されません。
  2. onResume()への登録とonPause()のキャンセルは、アプリが終了する前にonPause()が実行されるため、アプリが終了する前にブロードキャストがキャンセルされるため、メモリリークが防止されます。

5.3ブロードキャスト送信者がブロードキャストをAMSに送信する

(1)ブロードキャスト送信

ブロードキャストは「インテント」でマークされ、ブロードキャストの本質を定義します=
ブロードキャストによって送信される「インテント」ブロードキャストを定義します=ブロードキャスト送信者はsendBroadcast()メソッドを介してこのブロードキャストの「インテント」を送信します

(2)放送タイプ
a)通常の放送

開発者は独自のインテントブロードキャストを定義します(最も一般的に使用されます)。
1.送信者がカスタムブロードキャストを送信する

Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);

2.受信者登録ブロードキャスト

    <receiver
    //此广播接收者类是mBroadcastReceiver
    android:name=".mBroadcastReceiver" >
    //用于接收网络状态改变时发出的广播
    <intent-filter>
        <action android:name="BROADCAST_ACTION" />
    </intent-filter>
</receiver>

登録されたブロードキャストレシーバーに登録されたintentFilterのアクションが上記と一致する場合、ブロードキャストを受信します(つまり、コールバックonReceive())。

b)システムブロードキャスト

Androidには複数のシステムブロードキャストが組み込まれています。携帯電話の基本的な操作(電源投入、ネットワークステータスの変更、写真の撮影など)が関係する限り、対応するブロードキャストが発行されます。
各ブロードキャストには、特定のインテントフィルター(特定のアクションを含む)があります。 、Androidシステムのブロードキャストアクションは次のとおりです:(パート)

システム運用アクション
モニターのネットワークがandroid.net.conn.CONNECTIVITY_CHANGE変化し
、低バッテリIntent.ACTION_BATTERY_LOW
スクリーンロック画面Intent.ACTION_CLOSE_SYSTEM_DIALOGSの
画面がIntent.ACTION_SCREEN_OFFのオフになっている
画面がIntent.ACTION_SCREEN_ON開かれ

注:システムブロードキャストを使用する場合、ブロードキャストレシーバーを登録するときに関連アクションを定義するだけでよく、手動でブロードキャストを送信する必要はありません。システムブロードキャストは、システムに関連する操作があるときに自動的に実行されます。

c)順序付きブロードキャスト

送信されたブロードキャストは、ブロードキャストレシーバーによって順番に受信されます(優先度属性値に従って最大から最小に並べられます)。

sendOrderedBroadcast(intent);

順序付きブロードキャストと無秩序なブロードキャストの違い:

  • アウトオブオーダーのブロードキャスト
    context.sendBroadcast(Intent)メソッドによって送信されたブロードキャストは傍受できません。もちろん、送信されたデータは受信者が変更することはできません。

  • 通常のブロードキャストcontext.sendOrderBroadcast(Intent)メソッドによって送信されたブロードキャストは傍受され、受信者は送信されるデータを変更できます。変更と追加の両方が可能です。つまり、優先受信者がデータを変更します。次のレシーバーが受信したデータは、前のレシーバーによって変更されました。
d)ローカルブロードキャスト

Androidのブロードキャストは、アプリ間で直接通信できます(デフォルトでは、インテントフィルターを使用してエクスポートされます)アプリ内ブロードキャストはローカルブロードキャストとして理解できます。ブロードキャストの送信者と受信者は同じアプリに属しています。
(1)グローバルブロードキャストをローカルブロードキャストに
設定(a)ブロードキャストの登録時に、exportedプロパティをfalseに設定して、このアプリによって発行されないこのブロードキャストが受信されないようにします;
(b)ブロードキャストを送受信するときに、対応するアクセス許可を追加します、認証の確認に使用します。
(c)ブロードキャストを送信するときは、ブロードキャストレシーバーが配置されているパッケージ名を指定します。このブロードキャストは、このパッケージのアプリで一致する有効なブロードキャストレシーバーにのみ送信されます。(登録はintent.setPackage(packageName)で指定されます)
(2)カプセル化されたLocalBroadcastManagerクラスの使用は
、ブロードキャストレシーバーの登録/登録解除およびブロードキャストの送信時にパラメーターのコンテキストがLocalBroadcastManagerに変更されることを除いて、グローバルブロードキャストとほとんど同じです。単一インスタンス

//注册应用内广播接收器
//步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver 
mBroadcastReceiver = new mBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
//步骤2:实例化LocalBroadcastManager的实例
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
//步骤3:设置接收广播的类型 
        intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 
        localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消注册应用内广播接收器
        localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//发送应用内广播
        Intent intent = new Intent();
        intent.setAction(BROADCAST_ACTION);
        localBroadcastManager.sendBroadcast(intent);

注:ブロードキャストレシーバーのOnReceive(コンテキストコンテキスト、インテントインテント)のコンテキストの戻り値は、登録方法ごとに異なります
(3)ローカルブロードキャストとグローバルブロードキャストの違い
(3.1)
グローバルブロードキャストBroadcastReceiver の定義は、アプリケーション間のものです。アプリケーションとシステムの間およびアプリケーション内での通信方法
ローカルブロードキャストLocalBroadcastReceiverは、ブロードキャストを送受信するのは独自のアプリケーションのみです。つまり、独自のアプリケーションのみが受信でき、データはより安全です。ブロードキャストはこのプログラムでのみ行われ、より効率的です。 。
(3.2)
グローバルブロードキャストを使用する
1)インテントを作成する(パラメーター
を伝達できる)2)sendBroadcast()を使用してインテントを渡す;
3)ブロードキャストレシーバークラスにBroadcastReceiver rewrite onReceiveメソッドを継承させる(または匿名の内部クラスなどを使用できる)
4)ブロードキャストレシーバーをJava(動的登録)またはマニフェスト(静的登録)に直接登録します。registerReceiver()を使用してレシーバーとintentFilterを渡します
。5)登録解除はOnDestroy()関数で実行できます。unregisterReceiver()は
ローカルでレシーバーにあります。ブロードキャスト:
1)LocalBroadcastReceiverは静的に登録することはできませんが、動的にしか登録できません。
2)送信および登録時に、LocalBroadcastManagerのsendBroadcastメソッドおよびregisterReceiverメソッド

(6)ソースコードから見た放送メカニズムの分析

(6.1)システムブロードキャスト(BoardcastReceiver)ソースコード分析

  • ブロードキャストレシーバーBoardcastReceiver、およびreReceive()メソッドの書き換え、バインダーメカニズムによるAMSへの登録
  • ブロードキャスト送信者は、バインダーメカニズムを介してAMSにブロードキャストを送信します
  • ブロードキャスト送信者の要件に従って、AMSは登録済みリストで適切なブロードキャスト受信者を探し(IntentFilterに基づく)、適切なブロードキャスト受信者の適切なメッセージ受信キューにブロードキャストを送信します。
  • ブロードキャストレシーバーは、メッセージループを通じてブロードキャストを取得し、onReceive()メソッドを呼び出します。
    その中でも、ブロードキャスト送信側とブロードキャスト受信側の実行は非同期です。つまり、ブロードキャスト送信側は、受信側がそれを受信するかどうかを気にせず、受信側がいつ受信するかがわかりません。

(6.2)ローカルブロードキャスト(LocalBoardcastManager)ソースコード分析

1. LocalBroadcastManagerソースコード

(1)コンストラクタ

public static LocalBroadcastManager getInstance(Context context) {
    synchronized (mLock) {
        if (mInstance == null) {
            mInstance = new LocalBroadcastManager(context.getApplicationContext());
        }
        return mInstance;
    }
}
 
private LocalBroadcastManager(Context context) {
    mAppContext = context;
    mHandler = new Handler(context.getMainLooper()) {
 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_EXEC_PENDING_BROADCASTS:
                    executePendingBroadcasts();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
}

したがって、最初にコンストラクターを見ると、シングルトン実装はコンストラクターをプライベート化します。
メインスレッドに基づくルーパーが新しいハンドラーを作成したことに注意してください。handleMessageでは、レシーバーが呼び出されて、ブロードキャストメッセージを処理します。これは、LocalBroadcastManagerのコアパーツでもあります。詳細については、後でexecutePendingBroadcasts()の概要を参照してください。
(2)受信機の登録

HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();
 
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    synchronized (mReceivers) {
        ReceiverRecord entry = new ReceiverRecord(filter, receiver);
        ArrayList<IntentFilter> filters = mReceivers.get(receiver);
        if (filters == null) {
            filters = new ArrayList<IntentFilter>(1);
            mReceivers.put(receiver, filters);
        }
        filters.add(filter);
        for (int i=0; i<filter.countActions(); i++) {
            String action = filter.getAction(i);
            ArrayList<ReceiverRecord> entries = mActions.get(action);
            if (entries == null) {
                entries = new ArrayList<ReceiverRecord>(1);
                mActions.put(action, entries);
            }
            entries.add(entry);
        }
    }
}  

mReceiversはBroadcastReceiverをキーとして、IntentFilterリンクリストを値として、ブロードキャストおよびフィルター情報を格納します。mReceiversは、レシーバーとIntentFilterの間の対応表です。主な機能は、unregisterReceiver(...)での登録解除を容易にすると同時に、オブジェクトロックと同時に、レシーバーの登録、ブロードキャストの送信、レシーバーの登録解除などの複数のプロセスの同時アクセスを制限することです。
mActionsはアクションをキーとして取り、このアクションのBroadcastReceiverリストを値として登録します。mActionsの主な機能は、ブロードキャスト後にそれを受信できるBroadcastReceiverの取得を容易にすることです。
(3)ブロードキャストを送信する

public boolean sendBroadcast(Intent intent) {
    synchronized (mReceivers) {
        final String action = intent.getAction();
        final String type = intent.resolveTypeIfNeeded(mAppContext.getContentResolver());
        final Uri data = intent.getData();
        final String scheme = intent.getScheme();
        final Set<String> categories = intent.getCategories();
        ……
        ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
        if (entries != null) {
            if (debug) Log.v(TAG, "Action list: " + entries);
 
            ArrayList<ReceiverRecord> receivers = null;
            for (int i=0; i<entries.size(); i++) {
                ReceiverRecord receiver = entries.get(i);
                if (receiver.broadcasting) {
                    if (debug) {
                        Log.v(TAG, "  Filter's target already added");
                    }
                    continue;
                }
 
                int match = receiver.filter.match(action, type, scheme, data,
                        categories, "LocalBroadcastManager");
                if (match >= 0) {
                    if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                            Integer.toHexString(match));
                    if (receivers == null) {
                        receivers = new ArrayList<ReceiverRecord>();
                    }
                    receivers.add(receiver);
                    receiver.broadcasting = true;
                } else {
                    ……
                }
            }
 
            if (receivers != null) {
                for (int i=0; i<receivers.size(); i++) {
                    receivers.get(i).broadcasting = false;
                }
                mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                    mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                }
                return true;
            }
        }
    }
    return false;
}

まず、アクションに従ってmActionsからReceiverRecordリストを取り出し、各ReceiverRecordをループして、フィルターとインテントのアクション、タイプ、スキーム、データ、およびカテゴリーが一致するかどうか(intentFilterの一致メカニズム)かどうかを判断します。一致する場合は、レシーバーリストに保存して、 MSG_EXEC_PENDING_BROADCASTSのメッセージは、ハンドラーによって処理されます。
(4)メッセージ処理

Java
private void executePendingBroadcasts() {
    while (true) {
        BroadcastRecord[] brs = null;
        synchronized (mReceivers) {
            final int N = mPendingBroadcasts.size();
            if (N <= 0) {
                return;
            }
            brs = new BroadcastRecord[N];
            mPendingBroadcasts.toArray(brs);
            mPendingBroadcasts.clear();
        }
        for (int i=0; i<brs.length; i++) {
            BroadcastRecord br = brs[i];
            for (int j=0; j<br.receivers.size(); j++) {
                br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
            }
        }
    }
}

private void executePendingBroadcasts() {
    while (true) {
        BroadcastRecord[] brs = null;
        synchronized (mReceivers) {
            final int N = mPendingBroadcasts.size();
            if (N <= 0) {
                return;
            }
            brs = new BroadcastRecord[N];
            mPendingBroadcasts.toArray(brs);
            mPendingBroadcasts.clear();
        }
        for (int i=0; i<brs.length; i++) {
            BroadcastRecord br = brs[i];
            for (int j=0; j<br.receivers.size(); j++) {
                br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
            }
        }
    }
}

上記はメッセージ処理の機能です。mPendingBroadcastsは配列BroadcastRecordに変換され、各レシーバーをループし、そのonReceive関数を呼び出して、ブロードキャストのコアロジックを完成させます。
(5)登録のキャンセル

public void unregisterReceiver(BroadcastReceiver receiver) {
    synchronized (mReceivers) {
        ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
        if (filters == null) {
            return;
        }
        for (int i=0; i<filters.size(); i++) {
            IntentFilter filter = filters.get(i);
            for (int j=0; j<filter.countActions(); j++) {
                String action = filter.getAction(j);
                ArrayList<ReceiverRecord> receivers = mActions.get(action);
                if (receivers != null) {
                    for (int k=0; k<receivers.size(); k++) {
                        if (receivers.get(k).receiver == receiver) {
                            receivers.remove(k);
                            k--;
                        }
                    }
                    if (receivers.size() <= 0) {
                        mActions.remove(action);
                    }
                }
            }
        }
    }
}

mReceiversとmActionsから対応する要素を削除します。

(1)LocalBroadcastManagerのコア実装は実際にはハンドラーですが、IntentFilterの一致関数のみを使用します。BroadcastReceiverを他のインターフェースに置き換えても問題ありません。既存のクラスと概念を使用すると便利です。
(2)ハンドラによって実装されたアプリケーション内の通信であるため、自然なセキュリティが向上し、効率が向上します。

2. LocalBroadcastManagerの概要

ローカルブロードキャストによって送信されたブロードキャストは、自身のアプリでのみ伝播されます。プライバシーデータの漏洩を心配する必要はありません。
他のアプリはこのアプリにブロードキャストできません。セキュリティホールの悪用を心配する必要はありません。
ローカル放送はより効率的で安全です。

  • 効率的:ハンドラーを介して内部的に実装されるため、そのsendBroadcast()メソッドはシステムのsendBroadcast()と同じではなく、そのsendBroadcast()メソッドは実際にはハンドラーを介してメッセージを送信するだけです。
  • セキュリティ:Handlerを介してブロードキャストされるため、Binderメカニズムを介したシステムブロードキャストよりも明らかに効率的です。同時に、Handlerを使用して実装されます。他のアプリはブロードキャストをアプリケーションに送信できませんが、ブロードキャストはアプリ内に送信されます。アプリを離れません。

LocalBroadcastの内部コラボレーションは、主にmReceiversとmActionsの2つのMapコレクションに依存しています。もちろん、受信するブロードキャストオブジェクトを主に格納するListコレクションmPendingBroadcastsがあります。

3、LocalBroadcastManager使用

(1)カスタムBroadcastReceiverサブクラスLocalBroadcastReceiver

public class LocalBroadcastReceiver extends BroadcastReceiver {
 
    @Override
    public void onReceive(Context context, Intent intent) {
        localMsg.setText(intent.getStringExtra(MSG_KEY));
    }
}

(2)受信機の登録

LocalBroadcastReceiver localReceiver = new LocalBroadcastReceiver();
LocalBroadcastManager.getInstance(context).registerReceiver(localReceiver, new IntentFilter(ACTION_LOCAL_SEND));

(3)ブロードキャストを送信する

LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_LOCAL_SEND));

(4)登録のキャンセル

LocalBroadcastManager.getInstance(context).unregisterReceiver(localReceiver);
公開された74元の記事 ウォン称賛15 ビュー6256

おすすめ

転載: blog.csdn.net/qq_29966203/article/details/90383221