Broadcast之goAsync方法

我们先来看看源码中对goAsync方法的注释

/**
 * This can be called by an application in {@link #onReceive} to allow
 * it to keep the broadcast active after returning from that function.
 * This does <em>not</em> change the expectation of being relatively
 * responsive to the broadcast (finishing it within 10s), but does allow
 * the implementation to move work related to it over to another thread
 * to avoid glitching the main UI thread due to disk IO.
 * 可以在onReceive方法中调用,使得在onReceive方法返回之后广播仍处于活动状态。
 * 这并不会改变广播广播超时时间,但是允许将其相关的工作移动到其他线程中处理,
 * 避免由于磁盘IO导致的主线程故障。
 */
 public final PendingResult goAsync();
复制代码

简单来说,goAsync提供了一种机制,让我们可以在异步线程中处理广播消息,以防止主线程被阻塞。

我们知道AMS是通过跨进程调用ApplicationThread来实现调用广播的onReceive方法。 在正式开始介绍goAsync之前,ApplicationThread内部的实现逻辑。 ApplicationThread中有两个方法处理广播:scheduleReceiverscheduleRegisteredReceiver,对应静态广播接收者和动态广播接收者。

1 ApplicationThread是如何处理广播的

1.1 动态广播处理

scheduleRegisteredReceiver方法内部仅是调用了IIntentReceiver.performReceive方法。IIntentReceiver是我们在调用register***方法时方法内部创建的,它的实现类是InnerReceiverLoadedApk的内部类。performReceive方法会经历下面一系列的方法调用: IIntentReceiver.performReceive-> ReceiverDispatcher.performReceive-> Handler.post

最终进入Args.run方法。

public void run() {
    //mReceiver就是我们注册的广播接收者
    final BroadcastReceiver receiver = mReceiver;
    final boolean ordered = mOrdered;

    //log

    final IActivityManager mgr = ActivityManagerNative.getDefault();
    final Intent intent = mCurIntent;
    mCurIntent = null;

    //exception

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
    try {
        ClassLoader cl =  mReceiver.getClass().getClassLoader();
        intent.setExtrasClassLoader(cl);
        setExtrasClassLoader(cl);
        //关键点
        //在调用onReceive之前会调用setPendingResult
        //Args派生自PendingResult
        receiver.setPendingResult(this);
        receiver.onReceive(mContext, intent);
    } catch (Exception e) {
        //exception
    }
    //onReceive方法结束之后
    //如果getPendingResult不为null
    //那么调用PendingResult.finish方法
    if (receiver.getPendingResult() != null) {
        finish();
    }
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
复制代码

BroadcastReceiversetPendingResultgetPendingResult方法非常简单,就是BroadcastReceiver.mPendingResult字段的getset方法。 我们来看看PendingResult.finish方法。

public final void finish() {
    //mType有三个值TYPE_COMPONENT、TYPE_REGISTERED、TYPE_UNREGISTERED
    //分别代表静态广播接收者、已注册的广播接收者和已注销的广播接收者
    if (mType == TYPE_COMPONENT) {
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        if (QueuedWork.hasPendingWork()) {
            //...
            QueuedWork.singleThreadExecutor().execute( new Runnable() {
                @Override public void run() {
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast after work to component " + mToken);
                    sendFinished(mgr);
                }
            });
        } else {
            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                    "Finishing broadcast to component " + mToken);
            sendFinished(mgr);
        }
    } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                "Finishing broadcast to " + mToken);
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        sendFinished(mgr);
    }
}
复制代码

该方法最终调用了sendFinished方法,值得注意的是只有静态广播接收者以及有序广播场景下的动态接收者才会走到sendFinished方法中。通过之前的对于广播派发流程的分析,我们知道静态广播接收者都是有序的。因此可以说只有按顺序被接收的广播才会进入到sendFinished中。

public void sendFinished(IActivityManager am) {
    synchronized (this) {
        //...

        try {
            //...
            if (mOrderedHint) {
                //有序广播
                am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras, mAbortBroadcast, mFlags);
            } else {
                //静态广播接收者
                am.finishReceiver(mToken, 0, null, null, false, mFlags);
            }
        } catch (RemoteException ex) {
        }
    }
}
复制代码

