Android 로컬 브로드캐스트 LocalBroadcast 전체 분석
방송은 안드로이드의 4대 구성 요소로서 매우 광범위한 용도를 가지고 있다.브로드캐스트는 프로세스 간 통신이나 프로세스 내부의 일부 어셈블리 내 메시지 전달에 사용됩니다.문제는 송신되는 방송을 나만 받을 수 있고, 다른 사람에게 납치당하지 않으려면 방송에서 민감한 정보를 얻고 싶다는 것이다.또 다른 사람들이 같은 Action 방송을 보내서 진짜 방송을 위조하면 저의receiver를 속일 거예요.
어떻게 프로세스 내부의 방송 송신을 안전하고 효율적으로 실현합니까?어떤 사람은 라디오에 권한을 부여할 수 있다고 하는데, 당신은 Intent에서 PackageName을 지정할 수 있다고 합니다. 뒷글은 상세하게 설명하고, 먼저 간단하게 보십시오.
물론 이것은 모두 책에서 우리에게 알려준 방식이지만, 나는 아직 간단하지 않다고 느낀다.물론 한 차례의 배치를 거쳐 너는 실현할 수 있다.자, 이제 안전하게 해결됐으니 효율적일까요?
우리는 context를 뒤적였다.sendBroadcast 원본, 라디오를 보내는 과정을 보니 정말 상당히 복잡하군요.천진난만했던 나는 하루가 방송의 전체 과정을 이해하는 환상을 품었지만,sendBroadcast 방법의 행수를 보았을 때 내 얼굴은 대문자로 붕괴되었다.방송 대열의 분배 규칙과 과정은 잠시 언급하지 않겠다.이 사이에 존재하는 두 번의 binder call은 이 과정을 그리 효율적이지 않게 만든다.우선 sendBroadcast가 방송 정보를 System에 알려줄 거예요서버(첫 번째 Binder call), 그리고 시스템서버는 한 번 훑어보고 당신이 원하는receivers를 찾은 다음 배달 대기열에 들어가서 배달을 기다리고 (과정이 복잡합니다) 앱 프로세스receiver의 onReceiver () 방법 (두 번째 Binder call) 을 호출합니다.형제님, 저는 분명히 제 프로세스 내부에서 라디오 하나를 보내서 프로세스 내부에서 수신하고 싶은데 왜 시스템을 통해서버는?잘생겼어도 프리스타일이 있는데 바쁘잖아. 널 찾는 사람이 그렇게 많아.자신의 일은 스스로 하는 것이 초등학교에서 선생님께서 늘 우리에게 가르쳐 주신 것이다.Google 프로그래머는 초등학교 선생님의 가르침을 잊지 않은 것 같습니다.
간단한 소개
보아하니, 가지런한 발걸음으로 씩씩하게 우리를 향해 걸어오는 것은 LocalBroadcast: 먼저 공식 설명을 살펴보자.
**
- Helper to register for and send broadcasts of Intents to local objects
- within your process. This is has a number of advantages over sending
- global broadcasts with {@link android.content.Context#sendBroadcast}:
-
- - You know that the data you are broadcasting won't leave your app, so
- don't need to worry about leaking private data.
-
- It is not possible for other applications to send these broadcasts to
- your app, so you don't need to worry about having security holes they can
- exploit.
-
- It is more efficient than sending a global broadcast through the
- system.
-
*/
억압적이어서 전체 방송에 비해 셀 수 없이 많은 장점이 있다는 뜻이다.(이걸 이룬 형들과 전체 방송을 이룬 형들의 관계가 별로 좋지 않은 것 같은데, 이 단어를:has a number of advantages)-방송에서 휴대하는 데이터는 당신의 앱에만 있을 뿐 다른 앱에는 노출되지 않기 때문에 데이터 유출에 대한 걱정은 하지 않아도 됩니다-다른 앱에서는 리시버를 속이기 위해 라디오를 조작할 수 없습니다
원본 코드 분석
다음은 LocalBroadcastManager의 소스입니다.https://android.googlesource.com/platform/frameworks/support/+/android-support-lib-19.1.0/v4/java/android/support/v4/content/LocalBroadcastManager.java
1. LocalBroadcastManager의 구조를 살펴보고 표준적인 단일 모델로 이루어진 것이다.앱 개발자가 mInstance를 받으면registerReceiver, unregisterReceiver,sendBroadcast를 호출할 수 있다.
private final Handler mHandler;
private static final Object mLock = new Object();
private static LocalBroadcastManager mInstance;
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);
}
}
};
}
구조 함수에서 복잡한 조작을 하지 않은 것을 보고 주 라인에서handler를 초기화했습니다.이 핸들러가 바로 방송 배포에 사용된 것으로 추정된다.
2. 방송의 등록, 반등록, 발송 절차를 우리가 스스로 방송의 등록, 반등록, 발송을 실현하면 우리는 어떻게 할 것인가?우선, 등록할 때BroadcastReceiver와 대응하는 IntentFilter를 제공해야 한다. 우리는 이런 데이터 구조를 봉인하여 하나의 클래스에 Receiver Record에 넣을 수 있다.그리고 현재 등록된 BroadcastReceiver를 기록하는 Receiver Record 대상 목록을 유지합니다.ArrayList를 간단하게 사용할 수 있습니다.unRegister에서 제공된 BroadcastReceiver 대상에 따라 List를 옮겨다니며 해당하는receiver를 찾아 제거합니다.이렇게 하면 unRegister가 올 때마다 우리는 Receiver 목록을 한 번씩 훑어보아야 하기 때문에 비용이 좀 많이 든다. 작업이 비교적 많을 때 우리는 MAP를 사용할 수 있다.
HashMap
Receiver Record에는 BroadcastReceiver 대상이 포함되어 있기 때문에value는 IntentFilte를 직접 사용하면 데이터 구조를 간소화할 수 있습니다.그러면 Receiver에 여러 IntentFilter가 등록되어 있다면?예를 들어 하나의receiver 대상이 서로 다른 IntentFilter에 두 번 등록되어 전송된다.그래서 Value는 Array List로 개조해야 합니다.최종적으로 현재 Reciver 대상 목록을 유지하는 데 사용되는 데이터 구조는 다음과 같다. HashMap> mReceivers
.삭제할 때receiver 대상을 키로 맵에서 빠르게 찾아서 제거할 수 있습니다.라디오 보낼 때는요?sendBroadcast를 알았을 때 Intent 대상만 전송되었습니다. Intent는 Action을 가지고 등록된receiver와 일치하는 데 사용합니다.receiver를 찾을 때
HashMap> mReceivers
의Value를 옮겨다니고 각Value Array List를 한 번 더 옮겨다니야 합니다.이 검색의 비용은 정말 너무 많다.보아하니 우리는Action과receiver의 빠른 일치를 실현하기 위해 데이터 구조를 다시 유지해야 할 것 같다.마찬가지로 자주 찾는 수요는 HashMap을 사용합니다.Action을 Key로 삼았습니다.value는 그것과 일치하는 Receiver입니다.하나의 Action이 여러 개의receiver에 대응할 수 있기 때문에receiver가 등록할 때 같은 Action을 사용할 수 있습니다.그러므로value는 ArrayList를 사용해야 합니다.라디오를 보낼 때 Action에 따라 대응하는receiver를 신속하게 찾을 수 있습니다.참, Action 매칭만 사용하는 것이 아니라 Filter에 다른 정보가 매칭에 성공한 후에야 진정한receiver임을 확인할 수 있습니다.Receiver Record를value로 사용해야 합니다.receiver 대상뿐만 아니라 Intent Filter도 포함되어 있기 때문입니다.그래서 최종 데이터 구조는HashMap>.
Google이 어떻게 이루어졌는지 볼까요?먼저 두 가지 내부 클래스를 살펴보겠습니다.
// ReceiverRecord Receiver : reciver, IntentFilter broadcast
private static class ReceiverRecord {
final IntentFilter filter;
final BroadcastReceiver receiver;
boolean broadcasting;
ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
filter = _filter;
receiver = _receiver;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(128);
builder.append("Receiver{");
builder.append(receiver);
builder.append(" filter=");
builder.append(filter);
builder.append("}");
return builder.toString();
}
}
//BroadcastRecord : intent ReceiverRecord
private static class BroadcastRecord {
final Intent intent;
final ArrayList receivers;
BroadcastRecord(Intent _intent, ArrayList _receivers) {
intent = _intent;
receivers = _receivers;
}
}
몇 가지 멤버 변수를 살펴보겠습니다.
// mReceivers Map receiver, Intentfilters。 receiver 。
// , filter Action, mAction. mReceivers, mAction BroadcastRecord, filter Action, 。
private final HashMap> mReceivers
= new HashMap>();
// mActions Map Action, ReceiverRecord
private final HashMap> mActions
= new HashMap>();
// List, BroadcastRecord, BroadcastRecord intent receivers
private final ArrayList mPendingBroadcasts
= new ArrayList();
방송의 송신 과정
/**
* Register a receive for any local broadcasts that match the given IntentFilter.
*
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
*
* @see #unregisterReceiver
*/
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
synchronized (mReceivers) {
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
// receiver mReceiver , 。
ArrayList filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList(1);
mReceivers.put(receiver, filters);
}
// IntentFilter receiver 。filters mReceivers map value, IntentFilter ArrayList。
// receiver IntentFilter。mReceivers receiver, receiver IntentFilter.
filters.add(filter);
// IntentFilter Action. Action mActions, 。
//mActions Action Key , ArrayList value MAP。
// Action, Action Receiver(ReceiverRecord) 。
for (int i=0; i entries = mActions.get(action);
if (entries == null) {
entries = new ArrayList(1);
mActions.put(action, entries);
}
// ReceiverRcoder entries:entries Action ReceiverRecord 。
entries.add(entry);
}
}
}
브로드캐스트의 등록 거부 프로세스
/**
* Unregister a previously registered BroadcastReceiver. All
* filters that have been registered for this BroadcastReceiver will be
* removed.
*
* @param receiver The BroadcastReceiver to unregister.
*
* @see #registerReceiver
*/
public void unregisterReceiver(BroadcastReceiver receiver) {
synchronized (mReceivers) {
// Receiver receiver, filters.
ArrayList filters = mReceivers.remove(receiver);
if (filters == null) {
return;
}
// Action Map receiver
for (int i=0; ifor (int j=0; j// Action receiver , receiver.
ArrayList receivers = mActions.get(action);
if (receivers != null) {
for (int k=0; kif (receivers.get(k).receiver == receiver) {
receivers.remove(k);
k--;
}
}
// Action receiver , Action 。
// receiver Action 。
if (receivers.size() <= 0) {
mActions.remove(action);
}
}
}
}
}
}
:
/**
* Broadcast the given intent to all interested BroadcastReceivers. This
* call is asynchronous; it returns immediately, and you will continue
* executing while the receivers are run.
*
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
*
* @see #registerReceiver
*/
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 categories = intent.getCategories();
final boolean debug = DEBUG ||
((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
if (debug) Log.v(
TAG, "Resolving type " + type + " scheme " + scheme
+ " of intent " + intent);
// Action mActions MAP receivers.
ArrayList entries = mActions.get(intent.getAction());
if (entries != null) {
if (debug) Log.v(TAG, "Action list: " + entries);
ArrayList receivers = null;
// receivers IntentFilter
for (int i=0; iif (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
// receiver , 。
if (receiver.broadcasting) {
if (debug) {
Log.v(TAG, " Filter's target already added");
}
continue;
}
// receiver Intent , receivers
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();
}
receivers.add(receiver);
// receiver true
receiver.broadcasting = true;
} else {
if (debug) {
String reason;
switch (match) {
case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
default: reason = "unknown reason"; break;
}
Log.v(TAG, " Filter did not match: " + reason);
}
}
}
// broadcasting false mPendingBroadcasts .
if (receivers != null) {
for (int i=0; ifalse;
}
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
// MSG_EXEC_PENDING_BROADCASTS
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
// Handler mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
마지막으로 보낼 라디오를 대기열 mPendingBroadcasts에 추가한 다음Handler로 메인 라인에 메시지를 보내고 excutePendingBroadcasts()를 호출하여 나누어 주는 것을 볼 수 있다.
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; ifor (int j=0; j
또한 LocalBroadcast는 동기화를 통해 배포를 지원합니다.
/**
* Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
* the Intent this function will block and immediately dispatch them before
* returning.
*/
public void sendBroadcastSync(Intent intent) {
if (sendBroadcast(intent)) {
executePendingBroadcasts();
}
}
총결산
1. LocalBroadcast는 앱 내부에서 유지되는 방송 메커니즘으로 안전성과 효율성이 매우 높다.따라서 앱 내부에서 방송을 전송하고 수신할 필요가 있으면 LocalBroadcast를 사용해야 한다.2. Receiver는 동적 등록만 허용하고 Manifest에는 등록할 수 없습니다.3. LocalBroadcastManager가 보내는 라디오 액션은 LocalBroadcastManager에 등록된 BroadcastReceiver와만 상호작용할 수 있다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Android Studio 패키지 질문:gradle 패키지 Android 프로그램, 패키지에 종속 패키지가 포함되지 않는 방법안드로이드 프로젝트의build.gradle에서 dependencies 클립에는 다음과 같은 몇 가지 형식의 설명이 있습니다. 단일 파일로 컴파일됨 컴파일된 컨텐트는 로컬libs 디렉토리입니다. 컴파일된 내용은andr...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.