I wrote a collection widget to show calllogs in home screen. So there may have 2 receivers exist: one is widget provider the other is call log content observer. Im not sure whether content observer is a receiver or not.
CallLogAppWidgetProvider is declared in manifest.xml and I need to store some data in this receiver object (for using later in observer).
CallLogContentObserver is constructed and registered when WidgetProvider triggered. Observer is triggered when calls are made.
The problem is, when I first place my widget, widgetprovide is constructed, triggered and onUpdate is called, I can see CallLogAppWidgetProvider.mContext and CallLogAppWidgetProvider.mWidgetIds is set in app's main thread(tid=8148,pid=8148).
but when calls are made, CallLogContentObserver.onChange is called in the same thread(tid=8148,pid=8148). Inside onChange, mContext has the value that set previously but mWidgetIds is null, so mywidget could not reflect calllog's changes.
According to this post: Save data in Broadcast receiver , my widgetprovider may be destroyed when it returned, so it could explain why mWidgetIds is null, but conflict with mContext stored old value. And that post did not explain why we need persist our data in receivers.
If widgetprovider is destroyed, then when observer triggered how did android start to run observer's onChange? Is it the same mechanism like closures in lua? Why mContext can store a value?
If widgetprovider is not destroyed, then why mWidgetIds is null when observer runs? I dont know exactly how java passes primitive array, according to this post: Is an array a primitive type or an object (or something else entirely)? , arrays are runtime classes created by jvm, so mWidgetIds is a strong reference, the integer array mWidgetIds points to can not be gc after provider returned, because at least mWidgetIds points to it. What happend when provider returned?
Could you please tell me what on earth happend behind thses scenes? Any information will be precitated, thank you.
code is below:
public class CallLogAppWidgetProvider extends AppWidgetProvider {
private static final String TAG = "CallLogAppWidgetProvider";
private ContentObserver mContentObserver = null;
private Handler mHandler = new Handler();
private Context mContext = null; //data to store
private int[] mWidgetIds; //data to store
protected class CallLogContentObserver extends ContentObserver {
public CallLogContentObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
@Override
public void onChange(boolean selfChange) {
AppWidgetManager.getInstance(mContext).notifyAppWidgetViewDataChanged(mWidgetIds, R.id.calllog_list);
}
}
@Override
public void onEnabled(Context context) {
mContext = context; //store data when first place in home screen
mContentObserver = new CallLogContentObserver(mHandler);
context.getContentResolver().registerContentObserver(CallLog.CONTENT_URI, true, mContentObserver);
}
@Override
public void onDisabled(Context context) {
context.getContentResolver().unregisterContentObserver(mContentObserver);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
mWidgetIds = appWidgetIds; //store data when first place in home screen
for (int i = 0; i < appWidgetIds.length; i++) {
Intent intent = new Intent(context, CallLogRemoteViewsService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.calllog_appwidget);
rv.setRemoteAdapter(R.id.calllog_list, intent);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}