我在偏好活动中注册广播接收者,并从(唤醒)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上
解决方法
sendBroadcastSync()确实运行在被调用的线程上(除非有比赛进行).其实施
should be:
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运行和directly calls接收器的onReceive
其中一个时候,我应该看看为什么(在执行PendingBroadcasts)同步(mReceivers)而不同步(mPendingBroadcasts).