¿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:
- Compartir recursos entre el contexto de OpenGL en Android
- Actualización de la interfaz de usuario con Runnable y postDelayed no funciona con la aplicación de temporizador
- Multithreading de Android: WaitForGcToComplete después de enviar la aplicación al fondo
- Implementación de un grupo de subprocesos dentro de un servicio
- Método de dibujo de Android que se ejecuta lento
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
- ¿Cómo puedo asegurar que el controlador de otro hilo no sea nulo antes de llamarlo?
- ¿Por qué un AsyncTask no se inicia si se inicia / se detiene repetidamente?
- Utilizar controlador para publicar en UI Thread
- Acceso al dominio desde un subproceso incorrecto Excepción mientras se envió una copia usando copyFromRealm
- ¿Posibles estados de los subprocesos nativos en Android?
- Android verificar el estado WIFI (desconectado o el usuario ha cambiado WIFI) ¿Cómo FLAG?
- Registrar un receptor de difusión de un servicio en un nuevo hilo
- Iniciar nuevo subproceso en Async Task
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(); } }); }
- Cómo llamar a fragmento de una actividad en android?
- Cómo obtener el icono de otras aplicaciones (Android)