轉載請標明出處:http://www.cnblogs.com/zhaoyanjun/p/6048369.html
本文出自【趙彥軍的博客】
前言
在Android中,Broadcast是一種廣泛運用的在應用程序之間傳輸信息的機制。我們拿廣播電台來做個比方。我們平常使用收音機收音是這樣的:許許多多不同的廣播電台通過特定的頻率來發送他們的內容,而我們用戶只需要將頻率調成和廣播電台的一樣就可以收聽他們的內容了。Android中的廣播機制就和這個差不多的道理。
BroadcastReceiver安全問題
BroadcastReceiver設計的初衷是從全局考慮可以方便應用程序和系統、應用程序之間、應用程序內的通信,所以對單個應用程序而言BroadcastReceiver是存在安全性問題的(惡意程序腳本不斷的去發送你所接收的廣播)。為了解決這個問題LocalBroadcastManager就應運而生了。
LocalBroadcastManager
LocalBroadcastManager是Android Support包提供了一個工具,用於在同一個應用內的不同組件間發送Broadcast。LocalBroadcastManager也稱為局部通知管理器,這種通知的好處是安全性高,效率也高,適合局部通信,可以用來代替Handler更新UI
- 好處:
1、因廣播數據在本應用范圍內傳播,你不用擔心隱私數據泄露的問題。
2、不用擔心別的應用偽造廣播,造成安全隱患。
3、相比在系統內發送全局廣播,它更高效。
LocalBroadcastManager用法
- LocalBroadcastManager對象的創建
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
- 注冊廣播接收器
LocalBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );
- 發送廣播
LocalBroadcastManager.sendBroadcast( intent ) ;
- 取消注冊廣播接收器
LocalBroadcastManager.unregisterReceiver( broadcastReceiver );
LocalBroadcastManager部分源碼解析
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);
}
}
};
}
從這個部分源碼可以看出兩點:
-
在獲取LocalBroadcastManager對象實例的時候,這里用了單例模式。並且把外部傳進來的Context 轉化成了ApplicationContext,有效的避免了當前Context的內存泄漏的問題。這一點我們在設計單例模式框架的時候是值得學習的,看源碼可以學習到很多東西。
-
在LocalBroadcastManager構造函數中創建了一個Handler.可見 LocalBroadcastManager 的本質上是通過Handler機制發送和接收消息的。
-
在創建Handler的時候,用了
context.getMainLooper()
, 說明這個Handler是在Android 主線程中創建的,廣播接收器的接收消息的時候會在Android 主線程,所以我們決不能在廣播接收器里面做耗時操作,以免阻塞UI。
一個小例子
package com.app;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private LocalBroadcastManager localBroadcastManager ;
private MyBroadcastReceiver broadcastReceiver ;
private IntentFilter intentFilter ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注冊廣播接收器
localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
broadcastReceiver = new MyBroadcastReceiver() ;
intentFilter = new IntentFilter( "myaction") ;
localBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );
//在主線程發送廣播
Intent intent = new Intent( "myaction" ) ;
intent.putExtra( "data" , "主線程發過來的消息" ) ;
localBroadcastManager.sendBroadcast( intent ) ;
new Thread(new Runnable() {
@Override
public void run() {
//在子線程發送廣播
Intent intent = new Intent( "myaction" ) ;
intent.putExtra( "data" , "子線程發過來的消息" ) ;
localBroadcastManager.sendBroadcast( intent ) ;
}
}).start(); ;
}
private class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction() ;
if ( "myaction".equals( action )){
Log.d( "tttt 消息:" + intent.getStringExtra( "data" ) , "線程: " + Thread.currentThread().getName() ) ;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注冊廣播,防止內存泄漏
localBroadcastManager.unregisterReceiver( broadcastReceiver );
}
}
運行結果
D/tttt 消息:主線程發過來的消息: 線程: main
D/tttt 消息:子線程發過來的消息: 線程: main
可以看出,廣播接收器的onReceive
方法運行在主線程。
注意事項
雖然LocalBroadcastManager也通過BroadcastReceiver
來接收消息,但是他們兩個之間還是有很多區別的。
- LocalBroadcastManager注冊廣播只能通過代碼注冊的方式。傳統的廣播可以通過代碼和xml兩種方式注冊。
- LocalBroadcastManager注冊廣播后,一定要記得取消監聽。這一步可以有效的解決內存泄漏的問題。
- LocalBroadcastManager注冊的廣播,您在發送廣播的時候務必使用
LocalBroadcastManager.sendBroadcast(intent);
否則您接收不到廣播。傳統的發送廣播的方法:context.sendBroadcast( intent );