зеркало из https://github.com/mozilla/gecko-dev.git
Bug 760956 - Add SimpleCancellableCursorLoader; add warning to SimpleCursorLoader. r=margaret
This bug was originally suspected to be caused by the lack of cancellation in SimpleCursorLoader but as comments 35-37 discovered, the Android framework has an implementation with canceling and without. As such, this bug is not necessarily caused by the lack of canceling. However, solution was pretty close to being complete so I figured we should land it and see if it helps the crash at all. --HG-- extra : commitid : AbwaGhDufxC extra : rebase_source : 00fc3830aadc1c8894cc9dd6750630dfc0dcb211
This commit is contained in:
Родитель
8d2594e7e7
Коммит
fa5b6a3b29
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* This is an adapted version of Android's original CursorLoader
|
||||
* with the ContentProvider-specific bits pushed outside of this class.
|
||||
*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
import android.support.v4.os.CancellationSignal;
|
||||
import android.support.v4.os.OperationCanceledException;
|
||||
|
||||
/**
|
||||
* An implementation of the framework's CursorLoader class that pushes the
|
||||
* ContentResolver access outside of the this class. It occurs in an abstract method
|
||||
* {@link #loadCursor(CancellationSignal)}. We do this because the legacy code uses
|
||||
* and wraps Cursors directly (e.g. {@link org.mozilla.gecko.db.TopSitesCursorWrapper},
|
||||
* making it difficult and more work to do the ContentResolver work in here.
|
||||
*
|
||||
* This implementation was created because it may fix the crash from bug 760956 but the
|
||||
* framework implementation ({@link android.content.CursorLoader}) is preferred.
|
||||
*/
|
||||
abstract class SimpleCancellableCursorLoader extends AsyncTaskLoader<Cursor> {
|
||||
final ForceLoadContentObserver mObserver;
|
||||
|
||||
Cursor mCursor;
|
||||
CancellationSignal mCancellationSignal;
|
||||
|
||||
public SimpleCancellableCursorLoader(Context context) {
|
||||
super(context);
|
||||
mObserver = new ForceLoadContentObserver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the target cursor for this loader. This method is called
|
||||
* on a worker thread.
|
||||
*/
|
||||
protected abstract Cursor loadCursor(CancellationSignal cancellationSignal);
|
||||
|
||||
/* Runs on a worker thread */
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
synchronized (this) {
|
||||
if (isLoadInBackgroundCanceled()) {
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
mCancellationSignal = new CancellationSignal();
|
||||
}
|
||||
try {
|
||||
Cursor cursor = loadCursor(mCancellationSignal);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
// Ensure the cursor window is filled
|
||||
cursor.getCount();
|
||||
cursor.registerContentObserver(mObserver);
|
||||
} catch (RuntimeException ex) {
|
||||
cursor.close();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
return cursor;
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
mCancellationSignal = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelLoadInBackground() {
|
||||
super.cancelLoadInBackground();
|
||||
|
||||
synchronized (this) {
|
||||
if (mCancellationSignal != null) {
|
||||
mCancellationSignal.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Runs on the UI thread */
|
||||
@Override
|
||||
public void deliverResult(Cursor cursor) {
|
||||
if (isReset()) {
|
||||
// An async query came in while the loader is stopped
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Cursor oldCursor = mCursor;
|
||||
mCursor = cursor;
|
||||
|
||||
if (isStarted()) {
|
||||
super.deliverResult(cursor);
|
||||
}
|
||||
|
||||
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
|
||||
oldCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous load of the list data. When the result is ready the callbacks
|
||||
* will be called on the UI thread. If a previous load has been completed and is still valid
|
||||
* the result may be passed to the callbacks immediately.
|
||||
*
|
||||
* Must be called from the UI thread
|
||||
*/
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
if (mCursor != null) {
|
||||
deliverResult(mCursor);
|
||||
}
|
||||
|
||||
if (takeContentChanged() || mCursor == null) {
|
||||
forceLoad();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called from the UI thread
|
||||
*/
|
||||
@Override
|
||||
protected void onStopLoading() {
|
||||
// Attempt to cancel the current load task if possible.
|
||||
cancelLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(Cursor cursor) {
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
super.onReset();
|
||||
|
||||
// Ensure the loader is stopped
|
||||
onStopLoading();
|
||||
|
||||
if (mCursor != null && !mCursor.isClosed()) {
|
||||
mCursor.close();
|
||||
}
|
||||
|
||||
mCursor = null;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,10 @@ import android.content.Context;
|
|||
import android.database.Cursor;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
|
||||
/**
|
||||
* This implmentation may be causing the crash in bug 760956 – use at your own risk.
|
||||
* Prefer the framework implementation ({@link android.content.CursorLoader}) or {@link SimpleCancellableCursorLoader}.
|
||||
*/
|
||||
abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
|
||||
final ForceLoadContentObserver mObserver;
|
||||
Cursor mCursor;
|
||||
|
|
|
@ -406,6 +406,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
|||
'home/SearchEngineBar.java',
|
||||
'home/SearchEngineRow.java',
|
||||
'home/SearchLoader.java',
|
||||
'home/SimpleCancellableCursorLoader.java',
|
||||
'home/SimpleCursorLoader.java',
|
||||
'home/SpacingDecoration.java',
|
||||
'home/TabMenuStrip.java',
|
||||
|
|
Загрузка…
Ссылка в новой задаче