哪个线程不与LocalBroadcastManager注册的BroacastReceiver运行的onReceive()? [英] On which thread does onReceive() of a BroacastReceiver registered with LocalBroadcastManager run?

查看:433
本文介绍了哪个线程不与LocalBroadcastManager注册的BroacastReceiver运行的onReceive()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注册了preference活动的广播接收器,并从(清醒)IntentService发送一个(同步)播出。很明显的onReceive运行服务的线程上。这是我的部分的错吗?难道是记录的行为?

I register a broadcast receiver in a preference activity and send it a (sync) broadcast from a (Wakeful) IntentService. Apparently onReceive runs on the service's thread. Is this a fault of my part ? Is it documented behavior ?

preference活动:

Preference activity:

public final class SettingsActivity extends BaseSettings {

    private static CharSequence sMasterKey;
    private CheckBoxPreference mMasterPref;
    // Receiver
    /** If the master preference is changed externally this reacts */
    private BroadcastReceiver mExternalChangeReceiver =
        new ExternalChangeReceiver();

    public static void notifyMonitoringStateChange(Context ctx,
            CharSequence action, boolean isToggling) {
        final LocalBroadcastManager lbm = LocalBroadcastManager
            .getInstance(ctx);
        Intent intent = new Intent(ctx, ExternalChangeReceiver.class);
        intent.setAction(action.toString());
        intent.putExtra(TOGGLING_MONITORING_IN_PROGRESS, isToggling);
        lbm.sendBroadcastSync(intent);
    }

    @Override
    protected void onStart() {
        super.onStart();
        final LocalBroadcastManager lbm = LocalBroadcastManager
            .getInstance(this);
        lbm.registerReceiver(mExternalChangeReceiver, new IntentFilter(
            ac_toggling.toString()));
    }

    @Override
    protected void onStop() {
        // may not be called in Froyo
        final LocalBroadcastManager lbm = LocalBroadcastManager
            .getInstance(this);
        lbm.unregisterReceiver(mExternalChangeReceiver);
        super.onStop();
    }

    private final class ExternalChangeReceiver extends BroadcastReceiver {

        ExternalChangeReceiver() {}

        @Override
        @SuppressWarnings("synthetic-access")
        public void onReceive(Context ctx, Intent intent) {
            if (sMasterKey == null || mMasterPref == null) return; // if
            // onPostReceive has not run this will be null
            final String action = intent.getAction();
            if (ac_toggling.equals(action)) {
                final boolean isToggling = intent.getBooleanExtra(
                    TOGGLING_MONITORING_IN_PROGRESS, false);
                Log.w(ExternalChangeReceiver.class.getSimpleName(),
                    "isToggling " + isToggling);
                mMasterPref.setEnabled(!isToggling); // line 168 !!!
                refreshMasterPreference(isToggling);
            }
        }
    }
}

IntentService(LocationMonitor):

IntentService (LocationMonitor):

SettingsActivity.notifyMonitoringStateChange(this, ac_toggling, true);

异常(E / AndroidRuntime):

Exception (E/AndroidRuntime):

FATAL EXCEPTION: IntentService[LocationMonitor]
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4746)
    at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:823)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.view.View.requestLayout(View.java:15473)
    at android.widget.AbsListView.requestLayout(AbsListView.java:1819)
    at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:813)
    at android.widget.AbsListView$AdapterDataSetObserver.onChanged(AbsListView.java:5958)
    at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
    at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
    at android.preference.PreferenceGroupAdapter.onPreferenceChange(PreferenceGroupAdapter.java:238)
    at android.preference.Preference.notifyChanged(Preference.java:1099)
    at android.preference.Preference.setEnabled(Preference.java:726)
    at gr.uoa.di.monitoring.android.activities.SettingsActivity$ExternalChangeReceiver.onReceive(SettingsActivity.java:168)
    at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
    at android.support.v4.content.LocalBroadcastManager.sendBroadcastSync(LocalBroadcastManager.java:278)
    at gr.uoa.di.monitoring.android.activities.SettingsActivity.notifyMonitoringStateChange(SettingsActivity.java:54)
    at gr.uoa.di.monitoring.android.services.Monitor.abort(Monitor.java:241)
    at gr.uoa.di.monitoring.android.services.LocationMonitor.doWakefulWork(LocationMonitor.java:103)
    at com.commonsware.cwac.wakeful.WakefulIntentService.onHandleIntent(WakefulIntentService.java:94)
    at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.os.HandlerThread.run(HandlerThread.java:60)

