Chapter 1 BroadcastReceiver of the four major components (four)

Chapter One Four Components

The fourth component BroadcastReceiver

(1) Definition

Broadcasting is a global listener and belongs to one of the four major components of Android. Android broadcasting is divided into two roles: broadcast sender and broadcast receiver

(2) Function

Listen / receive broadcast messages sent by the App, and respond

(3) Application scenarios

3.1) Communication between different components of Android (including: within the application / between different applications)
3.2) Multi-threaded communication
3.3) Communication with the Android system in specific situations. Such as: when the phone is incoming, when the network is available

(4) Implementation principle

Broadcast in Android uses the observer pattern in the design pattern : a message-based publish / subscribe event model.
Android decouples the sender and receiver of the broadcast, making the system easy to integrate and easier to expand
. There are 3 roles in the model:
1. Message subscriber (broadcast receiver)
2. Message publisher (broadcast publisher)
3. Message center (AMS, Activity Manager Service)
Insert picture description here
observer pattern: The
observer design pattern defines a one-to-many combination of objects Relationship, so that when an object's state changes, all objects that depend on it are notified and automatically refreshed.

(5) Use process

[Appendix 2: Use of Boardcast Receiver]

5.1 Custom broadcast receiver BroadcastReceiver

Inheriting the BroadcastReceivre base class, you must override the abstract method onReceive () method
1. After the broadcast receiver receives the corresponding broadcast, it will automatically call back onReceive () method
2. In general, the onReceive method will involve interaction with other components, such as Send Notification, Start Service, etc.
3. By default, the broadcast receiver runs on the UI thread , therefore, the onReceive () method cannot perform time-consuming operations, otherwise it will cause ANR

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

The following is a broadcast receiver that monitors changes in network status

    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 Broadcast receiver registration

There are two ways to register: static registration and dynamic registration

(1) Static registration

Use: Declare through tags in AndroidManifest.xml
Features: resident, not affected by the life cycle of any component (after the application is closed, if there is information broadcast, the program will still be called by the system), consumes power and occupies memory
Application scenarios: Need to monitor the broadcast at all times

<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>

When this App starts for the first time, the system will automatically instantiate the mBroadcastReceiver class and register it with the system.

(2) Dynamic registration

Use: call the Context.registerReceiver () method in the code
Features: non-resident, flexible, follow the life cycle of the component (component ends, broadcast ends. Therefore, the broadcast receiver must be removed before the component ends)
Application scenario
2.1) Source code

    // 选择在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) Dynamic broadcasting is best registered in Activity's onResume () and canceled in onPause ().

  1. The registration of dynamic broadcast must be cancelled, otherwise it will lead to memory leakage; repeated registration, cancellation is not allowed.
  2. Registering in onResume () and canceling onPause () is because onPause () will be executed before the app dies, thus ensuring that the broadcast will be cancelled before the app dies, thus preventing memory leaks.

5.3 Broadcast sender sends broadcast to AMS

(1) Broadcast transmission

Broadcasting is marked with "Intent", defining the essence of broadcasting = defining the "Intent"
broadcast sent by the broadcast = the broadcast sender sends the "Intent" of this broadcast through the sendBroadcast () method

(2) Broadcast type
a) Normal Broadcast

Developers define their own intent broadcast (most commonly used).
1. The sender sends a custom broadcast

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

2. Receiver registration broadcast

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

If the action of the intentFilter registered with the registered broadcast receiver matches the above, it will receive the broadcast (that is, callback onReceive ()).

b) System Broadcast

There are multiple system broadcasts built into Android: as long as the basic operations of the mobile phone are involved (such as power-on, network status changes, taking pictures, etc.), corresponding broadcasts will be issued.
Each broadcast has a specific Intent-Filter (including specific actions) , Android system broadcast action is as follows: (part)

System Operation Action
monitor network changes android.net.conn.CONNECTIVITY_CHANGE
low battery Intent.ACTION_BATTERY_LOW
screen lock screen Intent.ACTION_CLOSE_SYSTEM_DIALOGS
screen is switched off Intent.ACTION_SCREEN_OFF
screen is opened Intent.ACTION_SCREEN_ON

Note: When using the system broadcast, you only need to define the relevant action when registering the broadcast receiver, you do not need to manually send the broadcast, the system broadcast will be automatically performed when the system has related operations

c) Ordered Broadcast

The broadcasts sent are received by the broadcast receivers in order (ordered from largest to smallest according to the Priority attribute value)

sendOrderedBroadcast(intent);

The difference between ordered broadcast and disordered broadcast:

  • The broadcast
    sent by the out-of-order broadcast context.sendBroadcast (Intent) method cannot be intercepted. Of course, the data sent cannot be modified by the receiver.
  • The broadcast
    sent by the ordered broadcast context.sendOrderBroadcast (Intent) method can be intercepted, and the receiver can modify the data to be sent, both modification and addition are possible, which means that the priority receiver will modify the data The data received by the next receiver was modified by the previous receiver.
d) Local Broadcast

The broadcast in Android can communicate directly across apps (exported is true by default with intent-filter). In-app broadcast can be understood as a local broadcast. The sender and receiver of the broadcast belong to the same app.
(1) Set the global broadcast to local broadcast
(a) Set the exported property to false when registering the broadcast, so that this broadcast that is not issued by this App will not be received;
(b) When the broadcast is sent and received, add the corresponding permission permission , Used for authorization verification;
(c) When sending a broadcast, specify the package name where the broadcast receiver is located. This broadcast will only be sent to the valid broadcast receiver that matches it in the App in this package. (The registration is specified by intent.setPackage (packageName))
(2) The use of the encapsulated LocalBroadcastManager class
is almost the same as the global broadcast, except that the context of the parameter is changed to LocalBroadcastManager when registering / unregistering the broadcast receiver and sending the broadcast. Single instance

