下面我们来看,递送广播动作中最重要的processNextBroadcast()。

3.2 最重要的processNextBroadcast()

从processNextBroadcast()的代码,我们就可以看清楚前面说的“平行广播”、“有序广播”和“动态receiver”、“静态receiver”之间的关系了。

我们在前文已经说过,所有的静态receiver都是串行处理的,而动态receiver则会按照发广播时指定的方式,进行“并行”或“串行”处理。能够并行处理的广播,其对应的若干receiver一定都已经存在了,不会牵扯到启动新进程的操作,所以可以在一个while循环中,一次性全部deliver。而有序广播,则需要一个一个地处理,其滚动处理的手段是发送事件,也就是说,在一个receiver处理完毕后,会利用广播队列(BroadcastQueue)的mHandler,发送一个BROADCAST_INTENT_MSG事件,从而执行下一次的processNextBroadcast()。

processNextBroadcast()的代码逻辑大体是这样的:先尝试处理BroadcastQueue中的“平行广播”部分。这需要遍历并行列表(mParallelBroadcasts)的每一个BroadcastRecord以及其中的receivers列表。对于平行广播而言,receivers列表中的每个子节点是个BroadcastFilter。我们直接将广播递送出去即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while (mParallelBroadcasts.size() > 0
{
    r = mParallelBroadcasts.remove(0);
    r.dispatchTime = SystemClock.uptimeMillis();
    r.dispatchClockTime = System.currentTimeMillis();
    final int N = r.receivers.size();
    . . . . . . 
    for (int i=0; i<N; i++) 
    {
        Object target = r.receivers.get(i);
        . . . . . .
        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
    }
    . . . . . .
}

3.2.1 用deliverToRegisteredReceiverLocked()递送到平行动态receiver

deliverToRegisteredReceiverLocked()的代码截选如下:

【frameworks/base/services/java/com/android/server/am/BroadcastQueue.java】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
                                                     BroadcastFilter filter, 
                                                     boolean ordered) 
{
    . . . . . .
    . . . . . .
    if (!skip) 
    {
        if (ordered) 
        {
            r.receiver = filter.receiverList.receiver.asBinder();
            r.curFilter = filter;
            filter.receiverList.curBroadcast = r;
            r.state = BroadcastRecord.CALL_IN_RECEIVE;
            if (filter.receiverList.app != null
            {
                r.curApp = filter.receiverList.app;
                filter.receiverList.app.curReceiver = r;
                mService.updateOomAdjLocked();
            }
        }
         
            . . . . . .
            performReceiveLocked(filter.receiverList.app, 
                                 filter.receiverList.receiver,
                                 new Intent(r.intent), r.resultCode,
                                 r.resultData, r.resultExtras, 
                                 r.ordered, r.initialSticky);
            if (ordered) 
            {
                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
            }
         
        . . . . . .
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
        Intent intent, int resultCode, String data, Bundle extras,
        boolean ordered, boolean sticky) throws RemoteException 
{
    // Send the intent to the receiver asynchronously using one-way binder calls.
    if (app != null && app.thread != null
    {
        // If we have an app thread, do the call through that so it is
        // correctly ordered with other one-way calls.
        app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                data, extras, ordered, sticky);
    
    else 
    {
        receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
    }
}

终于通过app.thread向用户进程传递语义了。注意scheduleRegisteredReceiver()的receiver参数,它对应的就是前文所说的ReceiverDispatcher的Binder实体——InnerReceiver了。

总之,当语义传递到用户进程的ApplicationThread以后,走到:

1
2
3
4
5
6
7
8
9
// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
        int resultCode, String dataStr, Bundle extras, boolean ordered,
        boolean sticky) throws RemoteException 
{
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
}

终于走到ReceiverDispatcher的InnerReceiver了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static final class ReceiverDispatcher 
{
    final static class InnerReceiver extends IIntentReceiver.Stub 
    {
        . . . . . .
        . . . . . .
        public void performReceive(Intent intent, int resultCode,
                                   String data, Bundle extras, 
                                   boolean ordered, boolean sticky) 
        {
            LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
            . . . . . .
            if (rd != null) {
                rd.performReceive(intent, resultCode, data, extras,
                                  ordered, sticky);
            
            . . . . . .
        }
    }
    . . . . . .
    public void performReceive(Intent intent, int resultCode,
                               String data, Bundle extras, 
                               boolean ordered, boolean sticky) 
    {
        . . . . . .
        Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
        if (!mActivityThread.post(args)) // 请注意这一句!
        {
            if (mRegistered && ordered) 
            {
                IActivityManager mgr = ActivityManagerNative.getDefault();
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing sync broadcast to " + mReceiver);
                args.sendFinished(mgr);
            }
        }
    }
}

请注意mActivityThread.post(args)一句,这样,事件泵最终会回调Args参数的run()成员函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final class Args extends BroadcastReceiver.PendingResult implements Runnable 
{
    . . . . . .
    . . . . . .
    public void run() 
    {
        final BroadcastReceiver receiver = mReceiver;
        . . . . . .
        try {
            ClassLoader cl =  mReceiver.getClass().getClassLoader();
            intent.setExtrasClassLoader(cl);
            setExtrasClassLoader(cl);
            receiver.setPendingResult(this);
            receiver.onReceive(mContext, intent);  // 回调具体receiver的onReceive()
        catch (Exception e) {
            . . . . . .
        }
         
        if (receiver.getPendingResult() != null) {
            finish();
        }
        . . . . . .
    }
}

其中的那句receiver.onReceive(this),正是回调我们具体receiver的onReceive()成员函数的地方。噢,终于看到应用程序员熟悉的onReceive()了。这部分的示意图如下:

3.2.2 静态receiver的递送

说完动态递送,我们再来看静态递送。对于静态receiver,情况会复杂很多,因为静态receiver所从属的进程有可能还没有运行起来呢。此时BroadcastRecord节点中记录的子列表的节点是ResolveInfo对象。

1
2
3
4
ResolveInfo info = (ResolveInfo)nextReceiver;
. . . . . .
r.state = BroadcastRecord.APP_RECEIVE;
String targetProcess = info.activityInfo.processName;

那么我们要先找到receiver所从属的进程的进程名。

processNextBroadcast()中启动进程的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ProcessRecord app = mService.getProcessRecordLocked(targetProcess, 
info.activityInfo.applicationInfo.uid);
. . . . . .
if (app != null && app.thread != null
{
    . . . . . .
    app.addPackage(info.activityInfo.packageName);
    processCurBroadcastLocked(r, app);
    return;
    . . . . . .
}
r.curApp = mService.startProcessLocked(targetProcess,
                               info.activityInfo.applicationInfo, true,
                               r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                               "broadcast", r.curComponent,              
                               (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0
                               false)
. . . . . .
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;

如果目标进程已经存在了,那么app.thread肯定不为null,直接调用processCurBroadcastLocked()即可,否则就需要启动新进程了。启动的过程是异步的,可能很耗时,所以要把BroadcastRecord节点记入mPendingBroadcast。

3.2.2.1 processCurBroadcastLocked()

我们先说processCurBroadcastLocked()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private final void processCurBroadcastLocked(BroadcastRecord r,
                        ProcessRecord app) throws RemoteException 
{
    . . . . . .
    r.receiver = app.thread.asBinder();
    r.curApp = app;
    app.curReceiver = r;
    . . . . . .
    . . . . . .
        app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
              mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
              r.resultCode, r.resultData, r.resultExtras, r.ordered);
        . . . . . .
        started = true;
    . . . . . .
}

其中最重要的是调用app.thread.scheduleReceiver()的那句。在IApplicationThread接口中,是这样定义scheduleReceiver()函数原型的:

1
2
3
4
5
void scheduleReceiver(Intent intent, ActivityInfo info, 
                      CompatibilityInfo compatInfo,                      
                      int resultCode, String data, 
                      Bundle extras, boolean sync) 
                      throws RemoteException;

其中ActivityInfo info参数,记录着目标receiver的信息。可以看到,递送静态receiver时,是不会用到RecevierDispatcher的。

用户进程里handleMessage()

1
2
3
4
5
6
case RECEIVER:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
    handleReceiver((ReceiverData)msg.obj);
    maybeSnapshot();
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);    
    break;

ActivityThread中,会运用反射机制,创建出BroadcastReceiver对象,而后回调该对象的onReceive()成员函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void handleReceiver(ReceiverData data) 
{
    . . . . . .
    IActivityManager mgr = ActivityManagerNative.getDefault();
    BroadcastReceiver receiver;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        data.intent.setExtrasClassLoader(cl);
        data.setExtrasClassLoader(cl);
        receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
    catch (Exception e) {
        . . . . . .
    }
    try {
        . . . . . .
        receiver.setPendingResult(data);
        receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);
    catch (Exception e) {
        . . . . . .
    finally {
        sCurrentBroadcastIntent.set(null);
    }
    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}

3.2.2.2 必要时启动新进程

现在我们回过头来看,在目标进程尚未启动的情况下,是如何完成递送的。刚刚我们已经看到调用startProcessLocked()的句子了,只要不出问题,目标进程成功启动后就会调用AMS的attachApplication()。

有关attachApplication()的详情,请参考其他关于AMS的文档,此处我们只需知道它里面又会调用attachApplicationLocked()函数。

1
private final boolean attachApplicationLocked(IApplicationThread thread, int pid)

attachApplicationLocked()内有这么几句:

1
2
3
4
5
6
7
8
9
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
    try {
        didSomething = sendPendingBroadcastsLocked(app);
    catch (Exception e) {
        // If the app died trying to launch the receiver we declare it 'bad'
        badApp = true;
    }
}

它们的意思是,如果新启动的进程就是刚刚mPendingBroadcast所记录的进程的话,此时AMS就会执行sendPendingBroadcastsLocked(app)一句。

1
2
3
4
5
6
7
8
// The app just attached; send any pending broadcasts that it should receive
boolean sendPendingBroadcastsLocked(ProcessRecord app) {
    boolean didSomething = false;
    for (BroadcastQueue queue : mBroadcastQueues) {
        didSomething |= queue.sendPendingBroadcastsLocked(app);
    }
    return didSomething;
}

BroadcastQueue的sendPendingBroadcastsLocked()函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
    boolean didSomething = false;
    final BroadcastRecord br = mPendingBroadcast;
    if (br != null && br.curApp.pid == app.pid) {
        try {
            mPendingBroadcast = null;
            processCurBroadcastLocked(br, app);
            didSomething = true;
        catch (Exception e) {
            . . . . . .
        }
    }
    return didSomething;
}

可以看到,既然目标进程已经成功启动了,那么mPendingBroadcast就可以赋值为null了。接着,sendPendingBroadcastsLocked()会调用前文刚刚阐述的processCurBroadcastLocked(),其内再通过app.thread.scheduleReceiver(),将语义发送到用户进程,完成真正的广播递送。这部分在上一小节已有阐述,这里就不多说了。

3.2.3 说说有序广播是如何循环起来的?

我们知道,平行广播的循环很简单,只是在一个while循环里对每个动态receiver执行deliverToRegisteredReceiverLocked()即可。而对有序广播来说,原则上每次processNextBroadcast()只会处理一个BroadcastRecord的一个receiver而已。当然,此时摘下的receiver既有可能是动态注册的,也有可能是静态的。

对于动态注册的receiver,目标进程处理完广播之后,会间接调用am.finishReceiver()向AMS发出反馈,关于这一步,其实在前面罗列ReceiverDispatcher的performReceive()时已经出现过了,我们再列一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void performReceive(Intent intent, int resultCode,
                           String data, Bundle extras, 
                           boolean ordered, boolean sticky) 
{
    . . . . . .
    Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
    if (!mActivityThread.post(args)) 
    {
        if (mRegistered && ordered) 
        {
            IActivityManager mgr = ActivityManagerNative.getDefault();
            . . . . . .
            args.sendFinished(mgr);  // 请注意这一句!
        }
    }
}

Args继承于BroadcastReceiver.PendingResult,它调用的sendFinished()就是PendingResult的sendFinished():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void sendFinished(IActivityManager am) 
{
    synchronized (this) {
        if (mFinished) {
            throw new IllegalStateException("Broadcast already finished");
        }
        mFinished = true;
     
        try {
            if (mResultExtras != null) {
                mResultExtras.setAllowFds(false);
            }
            if (mOrderedHint) {
                am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                        mAbortBroadcast);
            else {
                // This broadcast was sent to a component; it is not ordered,
                // but we still need to tell the activity manager we are done.
                am.finishReceiver(mToken, 0nullnullfalse);
            }
        catch (RemoteException ex) {
        }
    }
}

代码中的am.finishReceiver()会通知AMS,表示用户侧receiver已经处理好了,或者至少告一段落了,请AMS进行下一步动作。

而对于动态注册的receiver,情况是类似的,最终也是调用am.finishReceiver()向AMS发出回馈的,只不过发起的动作是在ActivityThread的handleReceiver()动作中。前文已经列过这个函数了,大家注意下面的句子即可:

1
2
3
4
5
6
7
8
9
10
11
private void handleReceiver(ReceiverData data) 
{
        . . . . . .
        receiver.setPendingResult(data);
        receiver.onReceive(context.getReceiverRestrictedContext(),
                data.intent);
        . . . . . .
    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}

ReceiverData也是继承于BroadcastReceiver.PendingResult的,它调用的finish()是PendingResult的finish():

1
2
3
4
5
6
7
8
9
10
11
public final void finish() 
{
    if (mType == TYPE_COMPONENT) {
        . . . . . .
    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()内部最终也会调用到am.finishReceiver(),向AMS通告receiver已经处理好了。

AMS侧在收到finishReceiver语义后,执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void finishReceiver(IBinder who, int resultCode, String resultData,
        Bundle resultExtras, boolean resultAbort) 
{
    . . . . . .
    try {
        boolean doNext = false;
        BroadcastRecord r = null;
        synchronized(this) {
            r = broadcastRecordForReceiverLocked(who);
            if (r != null) {
                doNext = r.queue.finishReceiverLocked(r, resultCode,
                    resultData, resultExtras, resultAbort, true);
            }
        }
        if (doNext) {
            r.queue.processNextBroadcast(false);
        }
        trimApplications();
    finally {
        Binder.restoreCallingIdentity(origId);
    }
}

可以看到,如有必要,会继续调用processNextBroadcast(),从而完成有序广播的循环处理。

3.2.4 说说有序广播的timeout处理

因为AMS很难知道一次广播究竟能不能完全成功递送出去,所以它必须实现一种“时限机制”。前文在阐述broadcastIntentLocked()时,提到过new一个BroadcastRecord节点,并插入一个BroadcastQueue里的“平行列表”或者“有序列表”。不过当时我们没有太细说那个BroadcastQueue,现在我们多加一点儿说明。

实际上系统中有两个BroadcastQueue,一个叫做“前台广播队列”,另一个叫“后台广播队列”,在AMS里是这样定义的:

1
2
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;

为什么要搞出两个队列呢?我认为这是因为系统对“广播时限”的要求不同导致的。对于前台广播队列而言,它里面的每个广播必须在10秒之内把广播递送给receiver,而后台广播队列的时限比较宽,只需60秒之内递送到就可以了。具体时限值请看BroadcastQueue的mTimeoutPeriod域。注意,这个10秒或60秒限制是针对一个receiver而言的。比方说“前台广播队列”的某个BroadcastRecord节点对应了3个receiver,那么在处理这个广播节点时,只要能在30秒(3 x 10)之内搞定就可以了。事实上,AMS系统考虑了更多东西,所以它给一个BroadcastRecord的总时限是其所有receiver时限之和的2倍,在此例中就是60秒(2 x 3 x 10)。

对于平行receiver而言,时限的作用小一点儿,因为动态receiver是直接递送到目标进程的,它不考虑目标端是什么时候处理完这个广播的。

然而对于有序receiver来说,时限就比较重要了。因为receiver之间必须是串行处理的,也就是说上一个receiver在没处理完时,系统是不会让下一个receiver进行处理的。从processNextBroadcast()的代码来看,在处理有序receiver时,BroadcastRecord里的nextReceiver域会记录“下一个应该处理的receiver”的标号。只有在BroadcastRecord的所有receiver都处理完后,或者BroadcastRecord的处理时间超过了总时限的情况下,系统才会把这个BroadcastRecord节点从队列里删除。因此我们在processNextBroadcast()里看到的获取当前BroadcastRecord的句子是写死为r = mOrderedBroadcasts.get(0)的。

在拿到当前BroadcastRecord之后,利用nextReceiver值拿到当前该处理的receiver信息:

1
2
3
int recIdx = r.nextReceiver++;
. . . . . .
Object nextReceiver = r.receivers.get(recIdx);

当然,一开始,nextReceiver的值只会是0,表示第一个receiver有待处理,此时会给BroadcastRecord的dispatchTime域赋值。

1
2
3
4
5
6
int recIdx = r.nextReceiver++;
r.receiverTime = SystemClock.uptimeMillis();if (recIdx == 0) {
    r.dispatchTime = r.receiverTime;
    r.dispatchClockTime = System.currentTimeMillis();
    . . . . . .
}

也就是说,dispatchTime的意义是标记实际处理BroadcastRecord的起始时间,那么这个BroadcastRecord所能允许的最大时限值就是:

dispatchTime + 2 * mTimeoutPeriod * 其receiver总数

一旦超过这个时限,而BroadcastRecord又没有处理完,那么就强制结束这个BroadcastRecord节点:

1
2
3
4
5
6
7
8
if ((numReceivers > 0) &&
        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) 
{
    . . . . . .
    broadcastTimeoutLocked(false); // forcibly finish this broadcast
    forceReceive = true;
    r.state = BroadcastRecord.IDLE;
}

此处调用的broadcastTimeoutLocked()的参数是boolean fromMsg,表示这个函数是否是在处理“时限消息”的地方调用的,因为当前是在processNextBroadcast()函数里调用broadcastTimeoutLocked()的,所以这个参数为false。从这个参数也可以看出,另一处判断“处理已经超时”的地方是在消息处理机制里,在那个地方,fromMsg参数应该设为true。

大体上说,每当processNextBroadcast()准备递送receiver时,会调用setBroadcastTimeoutLocked()设置一个延迟消息:

1
2
3
long timeoutTime = r.receiverTime + mTimeoutPeriod;
. . . . . .
setBroadcastTimeoutLocked(timeoutTime);

setBroadcastTimeoutLocked()的代码如下:

1
2
3
4
5
6
7
8
final void setBroadcastTimeoutLocked(long timeoutTime) 
{    if (! mPendingBroadcastTimeoutMessage) 
    {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

只要我们的receiver能及时处理广播,系统就会cancel上面的延迟消息。这也就是说,但凡事件泵的handleMessage()开始处理这个消息,就说明receiver处理超时了。此时,系统会放弃处理这个receiver,并接着尝试处理下一个receiver。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final Handler mHandler = new Handler() 
{
    public void handleMessage(Message msg) {
        switch (msg.what) 
        {
            . . . . . .
            case BROADCAST_TIMEOUT_MSG: 
            {
                synchronized (mService) 
                {
                    broadcastTimeoutLocked(true);
                }
            break;
        }
    }
};

broadcastTimeoutLocked()的代码截选如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final void broadcastTimeoutLocked(boolean fromMsg) 
{
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }
    if (mOrderedBroadcasts.size() == 0) {
        return;
    }
    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mOrderedBroadcasts.get(0);
    . . . . . .
    . . . . . .
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, true);
    scheduleBroadcastsLocked();
    . . . . . .
}

可以看到,当一个receiver超时后,系统会放弃继续处理它,并再次调用scheduleBroadcastsLocked(),尝试处理下一个receiver。

4 尾声

有关Android的广播机制,我们就先说这么多吧。品一杯红茶,说一段代码,管他云山雾罩,任那琐碎冗长,我自冷眼看安卓,瞧他修短随化。

转自http://blog.csdn.net/codefly/article/details/42323235

品茗论道说广播(Broadcast内部机制讲解)(下)的更多相关文章

  1. 品茗论道说广播(Broadcast内部机制讲解)(上)

    1 概述 我们在编写Android程序时,常常会用到广播(Broadcast)机制.从易用性的角度来说,使用广播是非常简单的.不过,这个不是本文关心的重点,我们希望探索得再深入一点儿.我想,许多人也不 ...

  2. [源码分析] 从实例和源码入手看 Flink 之广播 Broadcast

    [源码分析] 从实例和源码入手看 Flink 之广播 Broadcast 0x00 摘要 本文将通过源码分析和实例讲解,带领大家熟悉Flink的广播变量机制. 0x01 业务需求 1. 场景需求 对黑 ...

  3. zookeeper 内部机制学习

    zookeeper 内部机制学习 1. zk的设计目标 最终一致性:client不论连接到那个Server,展示给它的都是同一个视图. 可靠性:具有简单.健壮.良好的性能.如果消息m被到一台服务器接收 ...

  4. Android中的广播Broadcast详解

    今天来看一下Android中的广播机制,我们知道广播Broadcast是Android中的四大组件之一,可见他的重要性了,当然它的用途也很大的,比如一些系统的广播:电量低.开机.锁屏等一些操作都会发送 ...

  5. 万字综述,核心开发者全面解读PyTorch内部机制

    斯坦福大学博士生与 Facebook 人工智能研究所研究工程师 Edward Z. Yang 是 PyTorch 开源项目的核心开发者之一.他在 5 月 14 日的 PyTorch 纽约聚会上做了一个 ...

  6. [Spark內核] 第42课:Spark Broadcast内幕解密:Broadcast运行机制彻底解密、Broadcast源码解析、Broadcast最佳实践

    本课主题 Broadcast 运行原理图 Broadcast 源码解析 Broadcast 运行原理图 Broadcast 就是将数据从一个节点发送到其他的节点上; 例如 Driver 上有一张表,而 ...

  7. NumPy 广播(Broadcast)

    NumPy 广播(Broadcast) 广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行. 如果两个数组 a 和 b ...

  8. 广播 (Broadcast)

    广播 :在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制.我们拿广播电台来做个比方.我们平常使用收音机收音是这样的:许许多多不同的广播电台通过特定的频率来发送他们的内 ...

  9. Numpy | 10 广播(Broadcast)

    广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行. 下面的图片展示了数组 b 如何通过广播来与数组 a 兼容. 4x ...

随机推荐

  1. 如何在form初始化时自动隐藏FOLDER列

    方法1:直接设定PROMPT列和数据列ITEM的VISIBLE属性为No 方法2:在WHEN-NEW-FORM-INSTANCE触发器里: l_old_itm := :system.cursor_it ...

  2. 利用require.js实现javascript模块化加载

    这种引入很看到很想死吧! <script src="1.js"></script> <script src="2.js">& ...

  3. python笔记12-python多线程之事件(Event)

    前言 小伙伴a,b,c围着吃火锅,当菜上齐了,请客的主人说:开吃!,于是小伙伴一起动筷子,这种场景如何实现 Event(事件) Event(事件):事件处理的机制:全局定义了一个内置标志Flag,如果 ...

  4. Linux /sbin/service脚本一个基本无影响的bug

    CentOS提供了一个启动服务的功能:service [service name] (start|stop|restart|...),此功能的执行脚本为/sbin/service. 今天看了下此脚本, ...

  5. SQL locate()函数

    LOCATE(substr,str), LOCATE(substr,str,pos) 第一个语法返回字符串str第一次出现的子串substr的位置. 第二个语法返回第一次出现在字符串str的子串sub ...

  6. sql 改动表以及表字段

    用SQL语句加入删除改动字段 1.添加字段      alter table docdsp    add dspcode char(200)      alter table tbl add meet ...

  7. 【Todo】phantomjs获取动态网页

    上面一篇文章,使用了cheerio, http, request等库,来抓取了Lofter上面的美女图片. 但是很多网页是动态渲染的.据了解,Phantom.js 可以获取动态渲染的网页 http:/ ...

  8. rootkit后门之安装流程

    1.首先是获得远程服务器的root权限 2.然后下载rootkit程序,本文用到的是mafix (下载前最好把杀毒软件关掉,基本上会报毒的!) 3.开始安装 tar -xvzf mafix.tar.g ...

  9. 编译 arm 版的qt

    因为项目需要,我们需要在开发板上使用QT开发平台,因此需要编译一个arm版的QT. 在网上找了一些资料,费了几天时间,终于成功了. 第一步,准备源码 先下载QT 源码,在http://qt-proje ...

  10. 深入理解JS函数节流和去抖动

    一.什么是节流和去抖? 1.节流 节流就是拧紧水龙头让水少流一点,但是不是不让水流了.想象一下在现实生活中有时候我们需要接一桶水,接水的同时不想一直站在那等着,可能要离开一会去干一点别的事请,让水差不 ...