在一个仿真器API 17

On an emulator API 17

推荐答案

sendBroadcastSync()确实是从调用线程上运行(除非有一场比赛正在进行)。它的实施<一个href="https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v4/java/android/support/v4/content/LocalBroadcastManager.java"相对=nofollow>应的:

sendBroadcastSync() is indeed run on the thread it is called from (except if there is a race going on). Its implementation should be:

public void sendBroadcastSync(Intent intent) { // directly calls executePendingBroadcasts
        if (sendBroadcast(intent)) {
            executePendingBroadcasts();
        }
}

比赛的一部分,我不知道关于通过 sendBroadcast()呼叫若发现任何匹配的接收器(注册LBM此意图),它返回true,<进入EM>将消息发送到一个私人处理后的与主回路<关联/ EM> - 简称:

The race part I am not sure about enters via the sendBroadcast() call which returns true if it found any matching receiver (registered with LBM for this intent), after sending a message to a private handler associated with the main loop - abbreviated:

@Override
public boolean sendBroadcast(Intent intent) {
    synchronized (mReceivers) {
        final String action = intent.getAction();
        final ArrayList<ReceiverRecord> entries = mActions.get(action);
        if (entries == null) return false; // no receivers for that action
        ArrayList<ReceiverRecord> receivers = new ArrayList<ReceiverRecord>();
        for (ReceiverRecord receiver : entries) {
            if (receiver.broadcasting) continue;
            // match the intent
            int match = receiver.filter.match(action,
                intent.resolveTypeIfNeeded(mAppContext.getContentResolver()),
                intent.getScheme(), intent.getData(),
                intent.getCategories(), "LocalBroadcastManager");
            if (match >= 0) {
                receivers.add(receiver);
                receiver.broadcasting = true;
            }
        }
        final int size = receivers.size();
        if (size == 0) return false; // no receivers for this intent
        for (int i = 0; i < size; i++) {
            receivers.get(i).broadcasting = false;
        }
        mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
        if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
            mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
        }
        return true;
    }
}

其中:

    mHandler = new Handler(context.getMainLooper()) {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_EXEC_PENDING_BROADCASTS:
                    executePendingBroadcasts();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }

我不知道是否有可能是主线程和线程 sendBroadcastSync()正在执行之间的竞争 - 这样的 executePendingBroadcasts() 将运行在主线程。如果没有,那么 executePendingBroadcasts()运行线程 sendBroadcastSync 运行,并在<一个href="http://grep$c$c.com/file/repository.grep$c$c.com/java/ext/com.google.android/android/4.3_r2.1/android/support/v4/content/LocalBroadcastManager.java#297"相对=nofollow>直接调用接收器的的onReceive

I am not sure if there can be a race between the main thread and the thread sendBroadcastSync() is executing - so the executePendingBroadcasts() will run on the main thread. If not then executePendingBroadcasts() runs on the thread sendBroadcastSync is run and directly calls the onReceive of the receivers

总有一天我应该看看为什么(在executePendingBroadcasts)同步(mReceivers),而不是同步(mPendingBroadcasts)

One of these days I should look into why (in executePendingBroadcasts) synchronized(mReceivers) and not synchronized(mPendingBroadcasts).

这篇关于哪个线程不与LocalBroadcastManager注册的BroacastReceiver运行的onReceive()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