使用LocalBroadcastManager注册的BroacastReceiver的onReceive()运行在哪个线程上?

 常德锦江-余欢 发布于 2023-02-07 17:28

我在偏好活动中注册广播接收器,并从(唤醒)IntentService发送(同步)广播.显然onReceive在服务的线程上运行.这是我的错吗?是否记录了行为?

偏好活动:

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):

SettingsActivity.notifyMonitoringStateChange(this, ac_toggling, true);

例外(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上

2 个回答
  • sendBroadcastSync()确实是在它被调用的线程上运行(除非正在进行竞争).它的实施应该是:

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

    我不确定的竞赛部分是通过sendBroadcast()调用进入的,如果它发现任何匹配的接收者(在LBM中为此意图注册),则在向主循环关联的私有处理程序发送消息之后返回true - 缩写:

    @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并直接调用接收器的onReceive

    有一天我应该研究为什么(在executePendingBroadcasts中)synchronized(mReceivers)而不是synchronized(mPendingBroadcasts).

    2023-02-07 17:31 回答
  • 我曾一天收到与同样的问题sendBroadcastSync(Intent intent)LocalBroadcastManager.当我试图从onReceive(Context context, Intent intent)它更新视图时抛出以下异常:

    android.view.ViewRootImpl $ CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触及其视图.

    最后,我开始知道它可能无法在UI线程上运行,因此使用下面的简单代码:

    public void onReceive(Context context, Intent intent) {
                ....
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        setProgressBar();
                    }
                });
            }
    

    2023-02-07 17:32 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有