¿En qué hilo se ejecuta onReceive () de un BroacastReceiver registrado con LocalBroadcastManager?

Registro un receptor de difusión en una actividad de preferencia y le envío una transmisión (sincronizada) desde un (IntentService) de Wakeful. Al parecer, onReceive se ejecuta en el hilo del servicio. ¿Es esto una falta de mi parte? ¿Es un comportamiento documentado?

Actividad de preferencia:

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

Excepción (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) 

En un emulador API 17

sendBroadcastSync() hecho se ejecuta en el hilo que se llama desde (excepto si hay una carrera en curso). Su implementación debe ser :

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

La parte de la carrera que no estoy seguro acerca de ingresa a través de la llamada sendBroadcast() que devuelve true si encontró cualquier receptor coincidente (registrado con LBM para esta intención), después de enviar un mensaje a un manejador privado asociado con el bucle principal – abreviado:

 @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; } } 

dónde:

  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); } } 

No estoy seguro de si puede haber una carrera entre el hilo principal y el hilo que sendBroadcastSync() está ejecutando – por lo que executePendingBroadcasts() se ejecutará en el hilo principal. Si no, entonces executePendingBroadcasts() ejecuta en el thread sendBroadcastSync se ejecuta y llama directamente a la onReceive de los receptores

Uno de estos días debería mirar por qué (en executePendingBroadcasts) synchronized(mReceivers) y no synchronized(mPendingBroadcasts) .

Yo había estado recibiendo el mismo problema con el sendBroadcastSync(Intent intent) con el LocalBroadcastManager . Cuando había estado tratando de actualizar la vista desde el onReceive(Context context, Intent intent) , lanzó la siguiente excepción:

Android.view.ViewRootImpl $ CalledFromWrongThreadException: Sólo el subproceso original que creó una jerarquía de vistas puede tocar sus vistas.

Eventualmente, llegué a saber que podría no ejecutarse en el hilo de interfaz de usuario y, por tanto, recurrió a utilizar el código simple a continuación:

 public void onReceive(Context context, Intent intent) { .... getActivity().runOnUiThread(new Runnable() { @Override public void run() { setProgressBar(); } }); } 
  • IntentService y Threadpool
  • Android IllegalArgumentException lockCanvas ()
  • Thread, Asynctask o intentService para mi aplicación?
  • Android: actualiza el mapa de bits del hilo del temporizador
  • Iniciar un servicio en un proceso separado android
  • ¿Por qué los subprocesos nativos se comportan de forma diferente cuando la aplicación está en segundo plano?
  • Actualizar la interfaz de usuario desde el subproceso
  • Facebook y GoogleAnalytics está causando la señal fatal 11 (SIGSEGV)
  • RxJava - subir archivos secuencialmente - emitir el siguiente elemento, cuando onNext llamado
  • Hilos y uso de tareas Asynctask para httppost
  • ¿Qué sucede cuando mi actividad se destruye cuando estoy usando IntentService con ResultReceiver
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.