Android 源码的观察者模式

Android 源码的观察者模式

观察者模式介绍

观察者模式是一个使用率非常高的模式,它最常用的地方是 GUI 系统、订阅-发布系统。因为这个模式的一个非常重要的作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以 GUI 系统来说,应用的 UI 具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也会经常性变化,但是业务逻辑基本变化不大,此时,GUI 系统需要一套机制来应对这种情况,使得 UI 层与具体的业务逻辑解耦,观察者模式此时就派上用场了。

观察者模式的定义

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

ListView 的 notifyDataSetChanged

ListView 是 Android 中最重要的控件之一,而 ListView 最重要的一个功能就是 Adapter。通常,在我们往 ListView 添加数据后,都会调用 Adapter 的 notifyDataSetChanged 方法。

notifyDataSetChanged 方法定义在 BaseAdapter 中,具体代码如下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    // 数据集观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    private CharSequence[] mAutofillOptions;

    public boolean hasStableIds() {
        return false;
    }
    // 注册观察者
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }
    // 反注册观察者
    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    
    // 当数据集变化时,通知所有观察者
    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
...
}

一看 BaseAdapter 代码就大体有了这么一个认识:BaseAdapter 是一个观察者模式!

mDataSetObservable.notifyChanged 方法如下:

// 数据集观察者
public class DataSetObservable extends Observable<DataSetObserver> {
    // 调用每个观察者的 onChange 函数来通知它们被观察者发生了变化
    /**
     * Invokes {@link DataSetObserver#onChanged} on each observer.
     * Called when the contents of the data set have changed.  The recipient
     * will obtain the new contents the next time it queries the data set.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // 调用所有观察者的 onChanged 方法
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    /**
     * Invokes {@link DataSetObserver#onInvalidated} on each observer.
     * Called when the data set is no longer valid and cannot be queried again,
     * such as when the data set has been closed.
     */
    public void notifyInvalidated() {
        synchronized (mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onInvalidated();
            }
        }
    }
}

这个代码很简单,就是在 mDataSetObservable 的 notifyChanged 中遍历所有观察者,并且调用它们的 onChanged 方法,从而告知观察者发生了变化。

那么这些观察者是从哪里来的呢?其实这些观察者就是 ListView 通过 setAdapter 方法设置 Adapter 产生的。

ListView 的 setAdapter 方法如下:

@Override
    public void setAdapter(ListAdapter adapter) {
        // 如果已经有了一个 Adapter,那么先注销该 Adapter 对应的观察者
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        ...
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            // 获取数据的数量
            mItemCount = mAdapter.getCount();
            checkFocus();
            // 创建一个数据观察者
            mDataSetObserver = new AdapterDataSetObserver();
            // 将这个观察者注册到 Adapter 中,实际上是注册到 DataSetObservable 中
            mAdapter.registerDataSetObserver(mDataSetObserver);

            mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

           ...

        requestLayout();
    }

从程序中可以看到,在设置 Adapter 时会构建一个 AdapterDataSetObserver,这就是上面所说的观察者,最后,将这个观察者注册到 Adapter 中,这样我们的被观察者、观察者都有了。

AdapterDataSetObserver 定义在 ListView 的父类 AbsListView 中,具体代码如下:

    class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }
    }

AdapterDataSetObserver 继承自 AbsListView 的父类 AdapterView 的 AdapterDataSetObserver。

    class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;

        @Override
        public void onChanged() {
            mDataChanged = true;
            // 获取 Adapter 中数据的数量
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();

            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            // 重新布局 ListView、GridView 等 AdapterView 组件
            requestLayout();
        }

        @Override
        public void onInvalidated() {
            mDataChanged = true;

            if (AdapterView.this.getAdapter().hasStableIds()) {
                // Remember the current state for the case where our hosting activity is being
                // stopped and later restarted
                mInstanceState = AdapterView.this.onSaveInstanceState();
            }

            // Data is invalid so we should reset our state
            mOldItemCount = mItemCount;
            mItemCount = 0;
            mSelectedPosition = INVALID_POSITION;
            mSelectedRowId = INVALID_ROW_ID;
            mNextSelectedPosition = INVALID_POSITION;
            mNextSelectedRowId = INVALID_ROW_ID;
            mNeedSync = false;

            checkFocus();
            requestLayout();
        }

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

当 ListView 的数据发生变化时,调用 Adapter 的 notifyDataSetChanged 函数,这个函数会调用 DataSetObservable 的 notifyChanged 函数。notifyChanged 会调用所有观察者(AdapterDataSetObserver)的 onChanged 方法,在 onChanged 方法中又会调用 ListView 重新布局的函数使得 ListView 刷新界面。

BroadcastReceiver 广播接收器

BroadcastReceiver 是 Android 的四大组件之一,是一个典型的发布-订阅模式,也就是观察者模式。

注册广播

当调用 registerReceiver 注册广播时,会调用 ContextImpl 的 registerReceiverInternal 方法。

