7.数据库、Contentobserver

群组页是程序内部维护的一个数据库,其中一张表groups,用于存放创建的群组,还有一张表thread_group,用于关联群组和系统短信数据库中的会话。

数据库应该这样设计

MySqliteHelper

  1. public class MySqliteHelper extends SQLiteOpenHelper{
  2. public MySqliteHelper(Context context, String name, int version) {
  3. super(context, name, null, version);
  4. }
  5. public static final String TABLE_GROUPS = "groups";
  6. public static final String TABLE_THREAD_GROUPS = "thread_groups";
  7. @Override
  8. public void onCreate(SQLiteDatabase db) {
  9. db.execSQL("create table groups(_id integer primary key autoincrement, group_name varchar(20));");
  10. db.execSQL("create table thread_groups(_id integer primary key autoincrement, group_id integer, thread_id integer);");
  11. }
  12. @Override
  13. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  14. // TODO Auto-generated method stub
  15. }
  16. }

DBUtils

  1. public class DBUtils {
  2. /**
  3. * 用于更新cursor 的URI
  4. */
  5. private static final Uri uri = Uri.parse("content://www.itheima.com");
  6. private Context ctx;
  7. private MySqliteHelper sqlHelper;
  8. private DBUtils(Context ctx){
  9. this.ctx = ctx;
  10. sqlHelper = new MySqliteHelper(ctx, "heima39", 1);
  11. }
  12. private static DBUtils instance;
  13. public static synchronized DBUtils getInstance(Context ctx){
  14. if(instance == null){
  15. instance = new DBUtils(ctx);
  16. }
  17. return instance;
  18. }
  19. /**
  20. * 创建新群组
  21. * @param name 群组的名称
  22. */
  23. public void createNewGroup(String name) {
  24. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  25. ContentValues values = new ContentValues();
  26. values.put("group_name", name);
  27. db.insert(MySqliteHelper.TABLE_GROUPS, "_id", values);
  28. notifyCursor();
  29. }
  30. /**
  31. * 查询所有的群组信息
  32. * @return
  33. */
  34. public Cursor getAllGroup() {
  35. SQLiteDatabase db = sqlHelper.getReadableDatabase();
  36. Cursor cursor = db.query(MySqliteHelper.TABLE_GROUPS, null, null, null, null, null, null);
  37. // android.database.sqlite.SQLiteCursor
  38. System.out.println(cursor);
  39. //为cursor 设置 通知提醒的URI
  40. cursor.setNotificationUri(ctx.getContentResolver(), uri);
  41. return cursor;
  42. }
  43. /**
  44. * 通知cursor 群组表中的内容已经发生变化
  45. * @param cursor
  46. */
  47. private void notifyCursor() {
  48. // 让内容处理者,根据URI 发出更新通知
  49. ctx.getContentResolver().notifyChange(uri, null);
  50. }
  51. /**
  52. * 通过群组ID删除群组
  53. * @param groupId
  54. */
  55. public void deleteGroupById(int groupId) {
  56. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  57. db.delete(MySqliteHelper.TABLE_GROUPS, " _id = "+groupId, null);
  58. notifyCursor();
  59. }
  60. /**
  61. * 更新群组名称
  62. * @param groupId 群组的ID
  63. * @param name 群组的新名
  64. */
  65. public void updateGroupById(int groupId, String name) {
  66. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  67. ContentValues values = new ContentValues();
  68. values.put("group_name", name);
  69. db.update(MySqliteHelper.TABLE_GROUPS, values, " _id = "+groupId , null);
  70. notifyCursor();
  71. }
  72. /**
  73. * 将会话ID和群组ID 插入到会话群组关系 表中
  74. * @param threadId
  75. * @param groupId
  76. */
  77. public void insertThradIdAndGroupId(int threadId, int groupId) {
  78. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  79. ContentValues values = new ContentValues();
  80. values.put("thread_id", threadId);
  81. values.put("group_id", groupId);
  82. db.insert(MySqliteHelper.TABLE_THREAD_GROUPS,null, values);
  83. }
  84. /**
  85. * 返回所有的指定群组ID的会话信息
  86. * @param groupId
  87. * @return
  88. */
  89. public Cursor getAllThreadIdByGroupId(int groupId) {
  90. SQLiteDatabase db = sqlHelper.getReadableDatabase();
  91. Cursor cursor = db.query(MySqliteHelper.TABLE_THREAD_GROUPS, null, " group_id = "+groupId, null, null, null, null);
  92. return cursor;
  93. }
  94. }

在activity中不需要做任何操作,当数据库发生变化list条目也变化了,前提必须是CursorAdapter

源码:

curosr 注册监听:

* cursor.setNotificationUri(ctx.getContentResolver(), uri);

