Android廣播機制一
廣播機制做為android程序間通訊的一個重要機制被廣泛的使用。我承認已經到了濫用的地步。正因為如此,各方案公司甚至google都已經對它做修改限制,來達到控制大家濫用的目的。(這說明這個機制本身的設計還是很成功的,說明它對於使用者來說,簡單、方便,耦合度低的特點)。
那麼android是設計broadcast的這套機制的。我們來看一下大致的流程。
android的廣播機制分為註冊和接收兩塊。使用者將自己感興趣的廣播向系統註冊,系統在接收到相應的廣播後,會找到各個註冊者,並呼叫onReceive。
看呼叫方式,以在activity中呼叫為例:
public class MainActivity extends Activity {
private static final String Tag="TEST";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter("com.test.broadcast" );
registerReceiver(receiver, filter);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(receiver);
}
private BroadcastReceiver receiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Receive ....");
}
};
}
然後看Activity中是怎樣處理registerReceiver(receiver, filter);的。發現Activity類中並沒有直接實現該函式。再找它的父類ContextThemeWrapper,也沒有,接著找ContextThemeWrapper的父類ContextWrapper:
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
呼叫了mBase的registerReceiver,mBase又是個什麼東西呢?
它是一個ContextImpl ,為什麼?
這要從launch一個android的應用說起,我把程式碼貼上來:
在ActivityThread.java中有:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
....
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);
.....
而createBaseContextForActivity程式碼是這樣的:
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
ContextImpl appContext = new ContextImpl();
...
Context baseContext = appContext;
...
return baseContext;
}
Activity.java的attach函式:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attachBaseContext(context);
.
@Override protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
mBase = newBase;
}
所以,mBase是一個ContextImpl的例項。
這是套路!
我們來看Context,ContextThemeWrapper,ContextWrapper和ContextImpl這幾個類之間的關係類圖(公司限制不能上傳圖片或檔案,所以或許有一天這張圖片失效了):
再看套路之裝飾模式:
是不是很相似的感覺!
這就是套路!
這裡說一下android的命名,其實android的命名還是很統一的,基本上從類或檔案的命名上,就能看出端倪來。XXXWrapper就說明了問題,這對於我們看android程式碼很有幫助,後續會陸續說到android命名來看相關的程式碼。我們不可能把整個android的程式碼都背下來,但是,如果我們掌握了規律,找起程式碼來就會快一些。
繼續跟蹤ContextImpl裡的
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext());
}
轉到registerReceiverInternal:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}
mPackageInfo是一個LoadedApk的例項。看一下它的成員變數。
private final ActivityThread mActivityThread;
private final ApplicationInfo mApplicationInfo;
final String mPackageName;
private final String mAppDir;
private final String mResDir;
private final String[] mSharedLibraries;
private final String mDataDir;
private final String mLibDir;
private final File mDataDirFile;
private final ClassLoader mBaseClassLoader;
private final boolean mSecurityViolation;
private final boolean mIncludeCode;
private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
Resources mResources;
private ClassLoader mClassLoader;
private Application mApplication;
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
從它的成員變數看的出,這是個挺重要的類。尤其是成員變數ActivityThread mActivityThread。注意到mReceivers,看上去這個變數記錄了應用的所有Receiver。
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
獲得了主執行緒的handler.
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
這裡的getReceiverDispatcher返回的是IIntentReceiver型別例項,Binder物件。而Binder用於IPC通訊的,那麼這個Binder物件要和誰進行IPC通訊呢?應該是ActivityManagerService。
這個函式的實現:
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
rd = map.get(r);
}
}
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
先從mReceivers中找rd,這裡為空,所以就new 了一個ReceiverDispatcher,並將它放入mReceivers中記錄下來。key是一個Context,這裡指的就是我們的MainActiviy,所以通過一個activity可以找到對應的map,再通過BroadcastReceiver就可以找到相應的廣播接收發布器ReceiverDispatcher了。(引:http://blog.csdn.net/luoshengyang/article/details/6737352)在新建廣播接收發布器ReceiverDispatcher時,會在建構函式裡面建立一個InnerReceiver例項,這是一個Binder物件,實現了IIntentReceiver介面,可以通過ReceiverDispatcher.getIIntentReceiver函式來獲得,獲得後就會把它傳給ActivityManagerService,以便接收廣播。在ReceiverDispatcher類的建構函式中,還會把傳進來的Handle型別的引數activityThread儲存下來,以便後面在分發廣播的時候使用。
這樣說來,ActivityManagerService就能找到對應的BroadcastReceiver,並且往對應的執行緒中傳送訊息了。而這個訊息的響應函式應該就是BroadcastReceiver的onReceive函數了。
回到ContextImpl.registerReceiverInternal函式中,
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
呼叫了ActivityManagerNative的內部類ActivityManagerProxy.registerReceiver:
public Intent registerReceiver(IApplicationThread caller, String packageName,
IIntentReceiver receiver,
IntentFilter filter, String perm, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(packageName);
data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
filter.writeToParcel(data, 0);
data.writeString(perm);
data.writeInt(userId);
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
reply.readException();
Intent intent = null;
int haveIntent = reply.readInt();
if (haveIntent != 0) {
intent = Intent.CREATOR.createFromParcel(reply);
}
reply.recycle();
data.recycle();
return intent;
}
這裡直接呼叫Binder驅動,說明這個過程在使用IPC通訊。所以真正的執行體轉移到遠端物件去了。這裡使用了代理模式,特徵命名ActivityManagerProxy。實際的實現者是在遠端的ActivityManagerService.
ActivityManagerService.registerReceiver:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...
ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
...
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
}
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
int N = allSticky.size();
for (int i=0; i<N; i++) {
Intent intent = (Intent)allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
mRegisteredReceivers.put(receiver.asBinder(), rl);將receiver記錄下來,
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
這裡將filter與receiver關聯在了一起,然後記錄下來:
mReceiverResolver.addFilter(bf);
到這裡,整個註冊過程完成。註冊的資訊由客戶端呼叫最終被ActivityManagerService記錄在兩個成員變數mReceiverResolver和mRegisteredReceivers中。