Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> BroadcastReceiver探討之動態廣播注冊流程

BroadcastReceiver探討之動態廣播注冊流程

編輯:關於Android編程

相關

在Android中,BroadcastReceiver是一套用來實現組件之間的通信機制,它是基於消息發布和訂閱機制,可以用在不同線程之間組件的通信,也可以跨進程進行組件間通信。

在Android中,根據注冊類型來看,BroadcastReceiver可以分為兩種類型:靜態廣播和動態廣播,其中靜態廣播中的廣播中心是PMS(PackageManagerService),而動態廣播的廣播中心是AMS(ActivityManagerService),這邊文章主要是分析動態廣播的。

動態BroadcastReceiver的注冊是借助Context接口中的registerReceiver方法來實現的。其中,繼承Context接口的類有:Activity,Service,Application,所以在這3個類中,都可以動態注冊廣播。此外,Context接口的實現類,是ContextImpl類。

在討論BroadcastReceiver前,需要稍微了解下Android不同進程之間的通信,以及一些Android系統級的Service。

Android跨進程之間的通信,主要有Binder實現的IPC機制,匿名共享內存,socket等等,而廣播的跨進程通信是使用Binder來實現的,通過Binder將App進程切換到system_server進程和AMS通信。

在手機開機的時候,Android啟動了很多系統級的進程,比如zygote進程,system_server進程,其中在system_server進程上,又啟動了很多服務,
比如PowerManagerService(電源管理服務),ActivityManagerService(管理四大組件)

下面,主要是通過探討廣播,主要是分為兩部分,

用戶進程和system_server進程,廣播registerReceiver的流程

用戶進程和system_server進程,廣播底層的數據存儲結構

廣播在用戶進程中的注冊流程

Context.java

public abstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter);

receiver: 廣播接收組件,接收到相關的廣播會調用BroadcastReceiver中的onReceive方法

filter:主要是用來過濾廣播的,只有符合條件的廣播才會接收.

registerReceiver方法最終是調用ContextImpl中的registerReceiverInternal方法

ContextImpl.java

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類型,保存了整個App內部的信息,比如包名,資源的路徑,應用中所有啟動的service,注冊的reciver等等,用戶進程(即一個應用進程)中,只會存在一個對象,所有ContextImpl中的mPackageInfo都指向共同的mPackageInfo對象,因此,mPackageInfo在整個用戶進程中是唯一的。

registerReceiverInternal方法:

1,獲取Handler類型的scheduler對象,如果scheduler不為null,則scheduler為用戶傳遞進來的Handler,否則為主線程中的Hanlder(這也是為什麼BroadcastReceiver中的onReceive方法調用是處於主線程的原因),這個Handler對象主要是用來分發AMS傳遞過來的Intent,

2,如果mPackageInfo存在,則通過mPackageInfo獲取IIntentReceiver類型的rd對象,否則創建一個,rd同樣也是Binder類型。然後,將rd傳遞給AMS,AMS處理相對應的廣播,會通過這個Binder對象,跨進程回傳到這個Context所處的進程中。

3,通過ActivityManagerNative.getDefault()獲取到ActivityManagerProxy對象,然後借助ActivityManagerProxy對象,和AMS通信,這時候進程由用戶進程切換到system_server進程,最終調用的是AMS中的registerReceiver方法。

scheduler = mMainThread.getHandler();

mMainThread是ActivityThread對象,也可以說是主線程

這裡有個疑惑,關於mPackageInfo,這個對象什麼時候不存在?(以後再查看)按照我目前的理解,mPackageInfo最早是在Application創建的時候,就被初始化了,而context是當前的Activity,Service或者Application,所以符合條件(mPackageInfo != null && context != null)

rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler, mMainThread.getInstrumentation(), true);

LoadedApk

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
        Context context, Handler handler,
        Instrumentation instrumentation, boolean registered) {
    synchronized (mReceivers) {
        LoadedApk.ReceiverDispatcher rd = null;
        ArrayMap 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();
                    mReceivers.put(context, map);
                }
                map.put(r, rd);
            }
        } else {
            rd.validate(context, handler);
        }
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