//注册应用内广播接收器
//步骤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);

Note: The return value of the context in OnReceive (Context context, Intent intent) of the broadcast receiver for different registration methods is different
(3) The difference between local broadcast and global broadcast
(3.1) The definition of
global broadcast BroadcastReceiver is for inter-application, A method of communication between the application and the system and within the application
Local Broadcast LocalBroadcastReceiver only sends and receives broadcasts in its own application, that is, only its own application can receive, data is more secure. Broadcasting is only in this program, and it is more efficient .
(3.2) Use
global broadcasting:
1) Make intent (can carry parameters)
2) Use sendBroadcast () to pass intent;
3) Make broadcast receiver class inherit BroadcastReceiver rewrite onReceive method (or you can anonymous inner class or something)
4) Register the broadcast receiver in Java (dynamic registration) or directly in the Manifest (static registration) Use registerReceiver () to pass in the receiver and intentFilter
5) Unregister can be done in OnDestroy () function, unregisterReceiver () comes in the receiver
locally Broadcast:
1) LocalBroadcastReceiver cannot be registered statically, but can only be registered dynamically.
2) When sending and registering, the sendBroadcast method and registerReceiver method of LocalBroadcastManager

(6) Analysis of broadcasting mechanism from the perspective of source code

(6.1) System broadcast (BoardcastReceiver) source code analysis

  • Broadcast receiver BoardcastReceiver, and rewrite onReceive () method, register with AMS through Binder mechanism
  • Broadcast sender sends broadcast to AMS through Binder mechanism
  • According to the requirements of the broadcast sender, AMS looks for a suitable broadcast receiver in the registered list (based on IntentFilter) and sends the broadcast to the appropriate message receiving queue of the appropriate broadcast receiver.
  • The broadcast receiver gets the broadcast through the message loop and calls the onReceive () method.
    Among them, the execution of the broadcast sender and the broadcast receiver is asynchronous, that is, the broadcast sender will not care whether the receiver receives it & is not sure when the receiver will receive it.

(6.2) Local Broadcast (LocalBoardcastManager) source code analysis

1. LocalBroadcastManager source code

(1) Constructor

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

Looking at the constructor first, the singleton implementation thus privatizes the constructor.
Note that the Looper based on the main thread has created a new Handler. In the handleMessage, the receiver will be called to process the broadcast message, which is also the core part of the LocalBroadcastManager. For details, see the introduction of executePendingBroadcasts () later.
(2) Register receiver

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 stores broadcast and filter information, with BroadcastReceiver as the key and IntentFilter linked list as the value. mReceivers is the correspondence table between receiver and IntentFilter, the main function is to facilitate unregistering in unregisterReceiver (...), and at the same time as the object lock to restrict the concurrent access of several processes such as registering receiver, sending broadcast, and unregistering receiver.
mActions takes Action as the key, and registers the BroadcastReceiver list of this Action as the value. The main function of mActions is to facilitate getting BroadcastReceiver that can receive it after broadcasting.
(3) Send broadcast

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

First, take out the ReceiverRecord list from mActions according to Action, and loop each ReceiverRecord to determine whether the action, type, scheme, data, and categorized in the filter and intent match (the matching mechanism of intentFilter). Messages of MSG_EXEC_PENDING_BROADCASTS are processed by Handler.
(4) Message processing

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

The above is the function of message processing. mPendingBroadcasts is converted into an array BroadcastRecord, looping each receiver, and calling its onReceive function, thus completing the core logic of broadcasting.
(5) Cancel registration

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

Remove the corresponding elements from mReceivers and mActions.

(1) The core implementation of LocalBroadcastManager is actually Handler, but it only uses the match function of IntentFilter. It does not matter if BroadcastReceiver is replaced with other interfaces. It is convenient to use the existing classes and concepts.
(2) Because it is the communication within the application implemented by the Handler, the natural security is better and the efficiency is higher.

2. Summary of LocalBroadcastManager

The broadcast sent by the local broadcast is only propagated in its own app. Don't worry about privacy data leakage.
Other apps cannot broadcast to this app. Don't worry about exploiting security holes.
Local broadcasting is more efficient and safe.

  • Efficient: Because it is implemented internally through the Handler, its sendBroadcast () method is not the same as the system's sendBroadcast (). Its sendBroadcast () method is actually sending a Message through the Handler.
  • Security: Since it is broadcast through Handler, it is definitely more efficient than the system broadcast through the Binder mechanism. At the same time, it is implemented using Handler. Other apps cannot send the broadcast to our application, but the broadcast sent inside our app. Will not leave our app.

LocalBroadcast internal collaboration mainly depends on two Map collections: mReceivers and mActions, and of course there is a List collection mPendingBroadcasts, which mainly stores the broadcast objects to be received.

3, use LocalBroadcastManager

(1) Custom BroadcastReceiver subclass LocalBroadcastReceiver

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

(2) Register receiver

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

(3) Send broadcast

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

(4) Cancel registration

LocalBroadcastManager.getInstance(context).unregisterReceiver(localReceiver);
Published 74 original articles · won praise 15 · views 6256

Guess you like

Origin blog.csdn.net/qq_29966203/article/details/90383221