* SQLiteCursor --> AbstractWindowedCursor  --> AbstractCursor

*  在AbstractCursor类中:

public void setNotificationUri(ContentResolver cr, Uri notifyUri) {

mNotifyUri = notifyUri;

mContentResolver = cr;

mSelfObserver = new SelfContentObserver(this);

// 将uri 和 mSelfObserver 在  mContentResolver 中注册

mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);

}

}

发出通知:

ctx.getContentResolver().notifyChange(uri, null);

* 在 ContentResolver中发出通知:

notifyChange(uri, observer, true /* sync to network */);

*结论: 根据uri 找到 监听此URI 的Contentobserver ,即  AbstractCursor 类中的 mSelfObserver 然后,执行,mSelfObserver 中 onChange方法

* 那么 cursor 中的内容观察者执行onChange方法 时,如何刷新页面:

* CursorAdapter  init方法中为cursor注册了二个监听:

c.registerContentObserver(mChangeObserver); // 内容观察者

c.registerDataSetObserver(mDataSetObserver); // 数据观察者

* 当Contentobserver 根据URI 发送更新通知时,执行cursor的 内容观察者即:

* mChangeObserver.onChange()方法 :

private class ChangeObserver extends ContentObserver {

public void onChange(boolean selfChange) {

onContentChanged();

}

}

* 在 onContentChanged方法 中 执行curosr.requery:

protected void onContentChanged() {

...

mDataValid = mCursor.requery();

}

* 此时,cursor ,实际对象是 SQLiteCursor ,就执行 SQLiteCursor 中的requery方法 ,

* 在SQLiteCursor 中的requery方法中:

* 重新查询数据:

* 执行数据观察者的notify方法 :mDataSetObservable.notifyChanged();

* 即执行观察者的onChanged方法: observer.onChanged();

* CursorAdapter中已经为cursor 注册了一个数据观察者: mDataSetObserver

* 当 mDataSetObserver 执行onChanged 方法时:

private class MyDataSetObserver extends DataSetObserver {

public void onChanged() {

mDataValid = true;

notifyDataSetChanged(); // 刷新了listView的数据

}


GroupUI

群组创建后,在会话页,长按某一个会话添加到群组中