registerReceiverInternal 代码如下:

    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    // 获取 Handler 来投递消息
                    scheduler = mMainThread.getHandler();
                }
                // 获取 IIntentReceiver 对象,通过它与 AMS 交互,并且通过 Handler 传递消息
                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 {
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

mMainThread.getHandler() 会获取主线程的 Handler,这个 Handler 是用来分发 AMS 发送过来的广播。也就是 ActivityThread 里面名叫 H 的广播。

ActivityThread 的 getHandler 方法如下:

    final Handler getHandler() {
        return mH;
    }

ContextImpl 的 registerReceiverInternal 方法通过 mPackageInfo 的 getReceiverDispatcher 获取一个 IIntentReceiver 对象 rd,它是一个 Binder 对象,接下来会把它传给 AMS。AMS 在收到相应的广播时,就是通过这个 Binder 对象来通知 Activity 里面的接收者。

AMS 的 registerReceiver 方法如下:

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
        ...
        // 获取 ProcessRecord
        ProcessRecord callerApp = null;
        ...
        // 根据 Intent 查找匹配的 sticky 接收器(粘性广播)
        ArrayList<Intent> allSticky = null;
        if (stickyIntents != null) {
            ...
        }
        ...
        synchronized (this) {
            ...
            // 获取 ReceiverList
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            ...
            // 构建 BroadcastFilter,并且添加到 ReceiverList 中
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            ...
                mReceiverResolver.addFilter(bf);
            ...
            return sticky;
        }
    }

这里首先查找有没有对应的 sticky intent 列表才能在。sticky intent 是粘性广播,我们在最后一次调用 sendStickyBroadcast 来发送某个广播时,系统会把代表这个广播的 intent 保存下来,这样,后来调用 registerReceiver 来注册相同 action 的广播接收器时,就会得到这个最后发出的广播。这个最后发出的广播虽然被处理完了,但是仍然被粘在 AMS,以便下个注册相应 Action 的广播接收器还能继续处理。

接下来会把 receiver 保存在 ReceiverList 中,然后创建 BroadcastFilter 把广播接收器列表 rl 和 filter 关联起来,保存在 mReceiverResolver。

发送广播

sendBroadcast 发送广播时,会先通过 Binder 调用到 AMS 的 broadcastIntentLocked 方法。这里会通过 action 找到对应的广播接收器,然后把广播放到消息队列中,完成第一个阶段对广播的异步分发。

    @GuardedBy("this")
    final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
        intent = new Intent(intent);
        ...
                queue.enqueueOrderedBroadcastLocked(r);
                // 广播的异步分发
                queue.scheduleBroadcastsLocked();
        ...

        return ActivityManager.BROADCAST_SUCCESS;
    }

AMS 在消息循环中处理这个广播,并通过 Binder 把这个广播分发给 ReceiverDispatcher,ReceiverDispatcher 把这个广播放到 ActivityThread 的消息队列中。

scheduleBroadcastsLocked 方法:

    public void scheduleBroadcastsLocked() {
        ...
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

BroadcastHandler 处理广播消息:

    private final class BroadcastHandler extends Handler {
        public BroadcastHandler(Looper looper) {
            super(looper, null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    processNextBroadcast(true);
                } break;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }
    }

BroadcastHandler 会调用 processNextBroadcastLocked 处理广播。

processNextBroadcastLocked 方法如下:

    final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
        BroadcastRecord r;
        ...
                        performReceiveLocked(r.callerApp, r.resultTo,
                            new Intent(r.intent), r.resultCode,
                            r.resultData, r.resultExtras, false, false, r.userId);
        ...
    }

performReceiveLocked 方法会调用到 ActivityThread 的 scheduleRegisteredReceiver 方法。所以,注册广播的应用就收到了广播。

    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        ...
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
        ...
    }

scheduleRegisteredReceiver 在 ActivityThread 类。

        public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                int resultCode, String dataStr, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser, int processState) throws RemoteException {
            updateProcessState(processState, false);
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }

IIntentReceiver 定义在 LoadedApk,IIntentReceiver 的 performReceive 如下:

        public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
            ...
            if (intent == null || !mActivityThread.post(args.getRunnable())) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManager.getService();
                    ...
                    args.sendFinished(mgr);
                }
            }
        }

    }

performReceive 构造了一个 Args 对象,然后 post 给 Handler,然后告诉 AMS 发送完毕。

Args 的 Runnable 如下:

        final class Args extends BroadcastReceiver.PendingResult {
            ...
            public final Runnable getRunnable() {
                return () -> {
                    ...
                        receiver.onReceive(mContext, intent);
                    ...
                };
            }
        }

可以看出 runnable 调用了 receiver.onReceive,也就是 BroadcastReceiver 的 onReceive 回调。因为 runnable 是 post 到 mActivityThread,所以 onReceive 也是执行在主线程。

简单来说,广播是一个发布-订阅过程,也是观察者模式。订阅过程将广播注册到 AMS 的广播列表中。发布过程通过查询广播的 intentfilter 对应的 broadcastReceiver,然后通过 ReceiverDisptacher 将广播分发给各个订阅对象,从而完成这个发布-订阅过程。

发布了105 篇原创文章 · 获赞 2 · 访问量 3758

猜你喜欢

转载自blog.csdn.net/caoshen2014/article/details/102993521