Android-数据库监听ContentObserver--实例剖析

100 阅读4分钟

以launcher app为例说明,此功能的核心是ContentResolver 的notifyChange 和 registerContentObserver 接口 。

Launcher.java 的一个方法(在onCreate方法里调用),这里即是为要监视的对象注册Observer。我这里讨论如何监视数据库里数据的变化,所以要监视的对象自然是数据库了, LauncherSettings.Favorites.CONTENT_URI 指向的就是一张数据库表(详见launcher app源码)。
private void registerContentObservers() {
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI, true,
mWidgetObserver);
resolver.registerContentObserver (LauncherSettings.Favorites.CONTENT_URI, true,                   mObserver);
}

其中mObserver是个 FavoritesChangeObserver 类型的对象(只以它为例讲解),FavoritesChangeObserver类的定义如下:
private class FavoritesChangeObserver extends ContentObserver {
public FavoritesChangeObserver() {
super(new Handler());
}
@Override
public void onChange(boolean selfChange) {
onFavoritesChanged();
}
}

onFavoritesChanged()方法是Observer定义的一部分,用于在接收到通知后进行相应的处理操作。这个方法里的内容不展开叙述,它的工作就是刷新Home界面。
private void onFavoritesChanged() {
mDesktopLocked = true;
mModel.loadUserItems(false, this, false, false);
}



LauncherProvider.java
这里是实际操作数据库的地方,操作完成之后会视情况通知前面注册的Observer。
private void sendNotify (Uri uri) {
String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
if (notify == null || "true".equals(notify)) {
getContext().getContentResolver().
 notifyChange (uri, null);
}
 
}


@Override
public Uri insert(Uri uri, ContentValues initialValues) {
SqlArguments args = new SqlArguments(uri);

SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final long rowId = db.insert(args.table, null, initialValues);
if (rowId <= 0) return null;

uri = ContentUris.withAppendedId(uri, rowId);
sendNotify (uri);

return uri;
}

**

--------------------Android 3.0自带的天气预报例子代码  --------------------------------

下面是manifest.xml中的关键代码,只是少了xml的编码头

  1. <manifest xmlns:android="schemas.android.com/apk/res/and…"  
  2.     package="com.example.android.weatherlistwidget">  
  3.     <uses-sdk android:minSdkVersion="11" />  
  4.     <application android:label="Weather Widget Sample">  
  5.          <receiver android:name="WeatherWidgetProvider">  
  6.               
  7.                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
  8.               
  9.                    <meta-data android:name="android.appwidget.provider"  
  10.                     android:resource="@xml/widgetinfo" />  
  11.           
  12.          <service android:name="WeatherWidgetService"  
  13.             android:permission="android.permission.BIND_REMOTEVIEWS"  
  14.             android:exported="false" />  
  15.          <provider android:name="WeatherDataProvider"  
  16.               android:authorities="com.example.android.weatherlistwidget.provider" />  
  17.       
  18.  

这里WeatherDataProvider.Java的源码为主要是ContentProvider相关的处理,这里作为appWidget的receiver\

  • class WeatherDataPoint {   

  •     String city; //城市   

  •     int degrees; //度数   

  •     WeatherDataPoint(String c, int d) {   

  •         city = c;   

  •         degrees = d;   

  •     }   

  • }   

  • public class WeatherDataProvider extends ContentProvider {   

  •     public static final Uri CONTENT_URI =   

  •         Uri.parse("content://com.example.android.weatherlistwidget.provider");   

  •     public static class Columns {   

  •         public static final String ID = "_id";   

  •         public static final String CITY = "city";   

  •         public static final String TEMPERATURE = "temperature";   

  •     }   

  •      private static final ArrayList sData = new ArrayList();   

  •     @Override  

  •     public boolean onCreate() {   

  •         sData.add(new WeatherDataPoint("San Francisco", 13));   

  •         sData.add(new WeatherDataPoint("New York", 1));   

  •         sData.add(new WeatherDataPoint("Seattle", 7));   

  •         sData.add(new WeatherDataPoint("Boston", 4));   

  •         sData.add(new WeatherDataPoint("Miami", 22));   

  •         sData.add(new WeatherDataPoint("Toronto", -10));   

  •         sData.add(new WeatherDataPoint("Calgary", -13));   

  •         sData.add(new WeatherDataPoint("Tokyo", 8));   

  •         sData.add(new WeatherDataPoint("Kyoto", 11));   

  •         sData.add(new WeatherDataPoint("London", -1));   

  •         sData.add(new WeatherDataPoint("Nomanisan", 27));   

  •         return true;   

  •     }   

  •     @Override  

  •     public synchronized Cursor query(Uri uri, String[] projection, String selection,   

  •             String[] selectionArgs, String sortOrder) {   

  •         assert(uri.getPathSegments().isEmpty());   

  •          final MatrixCursor c = new MatrixCursor(   

  •                 new String[]{ Columns.ID, Columns.CITY, Columns.TEMPERATURE });   

  •         for (int i = 0; i < sData.size(); ++i) {   

  •             final WeatherDataPoint data = sData.get(i);   

  •             c.addRow(new Object[]{ new Integer(i), data.city, new Integer(data.degrees) });   

  •         }   

  •         return c;   

  •     }   

  •     @Override  

  •     public String getType(Uri uri) {   

  •         return "vnd.android.cursor.dir/vnd.weatherlistwidget.citytemperature";   

  •     }   

  •     @Override  

  •     public Uri insert(Uri uri, ContentValues values) {   

  •         return null;   

  •     }   

  •     @Override  

  •     public int delete(Uri uri, String selection, String[] selectionArgs) {   

  •         return 0;   

  •     }   

  •     @Override  

  •     public synchronized int update(Uri uri, ContentValues values, String selection,   

  •             String[] selectionArgs) {   

  •         assert(uri.getPathSegments().size() == 1);   

  •         final int index = Integer.parseInt(uri.getPathSegments().get(0));   

  •         final MatrixCursor c = new MatrixCursor(   

  •                 new String[]{ Columns.ID, Columns.CITY, Columns.TEMPERATURE });   

  •         assert(0 <= index && index < sData.size());   

  •         final WeatherDataPoint data = sData.get(index);   

  •         data.degrees = values.getAsInteger(Columns.TEMPERATURE);   

  •          getContext().getContentResolver().notifyChange(uri, null);   

  •         return 1;   

  •     }   

  • }   