sendFinished的工作就是回调AMS.finishReceiver方法

public void finishReceiver(IBinder who, int resultCode, String resultData,
                               Bundle resultExtras, boolean resultAbort, int flags) {
    //...

    final long origId = Binder.clearCallingIdentity();
    try {
        boolean doNext = false;
        BroadcastRecord r;

        synchronized (this) {
            BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
                    ? mFgBroadcastQueue : mBgBroadcastQueue;
            //查找对应的有序广播
            r = queue.getMatchingOrderedReceiver(who);
            if (r != null) {
                //finishReceiverLocked会清理当前广播的一些状态
                //然后决定是否可以处理下一个接收者
                doNext = r.queue.finishReceiverLocked(r, resultCode,
                        resultData, resultExtras, resultAbort, true);
            }
        }
        //doNext为true,则继续处理有序广播的下一个接收者
        if (doNext) {
            r.queue.processNextBroadcast(false);
        }
        trimApplications();
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}
复制代码

AMS.finishReceiver其实是个承上启下的作用

  1. 上一个广播接收者已经完成接收工作,清理当前广播的一些数据
  2. 决定是否开始处理下一个广播接收者。

1.2 静态广播处理

静态广播接收者的处理方法为scheduleReceiver,该方法会向主线程抛一个RECEIVER消息,然后进入ActivityThread.handleReceiver方法中

private void handleReceiver(ReceiverData data) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();

    String component = data.intent.getComponent().getClassName();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);

    IActivityManager mgr = ActivityManagerNative.getDefault();

    BroadcastReceiver receiver;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        data.intent.setExtrasClassLoader(cl);
        data.intent.prepareToEnterProcess();
        data.setExtrasClassLoader(cl);
        //通过反射构造一个接收者实例
        receiver = (BroadcastReceiver) cl.loadClass(component).newInstance();
    } catch (Exception e) {
        //exception
    }

    try {
        Application app = packageInfo.makeApplication(false, mInstrumentation);

        //log

        //下面的过程与动态广播接收者如出一辙
        ContextImpl context = (ContextImpl) app.getBaseContext();
        sCurrentBroadcastIntent.set(data.intent);
        receiver.setPendingResult(data);
        receiver.onReceive(context.getReceiverRestrictedContext(),
                data.intent);
    } catch (Exception e) {
        //exception
    } finally {
        sCurrentBroadcastIntent.set(null);
    }

    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}
复制代码

静态广播接收者的处理过程与处理动态广播接收者的过程如出一辙,唯一的不同点是对于静态广播接收者,系统还需要通过反射其创建对应的实例。

接下来的过程我们在分析动态广播接收者的时候都已经分析的差不多了,这里不再赘述。

1.3 总结

通过上面的分析,我们可以大致总结出,下面这个流程

而对于无序广播中的动态接收者,这个过程是没有意义的。因为通过对AMS.finishReceiver方法的分析,我们可以知道这整个过程其实是有序(包括静态广播接收者)广播按顺序广播的一个机制。因此无序广播中的动态接收者源码中根本就不会进入到sendFinished中。

2 goAsync方法

现在回过头来看看goAsync方法

public final PendingResult goAsync() {
    PendingResult res = mPendingResult;
    mPendingResult = null;
    return res;
}
复制代码

这个方法非常简单,返回mPendingResult并将其设置为null。 如果我们在onReceive方法中调用该方法,这意味着广播处理流程被打断了,当onReceive方法执行完毕,由于mPendingResultnull,因此并不会马上回调AMS.finishReceiver方法。而且由于goAsync返回了PendingResult,因此我们可以任意时刻、任意线程去调用PendingResult.finish去回调AMS。相当于将一个同步回调变成了异步回调。 而在这异步回调过程中,我们可以新起线程进行一些耗时的IO操作等等。

3 总结

结合1和2,我们不难得出一个结论:goAsync其实是有序广播场景下进行异步广播处理的一个解决方案。 同时它的粒度非常细,可以精确到单个广播。因此goAsync绝对是有序耗时广播场景下处理广播的一大利器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值