1,如果廣播已經注冊過,則通過mReceivers對象獲取key為當前Context的LoadedApk.ReceiverDispatcher類型的rd對象

2,如果rd對象不存在,則創建,並且將rd添加到map中;否則,驗證rd對象中的context和activitythread(validate這個方法有疑惑,什麼場景下,rd中context和activitythread和目前的不一致)

3,返回rd對象的中IIntentReceiver對象

廣播在用戶進程中的底層數據存儲

上面說過,在用戶進程中所有ContextImpl中的mPackageInfo都指向同一對象。

mPackageInfo中存在維持一個ArrayMap的mReceivers變量,mReceivers是ArrayMap嵌套ArrayMap的數據結構

key:為注冊的Context(即Activity,Service或者Application),

value:同樣也是個ArrayMap,

key:注冊的BroadcastReceiver

value:ReceiverDispatcher對象,廣播分發者,該對象內部保存注冊者的Context,Handler,BroadcastReceiver,IIntentReceiver.Stub(和用戶進程交互的)等等,
發送廣播時,AMS將會將Intent信息分發到BroadcastReceiver的onRecieve方法中。

mReceivers

private final ArrayMap> mReceivers
        = new ArrayMap>();

LoadedApk.ReceiverDispatcher

final IIntentReceiver.Stub mIIntentReceiver;
final BroadcastReceiver mReceiver;
final Context mContext;
final Handler mActivityThread;
final Instrumentation mInstrumentation;
final boolean mRegistered;
final IntentReceiverLeaked mLocation;
RuntimeException mUnregisterLocation;
//....

廣播在AMS中的注冊流程

try {
    // 跨進程和AMS通信,廣播的注冊實際上是在AMS中執行的
    return ActivityManagerNative.getDefault().registerReceiver(
            mMainThread.getApplicationThread(), mBasePackageName,
            rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
    return null;
}

ActivityManagerNative.getDefault()

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
    // ...省略
    // 獲取到唯一的
    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    // ...省略
    // Singleton是一個抽象類,Android實現的單例模式
    private static final Singleton gDefault = new Singleton() {
        protected IActivityManager create() {
            // 獲取指向ActivityManagerService的IBinder對象
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            // 創建ActivityManagerProxy對象,並將ActivityManagerService的Binder傳遞給ActivityManagerProxy
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
    // 創建一個ActivityManagerProxy對象
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }

}

ActivityManagerNative.getDefault()第一次會創建一個AMP(ActivityManagerProxy)對象,並通過ServiceManager遠程獲取到一個指向AMS
的IBinder對象,並將這個IBinder引用傳遞給AMS對象,最後返回AMP對象,注意這個對象運行在用戶進程中的,
ActivityManagerProxy中的IBinder引用是用來跨進程和AMS通信的。

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);
    // 跨進程和AMS通信,注意Binder驅動會掛起當前用戶線程
    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;
}

很明顯,這段代碼是用來跨進程通信的。mRemote是一個IBinder對象,指向AMS的。

mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);

Binder機制非常復雜,這裡不做討論。我們只需要知道IBinder.transact方法,會調用到IBinder.onTransact方法。

題外話:

這裡有個疑惑,mRemote.transact會不會阻塞線程呢?仔細思考了下,應該會的,准確的來說,Binder驅動會掛起當前用戶進程。

因為registerReceiver返回Intent對象,如果當前用戶線程沒有掛起的話,那麼後面的代碼就沒意義了,比如reply.readException肯定沒效果。

mRemote這個IBinder對象是指向AMS的,而AMS繼承了ActivityManagerNative類,並且AMS的onTransact方法是由ActivityManagerNative實現的。

ActivityManagerNative.onTransact,注意調用這個方法的時候,進程已經從用戶進程切換到system_server進程了。

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {
        case REGISTER_RECEIVER_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app =
                b != null ? ApplicationThreadNative.asInterface(b) : null;
            String packageName = data.readString();
            b = data.readStrongBinder();
            IIntentReceiver rec
                = b != null ? IIntentReceiver.Stub.asInterface(b) : null;
            IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
            String perm = data.readString();
            int userId = data.readInt();
            // 調用AMS中的registerReceiver方法,這個方法是廣播注冊的最終實現
            Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);
            reply.writeNoException();
            if (intent != null) {
                reply.writeInt(1);
                intent.writeToParcel(reply, 0);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
    }