上面可以看到,对于插入和删除没有做过多的处理,对于天气更新给出了详细的解决方法。
有关 WeatherWidgetProvider.java 主要是appWidget的核心,为provider\

  1. class WeatherDataProviderObserver extends ContentObserver { //监控数据库的变化   
  2.     private AppWidgetManager mAppWidgetManager;   
  3.     private ComponentName mComponentName;   
  4.     WeatherDataProviderObserver(AppWidgetManager mgr, ComponentName cn, Handler h) {   
  5.         super(h);   
  6.         mAppWidgetManager = mgr;   
  7.         mComponentName = cn;   
  8.     }   
  9.     @Override  
  10.     public void onChange(boolean selfChange) {   
  11.            mAppWidgetManager.notifyAppWidgetViewDataChanged(   
  12.                 mAppWidgetManager.getAppWidgetIds(mComponentName), R.id.weather_list);   
  13.     }   
  14. }   
  15. public class WeatherWidgetProvider extends AppWidgetProvider {   
  16.     public static String CLICK_ACTION = "com.example.android.weatherlistwidget.CLICK";   
  17.     public static String REFRESH_ACTION = "com.example.android.weatherlistwidget.REFRESH";   
  18.     public static String EXTRA_CITY_ID = "com.example.android.weatherlistwidget.city";   
  19.     private static HandlerThread sWorkerThread;   
  20.     private static Handler sWorkerQueue;   
  21.     private static WeatherDataProviderObserver sDataObserver;   
  22.     public WeatherWidgetProvider() {   
  23.          sWorkerThread = new HandlerThread("WeatherWidgetProvider-worker"); //开一个线程,这里用到了HandlerThread   
  24.         sWorkerThread.start();   
  25.         sWorkerQueue = new Handler(sWorkerThread.getLooper()); //不了解Thread的Looper可以看下这个例子比较简单清晰   
  26.     }   
  27.     @Override  
  28.     public void onEnabled(Context context) { //当appWidget添加到桌面上时   
  29.            final ContentResolver r = context.getContentResolver();   
  30.         if (sDataObserver == null) {   
  31.             final AppWidgetManager mgr = AppWidgetManager.getInstance(context);   
  32.             final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);   
  33.             sDataObserver = new WeatherDataProviderObserver(mgr, cn, sWorkerQueue);   
  34.             r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver); //注册监控数据库变化的回调   
  35.         }   
  36.     }   
  37.     @Override  
  38.     public void onReceive(Context ctx, Intent intent) {   
  39.         final String action = intent.getAction();   
  40.         if (action.equals(REFRESH_ACTION)) { //接收数据库改变的回调广播   
  41.                final Context context = ctx;   
  42.             sWorkerQueue.removeMessages(0);   
  43.             sWorkerQueue.post(new Runnable() {   
  44.                 @Override  
  45.                 public void run() {   
  46.                     final ContentResolver r = context.getContentResolver();   
  47.                     final Cursor c = r.query(WeatherDataProvider.CONTENT_URI, nullnullnull,    
  48.                             null);   
  49.                     final int count = c.getCount();   
  50.                     final int maxDegrees = 96;   
  51.                     r.unregisterContentObserver(sDataObserver); //首先取消数据库监控   
  52.                     for (int i = 0; i < count; ++i) {   
  53.                         final Uri uri = ContentUris.withAppendedId(WeatherDataProvider.CONTENT_URI, i);   
  54.                         final ContentValues values = new ContentValues();   
  55.                         values.put(WeatherDataProvider.Columns.TEMPERATURE,   
  56.                                 new Random().nextInt(maxDegrees));   
  57.                         r.update(uri, values, nullnull); //更新数据库记录层   
  58.                     }   
  59.                     r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver); //重新设置监视数据库   
  60.                     final AppWidgetManager mgr = AppWidgetManager.getInstance(context);   
  61.                     final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);   
  62.                     mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn), R.id.weather_list); //提示Widget有数据更新并刷新UI   
  63.                 }   
  64.             });   
  65.         } else if (action.equals(CLICK_ACTION)) {   
  66.              final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,   
  67.                     AppWidgetManager.INVALID_APPWIDGET_ID);   
  68.             final String city = intent.getStringExtra(EXTRA_CITY_ID);   
  69.             final String formatStr = ctx.getResources().getString(R.string.toast_format_string);   
  70.             Toast.makeText(ctx, String.format(formatStr, city), Toast.LENGTH_SHORT).show();   
  71.         }   
  72.         super.onReceive(ctx, intent);   
  73.     }   
  74.     @Override  
  75.     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {   
  76.                  for (int i = 0; i < appWidgetIds.length; ++i) {   
  77.                       final Intent intent = new Intent(context, WeatherWidgetService.class);   //当桌面上有多个这个相同的appWidget需要分别处理   
  78.             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds);   
  79.             intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));   
  80.             final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);   
  81.             rv.setRemoteAdapter(appWidgetIds, R.id.weather_list, intent);   
  82.             rv.setEmptyView(R.id.weather_list, R.id.empty_view);   
  83.             final Intent onClickIntent = new Intent(context, WeatherWidgetProvider.class);   
  84.             onClickIntent.setAction(WeatherWidgetProvider.CLICK_ACTION);   
  85.             onClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds);   
  86.             onClickIntent.setData(Uri.parse(onClickIntent.toUri(Intent.URI_INTENT_SCHEME)));   
  87.             final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0,   
  88.                     onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);   
  89.             rv.setPendingIntentTemplate(R.id.weather_list, onClickPendingIntent);   
  90.             final Intent refreshIntent = new Intent(context, WeatherWidgetProvider.class);   
  91.             refreshIntent.setAction(WeatherWidgetProvider.REFRESH_ACTION);   
  92.             final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0,   
  93.                     refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);   
  94.             rv.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);   
  95.             appWidgetManager.updateAppWidget(appWidgetIds, rv);   
  96.         }   
  97.         super.onUpdate(context, appWidgetManager, appWidgetIds);   
  98.     }   
  99. }   

