/frameworks/base/core/java/android/content/ContentQueryMap.java

https://gitlab.com/brian0218/rk3188_r-box_android4.2.2_sdk · Java · 182 lines · 101 code · 18 blank · 63 comment · 26 complexity · bfec21ddb3c809fb6b38bb8b3770e6d1 MD5 · raw file

  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package android.content;
  17. import android.database.ContentObserver;
  18. import android.database.Cursor;
  19. import android.os.Handler;
  20. import java.util.HashMap;
  21. import java.util.Map;
  22. import java.util.Observable;
  23. /**
  24. * Caches the contents of a cursor into a Map of String->ContentValues and optionally
  25. * keeps the cache fresh by registering for updates on the content backing the cursor. The column of
  26. * the database that is to be used as the key of the map is user-configurable, and the
  27. * ContentValues contains all columns other than the one that is designated the key.
  28. * <p>
  29. * The cursor data is accessed by row key and column name via getValue().
  30. */
  31. public class ContentQueryMap extends Observable {
  32. private volatile Cursor mCursor;
  33. private String[] mColumnNames;
  34. private int mKeyColumn;
  35. private Handler mHandlerForUpdateNotifications = null;
  36. private boolean mKeepUpdated = false;
  37. private Map<String, ContentValues> mValues = null;
  38. private ContentObserver mContentObserver;
  39. /** Set when a cursor change notification is received and is cleared on a call to requery(). */
  40. private boolean mDirty = false;
  41. /**
  42. * Creates a ContentQueryMap that caches the content backing the cursor
  43. *
  44. * @param cursor the cursor whose contents should be cached
  45. * @param columnNameOfKey the column that is to be used as the key of the values map
  46. * @param keepUpdated true if the cursor's ContentProvider should be monitored for changes and
  47. * the map updated when changes do occur
  48. * @param handlerForUpdateNotifications the Handler that should be used to receive
  49. * notifications of changes (if requested). Normally you pass null here, but if
  50. * you know that the thread that is creating this isn't a thread that can receive
  51. * messages then you can create your own handler and use that here.
  52. */
  53. public ContentQueryMap(Cursor cursor, String columnNameOfKey, boolean keepUpdated,
  54. Handler handlerForUpdateNotifications) {
  55. mCursor = cursor;
  56. mColumnNames = mCursor.getColumnNames();
  57. mKeyColumn = mCursor.getColumnIndexOrThrow(columnNameOfKey);
  58. mHandlerForUpdateNotifications = handlerForUpdateNotifications;
  59. setKeepUpdated(keepUpdated);
  60. // If we aren't keeping the cache updated with the current state of the cursor's
  61. // ContentProvider then read it once into the cache. Otherwise the cache will be filled
  62. // automatically.
  63. if (!keepUpdated) {
  64. readCursorIntoCache(cursor);
  65. }
  66. }
  67. /**
  68. * Change whether or not the ContentQueryMap will register with the cursor's ContentProvider
  69. * for change notifications. If you use a ContentQueryMap in an activity you should call this
  70. * with false in onPause(), which means you need to call it with true in onResume()
  71. * if want it to be kept updated.
  72. * @param keepUpdated if true the ContentQueryMap should be registered with the cursor's
  73. * ContentProvider, false otherwise
  74. */
  75. public void setKeepUpdated(boolean keepUpdated) {
  76. if (keepUpdated == mKeepUpdated) return;
  77. mKeepUpdated = keepUpdated;
  78. if (!mKeepUpdated) {
  79. mCursor.unregisterContentObserver(mContentObserver);
  80. mContentObserver = null;
  81. } else {
  82. if (mHandlerForUpdateNotifications == null) {
  83. mHandlerForUpdateNotifications = new Handler();
  84. }
  85. if (mContentObserver == null) {
  86. mContentObserver = new ContentObserver(mHandlerForUpdateNotifications) {
  87. @Override
  88. public void onChange(boolean selfChange) {
  89. // If anyone is listening, we need to do this now to broadcast
  90. // to the observers. Otherwise, we'll just set mDirty and
  91. // let it query lazily when they ask for the values.
  92. if (countObservers() != 0) {
  93. requery();
  94. } else {
  95. mDirty = true;
  96. }
  97. }
  98. };
  99. }
  100. mCursor.registerContentObserver(mContentObserver);
  101. // mark dirty, since it is possible the cursor's backing data had changed before we
  102. // registered for changes
  103. mDirty = true;
  104. }
  105. }
  106. /**
  107. * Access the ContentValues for the row specified by rowName
  108. * @param rowName which row to read
  109. * @return the ContentValues for the row, or null if the row wasn't present in the cursor
  110. */
  111. public synchronized ContentValues getValues(String rowName) {
  112. if (mDirty) requery();
  113. return mValues.get(rowName);
  114. }
  115. /** Requeries the cursor and reads the contents into the cache */
  116. public void requery() {
  117. final Cursor cursor = mCursor;
  118. if (cursor == null) {
  119. // If mCursor is null then it means there was a requery() in flight
  120. // while another thread called close(), which nulls out mCursor.
  121. // If this happens ignore the requery() since we are closed anyways.
  122. return;
  123. }
  124. mDirty = false;
  125. if (!cursor.requery()) {
  126. // again, don't do anything if the cursor is already closed
  127. return;
  128. }
  129. readCursorIntoCache(cursor);
  130. setChanged();
  131. notifyObservers();
  132. }
  133. private synchronized void readCursorIntoCache(Cursor cursor) {
  134. // Make a new map so old values returned by getRows() are undisturbed.
  135. int capacity = mValues != null ? mValues.size() : 0;
  136. mValues = new HashMap<String, ContentValues>(capacity);
  137. while (cursor.moveToNext()) {
  138. ContentValues values = new ContentValues();
  139. for (int i = 0; i < mColumnNames.length; i++) {
  140. if (i != mKeyColumn) {
  141. values.put(mColumnNames[i], cursor.getString(i));
  142. }
  143. }
  144. mValues.put(cursor.getString(mKeyColumn), values);
  145. }
  146. }
  147. public synchronized Map<String, ContentValues> getRows() {
  148. if (mDirty) requery();
  149. return mValues;
  150. }
  151. public synchronized void close() {
  152. if (mContentObserver != null) {
  153. mCursor.unregisterContentObserver(mContentObserver);
  154. mContentObserver = null;
  155. }
  156. mCursor.close();
  157. mCursor = null;
  158. }
  159. @Override
  160. protected void finalize() throws Throwable {
  161. if (mCursor != null) close();
  162. super.finalize();
  163. }
  164. }