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}