对于WeatherWidgetService.java这个Service集成于RemoteViewsService,主要是UI上的处理\

  1. public class WeatherWidgetService extends RemoteViewsService {   
  2.     @Override  
  3.     public RemoteViewsFactory onGetViewFactory(Intent intent) {   
  4.         return new StackRemoteViewsFactory(this.getApplicationContext(), intent);   
  5.     }   
  6. }   
  7. class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {   
  8.     private Context mContext;   
  9.     private Cursor mCursor;   
  10.     private int mAppWidgetId;   
  11.     public StackRemoteViewsFactory(Context context, Intent intent) {   
  12.         mContext = context;   
  13.         mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,   
  14.                 AppWidgetManager.INVALID_APPWIDGET_ID);   
  15.     }   
  16.     public void onCreate() {   
  17.     }   
  18.     public void onDestroy() {   
  19.         if (mCursor != null) {   
  20.             mCursor.close();   
  21.         }   
  22.     }   
  23.     public int getCount() {   
  24.         return mCursor.getCount();   
  25.     }   
  26.     public RemoteViews getViewAt(int position) {   
  27.         String city = "Unknown City";   
  28.         int temp = 0;   
  29.         if (mCursor.moveToPosition(position)) {   
  30.             final int cityColIndex = mCursor.getColumnIndex(WeatherDataProvider.Columns.CITY);   
  31.             final int tempColIndex = mCursor.getColumnIndex(   
  32.                     WeatherDataProvider.Columns.TEMPERATURE);   
  33.             city = mCursor.getString(cityColIndex);   
  34.             temp = mCursor.getInt(tempColIndex);   
  35.         }   
  36.         final String formatStr = mContext.getResources().getString(R.string.item_format_string);   
  37.         final int itemId = (position % 2 == 0 ? R.layout.light_widget_item   
  38.                 : R.layout.dark_widget_item);   
  39.         RemoteViews rv = new RemoteViews(mContext.getPackageName(), itemId);   
  40.         rv.setTextViewText(R.id.widget_item, String.format(formatStr, temp, city));   
  41.         final Intent fillInIntent = new Intent();   
  42.         final Bundle extras = new Bundle();   
  43.         extras.putString(WeatherWidgetProvider.EXTRA_CITY_ID, city);   
  44.         fillInIntent.putExtras(extras);   
  45.         rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);   
  46.         return rv;   
  47.     }   
  48.     public RemoteViews getLoadingView() {   
  49.         return null;   
  50.     }   
  51.     public int getViewTypeCount() {   
  52.         return 2;   
  53.     }   
  54.     public long getItemId(int position) {   
  55.         return position;   
  56.     }   
  57.     public boolean hasStableIds() {   
  58.         return true;   
  59.     }   
  60.     public void onDataSetChanged() {   
  61.         if (mCursor != null) {   
  62.             mCursor.close();   
  63.         }   
  64.         mCursor = mContext.getContentResolver().query(WeatherDataProvider.CONTENT_URI, nullnull,   
  65.                 nullnull);   
  66.     }   
  67. }