ActivityManagerService.registerReceiver

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
    enforceNotIsolatedCaller("registerReceiver");
    int callingUid;
    int callingPid;
    synchronized(this) {
        ProcessRecord callerApp = null;
        if (caller != null) {
            callerApp = getRecordForAppLocked(caller);
            if (callerApp == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                        + " (pid=" + Binder.getCallingPid()
                        + ") when registering receiver " + receiver);
            }
            if (callerApp.info.uid != Process.SYSTEM_UID &&
                    !callerApp.pkgList.containsKey(callerPackage) &&
                    !"android".equals(callerPackage)) {
                throw new SecurityException("Given caller package " + callerPackage
                        + " is not running in process " + callerApp);
            }
            callingUid = callerApp.info.uid;
            callingPid = callerApp.pid;
        } else {
            callerPackage = null;
            callingUid = Binder.getCallingUid();
            callingPid = Binder.getCallingPid();
        }

        userId = this.handleIncomingUser(callingPid, callingUid, userId,
                true, true, "registerReceiver", callerPackage);

        List allSticky = null;

        // Look for any matching sticky broadcasts...
        Iterator actions = filter.actionsIterator();
        if (actions != null) {
            while (actions.hasNext()) {
                String action = (String)actions.next();
                allSticky = getStickiesLocked(action, filter, allSticky,
                        UserHandle.USER_ALL);
                allSticky = getStickiesLocked(action, filter, allSticky,
                        UserHandle.getUserId(callingUid));
            }
        } else {
            allSticky = getStickiesLocked(null, filter, allSticky,
                    UserHandle.USER_ALL);
            allSticky = getStickiesLocked(null, filter, allSticky,
                    UserHandle.getUserId(callingUid));
        }

        // The first sticky in the list is returned directly back to
        // the client.
        Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;

        if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter
                + ": " + sticky);

        if (receiver == null) {
            return sticky;
        }

        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);
        } else if (rl.uid != callingUid) {
            throw new IllegalArgumentException(
                    "Receiver requested to register for uid " + callingUid
                    + " was previously registered for uid " + rl.uid);
        } else if (rl.pid != callingPid) {
            throw new IllegalArgumentException(
                    "Receiver requested to register for pid " + callingPid
                    + " was previously registered for pid " + rl.pid);
        } else if (rl.userId != userId) {
            throw new IllegalArgumentException(
                    "Receiver requested to register for user " + userId
                    + " was previously registered for user " + rl.userId);
        }
        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

 

1,首先會獲取應用的uid和pid,然後調用handleIncomingUser檢測注冊廣播所需的權限,並獲取userId

2,檢測所有符合條件的黏性事件,如果廣播為null,則返回最近的一條黏性事件

3,通過mRegisteredReceivers獲取key為receiver(IBinder對象,從用戶進程中傳遞進來的IIntentReceiver對象),獲取相關的ReceiverList; 如果ReceiverList為null,則創建,並添加到mRegisteredReceivers中

4,創建BroadcastFilter(繼承IntentFilter)對象,並添加進入到ReceiverList中

5,處理符合條件的黏性事件(這裡暫不探討)

廣播在AMS中的底層數據存儲

ReceiverList:繼承ArrayList,存儲的是BroadcastFilter對象

BroadcastFilter:繼承IntentFilter,主要是用來過濾廣播的,廣播接收器只能接收指定的廣播類型

AMS中維持一個mRegisteredReceivers變量的HashMap

final HashMap mRegisteredReceivers =
            new HashMap();

mRegisteredReceivers的數據結構是HashMap

key:IBinder,也就是ReceiverDispatcher中的IIntentReceiver對象,是用於AMS和用戶進程通信的

value:ReceiverList,存儲一系列的BroadcastFilter的List

mReceiverResolver:存儲對應的BroadcastFilter

動態廣播注冊的底層數據存儲

圖1

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved