ThreadedCursorAdapter.java revision 3ad8d5cf0911ba015e019bc75e8fb13c091efe0e
1f94abcf44fc1611f76e55461f48220e621fc31b7John Reck/* 2f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * Copyright (C) 2011 The Android Open Source Project 3f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * 4f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * Licensed under the Apache License, Version 2.0 (the "License"); 5f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * you may not use this file except in compliance with the License. 6f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * You may obtain a copy of the License at 7f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * 8f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * http://www.apache.org/licenses/LICENSE-2.0 9f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * 10f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * Unless required by applicable law or agreed to in writing, software 11f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * distributed under the License is distributed on an "AS IS" BASIS, 12f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * See the License for the specific language governing permissions and 14f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * limitations under the License. 15f94abcf44fc1611f76e55461f48220e621fc31b7John Reck */ 16f94abcf44fc1611f76e55461f48220e621fc31b7John Reckpackage com.android.browser.util; 17f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 18f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.content.Context; 19f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.database.Cursor; 20f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.Handler; 21f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.HandlerThread; 22f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.Message; 23f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.Process; 243ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reckimport android.os.SystemProperties; 25f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.util.Log; 26f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.view.View; 27f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.view.ViewGroup; 28f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.widget.Adapter; 29f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.widget.BaseAdapter; 30f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.widget.CursorAdapter; 31f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 32f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport com.android.browser.R; 33f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 34f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport java.lang.ref.WeakReference; 35f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 36f94abcf44fc1611f76e55461f48220e621fc31b7John Reckpublic abstract class ThreadedCursorAdapter<T> extends BaseAdapter { 37f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 383ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck private static final String LOGTAG = "BookmarksThreadedAdapter"; 39f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private static final boolean DEBUG = false; 403ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck private static boolean sEnableBitmapRecycling = true; 41f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 42f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private Context mContext; 43f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private Object mCursorLock = new Object(); 44f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private CursorAdapter mCursorAdapter; 45f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private T mLoadingObject; 46f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private Handler mLoadHandler; 47f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private Handler mHandler; 48f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private int mSize; 493ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck private boolean mHasCursor; 503ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck private long mGeneration; 513ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck 523ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck static { 533ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck // TODO: Remove this once recycling is either stabilized or scrapped 543ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck sEnableBitmapRecycling = SystemProperties 553ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck .getBoolean("com.android.browser.recycling", sEnableBitmapRecycling); 563ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck Log.d(LOGTAG, "Bitmap recycling enabled: " + sEnableBitmapRecycling); 573ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck } 58f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 59f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private class LoadContainer { 60f94abcf44fc1611f76e55461f48220e621fc31b7John Reck WeakReference<View> view; 61f94abcf44fc1611f76e55461f48220e621fc31b7John Reck int position; 62f94abcf44fc1611f76e55461f48220e621fc31b7John Reck T bind_object; 63f94abcf44fc1611f76e55461f48220e621fc31b7John Reck Adapter owner; 64f94abcf44fc1611f76e55461f48220e621fc31b7John Reck boolean loaded; 653ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck long generation; 66f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 67f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 68f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public ThreadedCursorAdapter(Context context, Cursor c) { 69f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mContext = context; 703ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck mHasCursor = (c != null); 71f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mCursorAdapter = new CursorAdapter(context, c, 0) { 72f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 73f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 74f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public View newView(Context context, Cursor cursor, ViewGroup parent) { 75f94abcf44fc1611f76e55461f48220e621fc31b7John Reck throw new IllegalStateException("not supported"); 76f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 77f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 78f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 79f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public void bindView(View view, Context context, Cursor cursor) { 80f94abcf44fc1611f76e55461f48220e621fc31b7John Reck throw new IllegalStateException("not supported"); 81f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 82f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 83f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 84f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public void notifyDataSetChanged() { 85f94abcf44fc1611f76e55461f48220e621fc31b7John Reck super.notifyDataSetChanged(); 86f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mSize = getCount(); 873ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck mGeneration++; 88f94abcf44fc1611f76e55461f48220e621fc31b7John Reck ThreadedCursorAdapter.this.notifyDataSetChanged(); 89f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 90f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 91f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 92f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public void notifyDataSetInvalidated() { 93f94abcf44fc1611f76e55461f48220e621fc31b7John Reck super.notifyDataSetInvalidated(); 94f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mSize = getCount(); 953ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck mGeneration++; 96f94abcf44fc1611f76e55461f48220e621fc31b7John Reck ThreadedCursorAdapter.this.notifyDataSetInvalidated(); 97f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 98f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 99f94abcf44fc1611f76e55461f48220e621fc31b7John Reck }; 100f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mSize = mCursorAdapter.getCount(); 101f94abcf44fc1611f76e55461f48220e621fc31b7John Reck HandlerThread thread = new HandlerThread("threaded_adapter_" + this, 102f94abcf44fc1611f76e55461f48220e621fc31b7John Reck Process.THREAD_PRIORITY_BACKGROUND); 103f94abcf44fc1611f76e55461f48220e621fc31b7John Reck thread.start(); 104f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mLoadHandler = new Handler(thread.getLooper()) { 105f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @SuppressWarnings("unchecked") 106f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 107f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public void handleMessage(Message msg) { 108f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (DEBUG) { 109f94abcf44fc1611f76e55461f48220e621fc31b7John Reck Log.d(LOGTAG, "loading: " + msg.what); 110f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 111f94abcf44fc1611f76e55461f48220e621fc31b7John Reck loadRowObject(msg.what, (LoadContainer) msg.obj); 112f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 113f94abcf44fc1611f76e55461f48220e621fc31b7John Reck }; 114f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mHandler = new Handler() { 115f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 116f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public void handleMessage(Message msg) { 117f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @SuppressWarnings("unchecked") 118f94abcf44fc1611f76e55461f48220e621fc31b7John Reck LoadContainer container = (LoadContainer) msg.obj; 119f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (container == null) { 120f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return; 121f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 122f94abcf44fc1611f76e55461f48220e621fc31b7John Reck View view = container.view.get(); 123f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (view == null 124f94abcf44fc1611f76e55461f48220e621fc31b7John Reck || container.owner != ThreadedCursorAdapter.this 1253ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck || container.position != msg.what 1263ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck || view.getWindowToken() == null 1273ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck || container.generation != mGeneration) { 128f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return; 129f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 130f94abcf44fc1611f76e55461f48220e621fc31b7John Reck container.loaded = true; 131f94abcf44fc1611f76e55461f48220e621fc31b7John Reck bindView(view, container.bind_object); 132f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 133f94abcf44fc1611f76e55461f48220e621fc31b7John Reck }; 134f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 135f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 136f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 137f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public int getCount() { 138f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return mSize; 139f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 140f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 141f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 142f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public Cursor getItem(int position) { 143f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return (Cursor) mCursorAdapter.getItem(position); 144f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 145f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 146f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 147f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public long getItemId(int position) { 148f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return position; 149f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 150f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 151f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private void loadRowObject(int position, LoadContainer container) { 152f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (container == null 153f94abcf44fc1611f76e55461f48220e621fc31b7John Reck || container.position != position 154f94abcf44fc1611f76e55461f48220e621fc31b7John Reck || container.owner != ThreadedCursorAdapter.this 155f94abcf44fc1611f76e55461f48220e621fc31b7John Reck || container.view.get() == null) { 156f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return; 157f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 158f94abcf44fc1611f76e55461f48220e621fc31b7John Reck synchronized (mCursorLock) { 159f94abcf44fc1611f76e55461f48220e621fc31b7John Reck Cursor c = (Cursor) mCursorAdapter.getItem(position); 1603ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck if (c == null || c.isClosed()) { 1613ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck return; 1623ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck } 1633ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck final T recycleObject = sEnableBitmapRecycling 1643ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck ? container.bind_object : null; 1653ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck container.bind_object = getRowObject(c, recycleObject); 166f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 167f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mHandler.obtainMessage(position, container).sendToTarget(); 168f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 169f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 170f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @Override 171f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public View getView(int position, View convertView, ViewGroup parent) { 172f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (convertView == null) { 173f94abcf44fc1611f76e55461f48220e621fc31b7John Reck convertView = newView(mContext, parent); 174f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 175f94abcf44fc1611f76e55461f48220e621fc31b7John Reck @SuppressWarnings("unchecked") 176f94abcf44fc1611f76e55461f48220e621fc31b7John Reck LoadContainer container = (LoadContainer) convertView.getTag(R.id.load_object); 177f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (container == null) { 178f94abcf44fc1611f76e55461f48220e621fc31b7John Reck container = new LoadContainer(); 179f94abcf44fc1611f76e55461f48220e621fc31b7John Reck container.view = new WeakReference<View>(convertView); 180f94abcf44fc1611f76e55461f48220e621fc31b7John Reck convertView.setTag(R.id.load_object, container); 181f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 182f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (container.position == position 183f94abcf44fc1611f76e55461f48220e621fc31b7John Reck && container.owner == this 1843ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck && container.loaded 1853ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck && container.generation == mGeneration) { 186f94abcf44fc1611f76e55461f48220e621fc31b7John Reck bindView(convertView, container.bind_object); 187f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } else { 188f94abcf44fc1611f76e55461f48220e621fc31b7John Reck bindView(convertView, cachedLoadObject()); 1893ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck if (mHasCursor) { 1903ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck container.position = position; 1913ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck container.loaded = false; 1923ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck container.owner = this; 1933ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck container.generation = mGeneration; 1943ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck mLoadHandler.obtainMessage(position, container).sendToTarget(); 1953ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck } 196f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 197f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return convertView; 198f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 199f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 200f94abcf44fc1611f76e55461f48220e621fc31b7John Reck private T cachedLoadObject() { 201f94abcf44fc1611f76e55461f48220e621fc31b7John Reck if (mLoadingObject == null) { 202f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mLoadingObject = getLoadingObject(); 203f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 204f94abcf44fc1611f76e55461f48220e621fc31b7John Reck return mLoadingObject; 205f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 206f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 207f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public void changeCursor(Cursor cursor) { 2083ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck mLoadHandler.removeCallbacksAndMessages(null); 2093ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck mHandler.removeCallbacksAndMessages(null); 210f94abcf44fc1611f76e55461f48220e621fc31b7John Reck synchronized (mCursorLock) { 2113ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck mHasCursor = (cursor != null); 212f94abcf44fc1611f76e55461f48220e621fc31b7John Reck mCursorAdapter.changeCursor(cursor); 213f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 214f94abcf44fc1611f76e55461f48220e621fc31b7John Reck } 215f94abcf44fc1611f76e55461f48220e621fc31b7John Reck 216f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public abstract View newView(Context context, ViewGroup parent); 217f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public abstract void bindView(View view, T object); 218f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public abstract T getRowObject(Cursor c, T recycleObject); 219f94abcf44fc1611f76e55461f48220e621fc31b7John Reck public abstract T getLoadingObject(); 220f94abcf44fc1611f76e55461f48220e621fc31b7John Reck}