Isolate calls to each remote DocumentsProvider.
All background work is going through AsyncTasks, which uses a shared thread pool. Even with the new ContentProviderClient logic to detect ANRs, the UI can still appear to be unresponsive for 20 seconds, even if the user attempted to switch to a different backend. In the worst case, a backlog of thumbnail requests would end up wedging Loaders for a long time, since they all share the same THREAD_POOL_EXECUTOR. This change isolates calls to each provider onto their own thread, which they're free to wedge and recover from over time. It also means we no longer need a dedicated thread pool for recents loading, and can use a simpler Semaphore instead. Disables thumbnails in recents on svelte devices. Bug: 10993301, 11014856 Change-Id: I7f8a5bbb5f64437e006cb2c48b7e854136d5c38c
This commit is contained in:
@@ -26,6 +26,7 @@ import android.util.TimeUtils;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Abstract Loader that provides an {@link AsyncTask} to do the work. See
|
||||
@@ -123,6 +124,8 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
||||
}
|
||||
}
|
||||
|
||||
private final Executor mExecutor;
|
||||
|
||||
volatile LoadTask mTask;
|
||||
volatile LoadTask mCancellingTask;
|
||||
|
||||
@@ -131,7 +134,13 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
||||
Handler mHandler;
|
||||
|
||||
public AsyncTaskLoader(Context context) {
|
||||
this(context, AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public AsyncTaskLoader(Context context, Executor executor) {
|
||||
super(context);
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,7 +232,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
||||
}
|
||||
}
|
||||
if (DEBUG) Slog.v(TAG, "Executing: " + mTask);
|
||||
mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
||||
mTask.executeOnExecutor(mExecutor, (Void[]) null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,12 @@ public class CreateDirectoryFragment extends DialogFragment {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
final String displayName = text1.getText().toString();
|
||||
new CreateDirectoryTask(displayName).execute();
|
||||
|
||||
final DocumentsActivity activity = (DocumentsActivity) getActivity();
|
||||
final DocumentInfo cwd = activity.getCurrentDirectory();
|
||||
|
||||
new CreateDirectoryTask(displayName).executeOnExecutor(
|
||||
ProviderExecutor.forAuthority(cwd.authority));
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
|
||||
@@ -27,6 +27,7 @@ import static com.android.documentsui.model.DocumentInfo.getCursorInt;
|
||||
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
|
||||
import static com.android.documentsui.model.DocumentInfo.getCursorString;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
@@ -113,6 +114,7 @@ public class DirectoryFragment extends Fragment {
|
||||
|
||||
private boolean mHideGridTitles = false;
|
||||
|
||||
private boolean mSvelteRecents;
|
||||
private Point mThumbSize;
|
||||
|
||||
private DocumentsAdapter mAdapter;
|
||||
@@ -203,6 +205,19 @@ public class DirectoryFragment extends Fragment {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
// Cancel any outstanding thumbnail requests
|
||||
final ViewGroup target = (mListView.getAdapter() != null) ? mListView : mGridView;
|
||||
final int count = target.getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View view = target.getChildAt(i);
|
||||
mRecycleListener.onMovedToScrapHeap(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
@@ -225,6 +240,10 @@ public class DirectoryFragment extends Fragment {
|
||||
mHideGridTitles = (doc != null) && doc.isGridTitlesHidden();
|
||||
}
|
||||
|
||||
final ActivityManager am = (ActivityManager) context.getSystemService(
|
||||
Context.ACTIVITY_SERVICE);
|
||||
mSvelteRecents = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
|
||||
|
||||
mCallbacks = new LoaderCallbacks<DirectoryResult>() {
|
||||
@Override
|
||||
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
|
||||
@@ -260,7 +279,7 @@ public class DirectoryFragment extends Fragment {
|
||||
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
|
||||
if (!isAdded()) return;
|
||||
|
||||
mAdapter.swapResult(result.cursor, result.exception);
|
||||
mAdapter.swapResult(result);
|
||||
|
||||
// Push latest state up to UI
|
||||
// TODO: if mode change was racing with us, don't overwrite it
|
||||
@@ -286,7 +305,7 @@ public class DirectoryFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<DirectoryResult> loader) {
|
||||
mAdapter.swapResult(null, null);
|
||||
mAdapter.swapResult(null);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -654,13 +673,13 @@ public class DirectoryFragment extends Fragment {
|
||||
|
||||
private List<Footer> mFooters = Lists.newArrayList();
|
||||
|
||||
public void swapResult(Cursor cursor, Exception e) {
|
||||
mCursor = cursor;
|
||||
mCursorCount = cursor != null ? cursor.getCount() : 0;
|
||||
public void swapResult(DirectoryResult result) {
|
||||
mCursor = result != null ? result.cursor : null;
|
||||
mCursorCount = mCursor != null ? mCursor.getCount() : 0;
|
||||
|
||||
mFooters.clear();
|
||||
|
||||
final Bundle extras = cursor != null ? cursor.getExtras() : null;
|
||||
final Bundle extras = mCursor != null ? mCursor.getExtras() : null;
|
||||
if (extras != null) {
|
||||
final String info = extras.getString(DocumentsContract.EXTRA_INFO);
|
||||
if (info != null) {
|
||||
@@ -675,7 +694,7 @@ public class DirectoryFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
if (e != null) {
|
||||
if (result != null && result.exception != null) {
|
||||
mFooters.add(new MessageFooter(
|
||||
3, R.drawable.ic_dialog_alert, getString(R.string.query_error)));
|
||||
}
|
||||
@@ -776,7 +795,7 @@ public class DirectoryFragment extends Fragment {
|
||||
final boolean supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
|
||||
final boolean allowThumbnail = (state.derivedMode == MODE_GRID)
|
||||
|| MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
|
||||
final boolean showThumbnail = supportsThumbnail && allowThumbnail;
|
||||
final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents;
|
||||
|
||||
boolean cacheHit = false;
|
||||
if (showThumbnail) {
|
||||
@@ -790,7 +809,7 @@ public class DirectoryFragment extends Fragment {
|
||||
final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
|
||||
uri, iconMime, iconThumb, mThumbSize);
|
||||
iconThumb.setTag(task);
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
task.executeOnExecutor(ProviderExecutor.forAuthority(docAuthority));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,6 +1002,8 @@ public class DirectoryFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
protected Bitmap doInBackground(Uri... params) {
|
||||
if (isCancelled()) return null;
|
||||
|
||||
final Context context = mIconThumb.getContext();
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
|
||||
public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
|
||||
int userSortOrder) {
|
||||
super(context);
|
||||
super(context, ProviderExecutor.forAuthority(root.authority));
|
||||
mType = type;
|
||||
mRoot = root;
|
||||
mDoc = doc;
|
||||
@@ -157,11 +157,11 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
|
||||
+ result.mode + ", sortOrder=" + result.sortOrder);
|
||||
|
||||
ContentProviderClient client = null;
|
||||
try {
|
||||
result.client = DocumentsApplication.acquireUnstableProviderOrThrow(
|
||||
resolver, authority);
|
||||
client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
|
||||
|
||||
cursor = result.client.query(
|
||||
cursor = client.query(
|
||||
mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
|
||||
cursor.registerContentObserver(mObserver);
|
||||
|
||||
@@ -175,11 +175,12 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
cursor = new SortingCursorWrapper(cursor, result.sortOrder);
|
||||
}
|
||||
|
||||
result.client = client;
|
||||
result.cursor = cursor;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Failed to query", e);
|
||||
result.exception = e;
|
||||
ContentProviderClient.releaseQuietly(result.client);
|
||||
ContentProviderClient.releaseQuietly(client);
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
mSignal = null;
|
||||
|
||||
@@ -91,6 +91,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class DocumentsActivity extends Activity {
|
||||
public static final String TAG = "Documents";
|
||||
@@ -215,7 +216,7 @@ public class DocumentsActivity extends Activity {
|
||||
if (!mState.restored) {
|
||||
if (mState.action == ACTION_MANAGE) {
|
||||
final Uri rootUri = getIntent().getData();
|
||||
new RestoreRootTask(rootUri).execute();
|
||||
new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
|
||||
} else {
|
||||
new RestoreStackTask().execute();
|
||||
}
|
||||
@@ -782,6 +783,15 @@ public class DocumentsActivity extends Activity {
|
||||
return mState.stack.peek();
|
||||
}
|
||||
|
||||
public Executor getCurrentExecutor() {
|
||||
final DocumentInfo cwd = getCurrentDirectory();
|
||||
if (cwd != null && cwd.authority != null) {
|
||||
return ProviderExecutor.forAuthority(cwd.authority);
|
||||
} else {
|
||||
return AsyncTask.THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
}
|
||||
|
||||
public State getDisplayState() {
|
||||
return mState;
|
||||
}
|
||||
@@ -855,7 +865,7 @@ public class DocumentsActivity extends Activity {
|
||||
mState.stackTouched = true;
|
||||
|
||||
if (!mRoots.isRecentsRoot(root)) {
|
||||
new PickRootTask(root).execute();
|
||||
new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
|
||||
} else {
|
||||
onCurrentDirectoryChanged(ANIM_SIDE);
|
||||
}
|
||||
@@ -932,7 +942,7 @@ public class DocumentsActivity extends Activity {
|
||||
onCurrentDirectoryChanged(ANIM_DOWN);
|
||||
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
|
||||
// Explicit file picked, return
|
||||
new ExistingFinishTask(doc.derivedUri).execute();
|
||||
new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getCurrentExecutor());
|
||||
} else if (mState.action == ACTION_CREATE) {
|
||||
// Replace selected file
|
||||
SaveFragment.get(fm).setReplaceTarget(doc);
|
||||
@@ -966,16 +976,16 @@ public class DocumentsActivity extends Activity {
|
||||
for (int i = 0; i < size; i++) {
|
||||
uris[i] = docs.get(i).derivedUri;
|
||||
}
|
||||
new ExistingFinishTask(uris).execute();
|
||||
new ExistingFinishTask(uris).executeOnExecutor(getCurrentExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
public void onSaveRequested(DocumentInfo replaceTarget) {
|
||||
new ExistingFinishTask(replaceTarget.derivedUri).execute();
|
||||
new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
|
||||
}
|
||||
|
||||
public void onSaveRequested(String mimeType, String displayName) {
|
||||
new CreateFinishTask(mimeType, displayName).execute();
|
||||
new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
|
||||
}
|
||||
|
||||
private void saveStackBlocking() {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 com.android.documentsui;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class ProviderExecutor extends Thread implements Executor {
|
||||
|
||||
@GuardedBy("sExecutors")
|
||||
private static HashMap<String, ProviderExecutor> sExecutors = Maps.newHashMap();
|
||||
|
||||
public static Executor forAuthority(String authority) {
|
||||
synchronized (sExecutors) {
|
||||
ProviderExecutor executor = sExecutors.get(authority);
|
||||
if (executor == null) {
|
||||
executor = new ProviderExecutor();
|
||||
executor.setName("ProviderExecutor: " + authority);
|
||||
executor.start();
|
||||
sExecutors.put(authority, executor);
|
||||
}
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
||||
private final LinkedBlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<Runnable>();
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
Preconditions.checkNotNull(command);
|
||||
mQueue.add(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
final Runnable command = mQueue.take();
|
||||
command.run();
|
||||
} catch (InterruptedException e) {
|
||||
// That was weird; let's go look for more tasks.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,9 +49,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
@@ -74,30 +72,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
/** MIME types that should always be excluded from recents. */
|
||||
private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
|
||||
|
||||
private static ExecutorService sExecutor;
|
||||
|
||||
/**
|
||||
* Create a bounded thread pool for fetching recents; it creates threads as
|
||||
* needed (up to maximum) and reclaims them when finished.
|
||||
*/
|
||||
private synchronized static ExecutorService getExecutor(Context context) {
|
||||
if (sExecutor == null) {
|
||||
final ActivityManager am = (ActivityManager) context.getSystemService(
|
||||
Context.ACTIVITY_SERVICE);
|
||||
final int maxOutstanding = am.isLowRamDevice() ? MAX_OUTSTANDING_RECENTS_SVELTE
|
||||
: MAX_OUTSTANDING_RECENTS;
|
||||
|
||||
// Create a bounded thread pool for fetching recents; it creates
|
||||
// threads as needed (up to maximum) and reclaims them when finished.
|
||||
final ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
||||
maxOutstanding, maxOutstanding, 10, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>());
|
||||
executor.allowCoreThreadTimeOut(true);
|
||||
sExecutor = executor;
|
||||
}
|
||||
|
||||
return sExecutor;
|
||||
}
|
||||
private final Semaphore mQueryPermits;
|
||||
|
||||
private final RootsCache mRoots;
|
||||
private final State mState;
|
||||
@@ -129,6 +104,20 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
public void run() {
|
||||
if (isCancelled()) return;
|
||||
|
||||
try {
|
||||
mQueryPermits.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
runInternal();
|
||||
} finally {
|
||||
mQueryPermits.release();
|
||||
}
|
||||
}
|
||||
|
||||
public void runInternal() {
|
||||
ContentProviderClient client = null;
|
||||
try {
|
||||
client = DocumentsApplication.acquireUnstableProviderOrThrow(
|
||||
@@ -138,6 +127,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
final Cursor cursor = client.query(
|
||||
uri, null, null, null, DirectoryLoader.getQuerySortOrder(mSortOrder));
|
||||
mWithRoot = new RootCursorWrapper(authority, rootId, cursor, MAX_DOCS_FROM_ROOT);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Failed to load " + authority + ", " + rootId, e);
|
||||
} finally {
|
||||
@@ -162,12 +152,17 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
super(context);
|
||||
mRoots = roots;
|
||||
mState = state;
|
||||
|
||||
// Keep clients around on high-RAM devices, since we'd be spinning them
|
||||
// up moments later to fetch thumbnails anyway.
|
||||
final ActivityManager am = (ActivityManager) getContext().getSystemService(
|
||||
Context.ACTIVITY_SERVICE);
|
||||
mQueryPermits = new Semaphore(
|
||||
am.isLowRamDevice() ? MAX_OUTSTANDING_RECENTS_SVELTE : MAX_OUTSTANDING_RECENTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryResult loadInBackground() {
|
||||
final ExecutorService executor = getExecutor(getContext());
|
||||
|
||||
if (mFirstPassLatch == null) {
|
||||
// First time through we kick off all the recent tasks, and wait
|
||||
// around to see if everyone finishes quickly.
|
||||
@@ -182,7 +177,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
|
||||
mFirstPassLatch = new CountDownLatch(mTasks.size());
|
||||
for (RecentTask task : mTasks.values()) {
|
||||
executor.execute(task);
|
||||
ProviderExecutor.forAuthority(task.authority).execute(task);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -224,7 +219,6 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
||||
|
||||
if (LOGD) {
|
||||
Log.d(TAG, "Found " + cursors.size() + " of " + mTasks.size() + " recent queries done");
|
||||
Log.d(TAG, executor.toString());
|
||||
}
|
||||
|
||||
final DirectoryResult result = new DirectoryResult();
|
||||
|
||||
@@ -66,6 +66,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
|
||||
private static final boolean CHILD_WEDGE = false;
|
||||
private static final boolean CHILD_CRASH = false;
|
||||
|
||||
private static final boolean THUMB_HUNDREDS = false;
|
||||
private static final boolean THUMB_WEDGE = false;
|
||||
private static final boolean THUMB_CRASH = false;
|
||||
|
||||
@@ -225,6 +226,12 @@ public class TestDocumentsProvider extends DocumentsProvider {
|
||||
includeFile(result, "localfile3", 0);
|
||||
includeFile(result, "localfile4", 0);
|
||||
|
||||
if (THUMB_HUNDREDS) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
includeFile(result, "i maded u an picshure", Document.FLAG_SUPPORTS_THUMBNAIL);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
// Try picking up an existing network fetch
|
||||
CloudTask task = mTask != null ? mTask.get() : null;
|
||||
@@ -292,7 +299,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
|
||||
public AssetFileDescriptor openDocumentThumbnail(
|
||||
String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
|
||||
|
||||
if (THUMB_WEDGE) SystemClock.sleep(Integer.MAX_VALUE);
|
||||
if (THUMB_WEDGE) wedgeUntilCanceled(signal);
|
||||
if (THUMB_CRASH) System.exit(12);
|
||||
|
||||
final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
|
||||
@@ -332,6 +339,18 @@ public class TestDocumentsProvider extends DocumentsProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void wedgeUntilCanceled(CancellationSignal signal) {
|
||||
if (signal != null) {
|
||||
while (true) {
|
||||
signal.throwIfCanceled();
|
||||
SystemClock.sleep(500);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "WEDGING WITHOUT A CANCELLATIONSIGNAL");
|
||||
SystemClock.sleep(Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void includeFile(MatrixCursor result, String docId, int flags) {
|
||||
final RowBuilder row = result.newRow();
|
||||
row.add(Document.COLUMN_DOCUMENT_ID, docId);
|
||||
|
||||
Reference in New Issue
Block a user