¿Cuál es la forma correcta de compartir datos entre AppWidgetProvider y RemoteViewsService.RemoteViewsFactory?
Actualmente, mi AppWidgetProvider
está teniendo datos estáticos. Se utiliza para pasar información alrededor de AppWidgetProvider
y RemoteViewsService.RemoteViewsFactory
public class MyAppWidgetProvider extends AppWidgetProvider { // Key will be widget id private static Map<Integer, Holder> holderMap = new java.util.concurrent.ConcurrentHashMap<Integer, Holder>(); public static int getClickedColumn(int appWidgetId) { Holder holder = holderMap.get(appWidgetId); if (holder == null) { return -1; } return holder.clickedColumn; }
public class AppWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { @Override public void onDataSetChanged() { int clickedColumn = MyAppWidgetProvider.getClickedColumn(mAppWidgetId);
Llamar el método estático de AppWidgetProvider
funciona bien en la mayoría de la situación.
Sin embargo, a veces, si coloco el widget en la pantalla de inicio, dejarlo estar allí durante pocas horas. Cuando vuelva y escoll el ListView
, puedo conseguir el error siguiente aleatoriamente.
java.lang.ExceptionInInitializerError at org.yccheok.project.gui.widget.AppWidgetRemoteViewsFactory.onDataSetChanged(AppWidgetRemoteViewsService.java:390) at android.widget.RemoteViewsService$RemoteViewsFactoryAdapter.onDataSetChanged(RemoteViewsService.java:142) at com.android.internal.widget.IRemoteViewsFactory$Stub.onTransact(IRemoteViewsFactory.java:49) at android.os.Binder.execTransact(Binder.java:367) at dalvik.system.NativeStart.run(Native Method) Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.<init>(Handler.java:121) at org.yccheok.project.gui.widget.MyAppWidgetProvider.<clinit>(MyAppWidgetProvider.java:564)
De <clinit>
, sospecho que MyAppWidgetProvider
es destruido por el sistema operativo? Esto causa que AppWidgetRemoteViewsFactory
quiera realizar la inicialización de la clase, antes de llamar a la función estática?
¿Significa esto que MyAppWidgetProvider
puede ser destruido en cualquier momento por el sistema operativo y no deberíamos colocar datos estáticos compartibles en él?
Si es así, ¿cuál es la forma correcta de compartir datos entre AppWidgetProvider
y RemoteViewsService.RemoteViewsFactory
? (Además de usar File o SharedPreferences)
RemoteViewsFactory -> AppWidgetProvider
La comunicación desde RemoteViewsFactory a AppWidgetProvider se puede hacer usando Broadcasts, por ejemplo, como esto:
Intent intent = new Intent(ACTION_PROGRESS_OFF); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
El AppWidgetProvider recibe el evento de la siguiente manera:
@Override public void onReceive(final Context context, final Intent intent) { // here goes the check if the app widget id is ours final String action = intent.getAction(); if (ACTION_PROGRESS_OFF.equals(action)) { // turn off the refresh spinner
Por supuesto, la acción de difusión debe definirse en el manifiesto:
<receiver android:name="..."> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="myPackage.ACTION_PROGRESS_OFF" /> </intent-filter> <meta-data ... /> </receiver>
AppWidgetProvider -> RemoteViewsFactory
Una manera de comunicarse con RemoteViewsFactory (y en su caso probablemente la mejor) es enviar la información en la intención del servicio que está pasando a RemoteViewsAdapter:
Intent intentRVService = new Intent(context, RemoteViewsService.class); intentRVService.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); // let's put in some extra information we want to pass to the RemoteViewsFactory intentRVService.putExtra("HELLO", "WORLD"); // when intents are compared, the extras are ignored, so we need to // embed the extras into the data so that the extras will not be ignored intentRVService.setData(Uri.parse(intentRVService.toUri(Intent.URI_INTENT_SCHEME))); rv.setRemoteAdapter(appWidgetId, R.id.my_list, intentRVService); rv.setEmptyView(R.id.my_list, android.R.id.empty); // create the pending intent template for individual list items ... rv.setPendingIntentTemplate(R.id.my_list, pendingIntentTemplate); appWidgetMgr.notifyAppWidgetViewDataChanged(appWidgetId, R.id.my_list);
RemoteViewsService puede recuperar fácilmente la información de la intención y pasarla a la RemoteViewsService.RemoteViewsFactory.
No estoy 100% seguro cuándo y cómo su widget decide clasificar los datos pero asumiría si el usuario escoge una columna para clasificar entonces usted tiene que pasar con el ciclo de la actualización con notifyAppWidgetViewDataChanged y después usted pasaría esa columna adelante. Si necesita esa información más tarde, entonces tendría que almacenar la información de alguna manera (SharedPreferences).