  1. public class GroupUI extends ListActivity implements OnItemLongClickListener, OnItemClickListener{
  2. private ListView listView;
  3. private Context ctx;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. ctx = this;
  8. listView = getListView();
  9. adapter = new GropListAdapter(this, null);
  10. listView.setAdapter(adapter);
  11. listView.setOnItemLongClickListener(this);
  12. listView.setOnItemClickListener(this);
  13. prepareData();
  14. }
  15. private void prepareData() {
  16. DBUtils dbu = DBUtils.getInstance(ctx);
  17. Cursor cursor = dbu.getAllGroup();
  18. adapter.changeCursor(cursor);
  19. }
  20. private GropListAdapter adapter;
  21. class GropListAdapter extends CursorAdapter{
  22. public GropListAdapter(Context context, Cursor c) {
  23. super(context, c);
  24. }
  25. @Override
  26. public View newView(Context context, Cursor cursor, ViewGroup parent) {
  27. View view = View.inflate(context, R.layout.list_item_group, null);
  28. TextView name = (TextView) view.findViewById(R.id.tv_name_group);
  29. view.setTag(name);
  30. return view;
  31. }
  32. @Override
  33. public void bindView(View view, Context context, Cursor cursor) {
  34. TextView name = (TextView) view.getTag();
  35. name.setText(cursor.getString(cursor.getColumnIndex("group_name")));
  36. }
  37. }
  38. @Override
  39. /**
  40. * 创建菜单
  41. */
  42. public boolean onCreateOptionsMenu(Menu menu) {
  43. // 将 资料ID对应的文件转换为 菜单条目 ,并添加至 menu 中
  44. getMenuInflater().inflate(R.menu.activity_group, menu);
  45. return true;
  46. }
  47. @Override
  48. /**
  49. * 响应菜单的点击事件
  50. */
  51. public boolean onOptionsItemSelected(MenuItem item) {
  52. switch (item.getItemId()) {
  53. case R.id.menu_new_group:
  54. showCreateGroupDialog();
  55. break;
  56. }
  57. return true;
  58. }
  59. private AlertDialog dialog;
  60. /**
  61. * 显示新建群组对话框
  62. */
  63. private void showCreateGroupDialog() {
  64. AlertDialog.Builder adb =new AlertDialog.Builder(ctx);
  65. dialog = adb.create();
  66. View view = View.inflate(ctx, R.layout.dialog_new_group, null);
  67. final EditText etInputName = (EditText) view.findViewById(R.id.et_input_new_group);
  68. Button btnOk = (Button) view.findViewById(R.id.btn_ok);
  69. btnOk.setOnClickListener(new OnClickListener() {
  70. @Override
  71. public void onClick(View v) {
  72. String name = etInputName.getText().toString();
  73. if(TextUtils.isEmpty(name)){
  74. Toast.makeText(ctx, "请输入群组名称", 0).show();
  75. return ;
  76. }
  77. // 将群组名称保存至数据库
  78. DBUtils dbu = DBUtils.getInstance(ctx);
  79. dbu.createNewGroup(name);
  80. dialog.dismiss();
  81. }
  82. });
  83. dialog.setView(view,0,0,0,0);
  84. dialog.show();
  85. }
  86. @Override
  87. /**
  88. * 响应listview条目的长按事件
  89. */
  90. public boolean onItemLongClick(AdapterView<?> parent, View view,
  91. int position, long id) {
  92. showEditGroupDialog(position);
  93. return true;
  94. }
  95. /**
  96. * 显示编辑群组对话框
  97. * @param position
  98. */
  99. private void showEditGroupDialog(final int position) {
  100. AlertDialog.Builder adb = new AlertDialog.Builder(ctx);
  101. String items[]=new String[]{"编辑","删除"};
  102. adb.setItems(items, new DialogInterface.OnClickListener() {
  103. @Override
  104. /**
  105. * which 是点击的条目的位置
  106. */
  107. public void onClick(DialogInterface dialog, int which) {
  108. if(which == 0){ // “编辑”
  109. showUpdateGroupDialog(position);
  110. }else{ // 删除
  111. showConfirmDeleteDialog(position);
  112. }
  113. }
  114. });
  115. adb.show();
  116. }
  117. /**
  118. * 显示确认删除的对话框
  119. * @param position
  120. */
  121. protected void showConfirmDeleteDialog(final int position) {
  122. AlertDialog.Builder adb = new AlertDialog.Builder(ctx);
  123. adb.setTitle("删除群组");
  124. adb.setMessage("确定要删除这个群吗?");
  125. adb.setNegativeButton("确定", new DialogInterface.OnClickListener() {
  126. @Override
  127. public void onClick(DialogInterface dialog, int which) {
  128. // 删除对应的群组
  129. DBUtils dbu = DBUtils.getInstance(ctx);
  130. Cursor cursor = adapter.getCursor();
  131. cursor.moveToPosition(position);
  132. int groupId = cursor.getInt(0); // 获得第0列的值,即,_id 这一列的值
  133. dbu.deleteGroupById(groupId);
  134. dialog.dismiss();
  135. }
  136. });
  137. adb.setPositiveButton("取消", null);
  138. adb.show();
  139. }
  140. /**
  141. * 显示更新群组的对话框
  142. * @param position
  143. */
  144. protected void showUpdateGroupDialog(final int position) {
  145. AlertDialog.Builder adb =new AlertDialog.Builder(ctx);
  146. dialog = adb.create();
  147. View view = View.inflate(ctx, R.layout.dialog_new_group, null);
  148. TextView title = (TextView) view.findViewById(R.id.tv_title_dialog);
  149. title.setText("更新群组名称");
  150. final EditText etInputName = (EditText) view.findViewById(R.id.et_input_new_group);
  151. Button btnOk = (Button) view.findViewById(R.id.btn_ok);
  152. btnOk.setOnClickListener(new OnClickListener() {
  153. @Override
  154. public void onClick(View v) {
  155. String name = etInputName.getText().toString();
  156. if(TextUtils.isEmpty(name)){
  157. Toast.makeText(ctx, "请输入群组名称", 0).show();
  158. return ;
  159. }
  160. // 将群组名称保存至数据库
  161. DBUtils dbu = DBUtils.getInstance(ctx);
  162. Cursor cursor = adapter.getCursor();
  163. cursor.moveToPosition(position);
  164. int groupId = cursor.getInt(0); // 获得群组ID
  165. dbu.updateGroupById(groupId,name);
  166. dialog.dismiss();
  167. }
  168. });
  169. dialog.setView(view,0,0,0,0);
  170. dialog.show();
  171. }
  172. @Override
  173. /**
  174. * 响应listview条目点击事件
  175. */
  176. public void onItemClick(AdapterView<?> parent, View view, int position,
  177. long id) {
  178. Cursor cursor = adapter.getCursor();
  179. cursor.moveToPosition(position);
  180. int groupId = cursor.getInt(0); // 返回群组ID
  181. DBUtils dbu =DBUtils.getInstance(ctx);
  182. Cursor cursor2 = dbu.getAllThreadIdByGroupId(groupId);
  183. if(cursor2.getCount() == 0){ // 该群组没有人
  184. Toast.makeText(ctx, "该群还没有人,快回点人气吧", 0).show();
  185. }else{
  186. // 如果有人的话,希望能过cursor2 拼凑出如: thread_id in (1,2);
  187. String subSql = convertCursor2Str(cursor2);
  188. System.out.println("subSql"+subSql);
  189. Intent intent = new Intent(this,ConversationUI.class);
  190. intent.putExtra("subSql", subSql);
  191. startActivity(intent);
  192. }
  193. }
  194. /**
  195. * 根据cursor中的内容,拼凑出 如 thread_id in (1,2); 的字符串
  196. * @param cursor2
  197. * @return
  198. */
  199. private String convertCursor2Str(Cursor cursor) {
  200. cursor.moveToPosition(-1);
  201. StringBuilder sb=new StringBuilder(" thread_id in ( ");
  202. while(cursor.moveToNext()){
  203. String threadId = cursor.getString(cursor.getColumnIndex("thread_id"));
  204. sb.append(threadId+","); // thread_id in (1,
  205. }
  206. // thread_id in (1,2,3,4,
  207. sb.replace(sb.lastIndexOf(","), sb.length(), ")");
  208. return sb.toString();
  209. }
  210. }

来自为知笔记(Wiz)

时间: 2024-12-14 06:49:09

7.数据库、Contentobserver的相关文章

ContentObserver实现数据库的监听

工作中有时需要开启一个线程做大量的查询,来检测某个数据库值发送了变化,导致的开销很大,后来利用了ContentObserver完美的解决了该问题,这里做下总结. (1)ContentObserver--内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库中的触发器,当所察的Uri发生变化时,便会触发它.它的主要方法有: A,public final void registerContentObserver(Uri uri, boolean notify

Android使用ContentObserver监听数据库变化(转自:http://www.blogjava.net/zhaojianhua/archive/2011/10/27/362204.html)

android 使用contentobserver监听数据库内容变化 android 使用contentobserver监听数据库内容变化 在 android中经常会用到改变数据库内容后再去使用数据库更新的内容,很多人会重新去query一遍,但是这样的问题就是程序会特别占内存,而且有可能 会搂关cursor而导致程序内存未释放等等.其实android内部提供了一种ContentObserver的东西来监听数据库内容的变化.ContentObserver 的构造函数需要一个参数Hanlder,因为

ContentObserver监听数据库&#183;变化

//短信Uri Uri smsUri = Uri.parse("content://sms"); //使用ContentReslover注册·监听器 getContentResolver().registerContentObserver(smsUri, true, new MySmsListener(mHandler, SmsObserverService.this)); //继承ContentObserver监听 class MySmsListener extends Conten

ContentObserver的使用

ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于 数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它.触发器分为表触发器.行触发器, 相应地ContentObserver也分为“表“ContentObserver.“行”ContentObserver,当然这是与它所监听的Uri MIME Type有关的. 熟悉Content Provider(内容提供者)的应该知

Android ContentObserver详解

前言: 工作中,需要开启一个线程大量的查询某个数据库值发送了变化,导致的开销很大,后来在老大的指点下,利用了ContentObserver完美的解决了该问题,感到很兴奋,做完之后自己也对ContentObserver做下总结. ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它.触发器分为表触发器.行触发器,相应地C

Android ContentProvider、ContentResolver和ContentObserver的使用

1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之一,可见它在Android中的作用非同小可.它主要的作用是:实现各个应用程序之间的(跨应用)数据共享,比如联系人应用中就使用了ContentProvider,你在自己的应用中可以读取和修改联系人的数据,不过需要获得相应的权限.其实它也只是一个中间人,真正的数据源是文件或者SQLite等. 一个应用实现ContentProvider来提供内容

ContentObserver的使用完整详细示例

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

无废话Android之内容观察者ContentObserver、获取和保存系统的联系人信息、网络图片查看器、网络html查看器、使用异步框架Android-Async-Http(4)

1.内容观察者ContentObserver 如果ContentProvider的访问者需要知道ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下: private static final Uri URI = Uri.parse("content://person.db"); public class

基于ContentObserver来动态取消或添加屏幕超时任务

前面也说了,ContentObserver可以来监控数据库里某一项数据的变化,当然也可以同时监控多个数据项的变化.笔者在项目中需要修改到屏幕超时的需求,比如在车载业务中,倒车事件发生的时候,是不需要屏幕超时变黑的,相当于这个计时timer要Reset一下,同样在蓝牙电话也要Reset一下,最好就是在这种特殊任务的时候,这个屏幕超时计时任务就不要跑起来,这样是最好的,那怎么实现呢? 笔者通过研究phonewindowsmanger.cpp中发现,最终都是驱动一个mScreenLockTimeout