* commit 'ef4c3790e52beab467359f6b5125b66fbe1993ef': Isolate calls to each remote DocumentsProvider.
This commit is contained in:
@ -26,6 +26,7 @@ import android.util.TimeUtils;
|
|||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract Loader that provides an {@link AsyncTask} to do the work. See
|
* 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 mTask;
|
||||||
volatile LoadTask mCancellingTask;
|
volatile LoadTask mCancellingTask;
|
||||||
|
|
||||||
@ -131,7 +134,13 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
|||||||
Handler mHandler;
|
Handler mHandler;
|
||||||
|
|
||||||
public AsyncTaskLoader(Context context) {
|
public AsyncTaskLoader(Context context) {
|
||||||
|
this(context, AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@hide} */
|
||||||
|
public AsyncTaskLoader(Context context, Executor executor) {
|
||||||
super(context);
|
super(context);
|
||||||
|
mExecutor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,7 +232,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) Slog.v(TAG, "Executing: " + mTask);
|
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
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
final String displayName = text1.getText().toString();
|
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);
|
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.getCursorLong;
|
||||||
import static com.android.documentsui.model.DocumentInfo.getCursorString;
|
import static com.android.documentsui.model.DocumentInfo.getCursorString;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.FragmentManager;
|
import android.app.FragmentManager;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
@ -113,6 +114,7 @@ public class DirectoryFragment extends Fragment {
|
|||||||
|
|
||||||
private boolean mHideGridTitles = false;
|
private boolean mHideGridTitles = false;
|
||||||
|
|
||||||
|
private boolean mSvelteRecents;
|
||||||
private Point mThumbSize;
|
private Point mThumbSize;
|
||||||
|
|
||||||
private DocumentsAdapter mAdapter;
|
private DocumentsAdapter mAdapter;
|
||||||
@ -203,6 +205,19 @@ public class DirectoryFragment extends Fragment {
|
|||||||
return view;
|
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
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
@ -225,6 +240,10 @@ public class DirectoryFragment extends Fragment {
|
|||||||
mHideGridTitles = (doc != null) && doc.isGridTitlesHidden();
|
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>() {
|
mCallbacks = new LoaderCallbacks<DirectoryResult>() {
|
||||||
@Override
|
@Override
|
||||||
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
|
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) {
|
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
|
|
||||||
mAdapter.swapResult(result.cursor, result.exception);
|
mAdapter.swapResult(result);
|
||||||
|
|
||||||
// Push latest state up to UI
|
// Push latest state up to UI
|
||||||
// TODO: if mode change was racing with us, don't overwrite it
|
// TODO: if mode change was racing with us, don't overwrite it
|
||||||
@ -286,7 +305,7 @@ public class DirectoryFragment extends Fragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoaderReset(Loader<DirectoryResult> loader) {
|
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();
|
private List<Footer> mFooters = Lists.newArrayList();
|
||||||
|
|
||||||
public void swapResult(Cursor cursor, Exception e) {
|
public void swapResult(DirectoryResult result) {
|
||||||
mCursor = cursor;
|
mCursor = result != null ? result.cursor : null;
|
||||||
mCursorCount = cursor != null ? cursor.getCount() : 0;
|
mCursorCount = mCursor != null ? mCursor.getCount() : 0;
|
||||||
|
|
||||||
mFooters.clear();
|
mFooters.clear();
|
||||||
|
|
||||||
final Bundle extras = cursor != null ? cursor.getExtras() : null;
|
final Bundle extras = mCursor != null ? mCursor.getExtras() : null;
|
||||||
if (extras != null) {
|
if (extras != null) {
|
||||||
final String info = extras.getString(DocumentsContract.EXTRA_INFO);
|
final String info = extras.getString(DocumentsContract.EXTRA_INFO);
|
||||||
if (info != null) {
|
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(
|
mFooters.add(new MessageFooter(
|
||||||
3, R.drawable.ic_dialog_alert, getString(R.string.query_error)));
|
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 supportsThumbnail = (docFlags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
|
||||||
final boolean allowThumbnail = (state.derivedMode == MODE_GRID)
|
final boolean allowThumbnail = (state.derivedMode == MODE_GRID)
|
||||||
|| MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
|
|| MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
|
||||||
final boolean showThumbnail = supportsThumbnail && allowThumbnail;
|
final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents;
|
||||||
|
|
||||||
boolean cacheHit = false;
|
boolean cacheHit = false;
|
||||||
if (showThumbnail) {
|
if (showThumbnail) {
|
||||||
@ -790,7 +809,7 @@ public class DirectoryFragment extends Fragment {
|
|||||||
final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
|
final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
|
||||||
uri, iconMime, iconThumb, mThumbSize);
|
uri, iconMime, iconThumb, mThumbSize);
|
||||||
iconThumb.setTag(task);
|
iconThumb.setTag(task);
|
||||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
task.executeOnExecutor(ProviderExecutor.forAuthority(docAuthority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,6 +1002,8 @@ public class DirectoryFragment extends Fragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Bitmap doInBackground(Uri... params) {
|
protected Bitmap doInBackground(Uri... params) {
|
||||||
|
if (isCancelled()) return null;
|
||||||
|
|
||||||
final Context context = mIconThumb.getContext();
|
final Context context = mIconThumb.getContext();
|
||||||
final ContentResolver resolver = context.getContentResolver();
|
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,
|
public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
|
||||||
int userSortOrder) {
|
int userSortOrder) {
|
||||||
super(context);
|
super(context, ProviderExecutor.forAuthority(root.authority));
|
||||||
mType = type;
|
mType = type;
|
||||||
mRoot = root;
|
mRoot = root;
|
||||||
mDoc = doc;
|
mDoc = doc;
|
||||||
@ -157,11 +157,11 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
|
|||||||
Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
|
Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
|
||||||
+ result.mode + ", sortOrder=" + result.sortOrder);
|
+ result.mode + ", sortOrder=" + result.sortOrder);
|
||||||
|
|
||||||
|
ContentProviderClient client = null;
|
||||||
try {
|
try {
|
||||||
result.client = DocumentsApplication.acquireUnstableProviderOrThrow(
|
client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
|
||||||
resolver, authority);
|
|
||||||
|
|
||||||
cursor = result.client.query(
|
cursor = client.query(
|
||||||
mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
|
mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
|
||||||
cursor.registerContentObserver(mObserver);
|
cursor.registerContentObserver(mObserver);
|
||||||
|
|
||||||
@ -175,11 +175,12 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
|
|||||||
cursor = new SortingCursorWrapper(cursor, result.sortOrder);
|
cursor = new SortingCursorWrapper(cursor, result.sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.client = client;
|
||||||
result.cursor = cursor;
|
result.cursor = cursor;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, "Failed to query", e);
|
Log.w(TAG, "Failed to query", e);
|
||||||
result.exception = e;
|
result.exception = e;
|
||||||
ContentProviderClient.releaseQuietly(result.client);
|
ContentProviderClient.releaseQuietly(client);
|
||||||
} finally {
|
} finally {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
mSignal = null;
|
mSignal = null;
|
||||||
|
@ -91,6 +91,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public class DocumentsActivity extends Activity {
|
public class DocumentsActivity extends Activity {
|
||||||
public static final String TAG = "Documents";
|
public static final String TAG = "Documents";
|
||||||
@ -215,7 +216,7 @@ public class DocumentsActivity extends Activity {
|
|||||||
if (!mState.restored) {
|
if (!mState.restored) {
|
||||||
if (mState.action == ACTION_MANAGE) {
|
if (mState.action == ACTION_MANAGE) {
|
||||||
final Uri rootUri = getIntent().getData();
|
final Uri rootUri = getIntent().getData();
|
||||||
new RestoreRootTask(rootUri).execute();
|
new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
|
||||||
} else {
|
} else {
|
||||||
new RestoreStackTask().execute();
|
new RestoreStackTask().execute();
|
||||||
}
|
}
|
||||||
@ -782,6 +783,15 @@ public class DocumentsActivity extends Activity {
|
|||||||
return mState.stack.peek();
|
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() {
|
public State getDisplayState() {
|
||||||
return mState;
|
return mState;
|
||||||
}
|
}
|
||||||
@ -855,7 +865,7 @@ public class DocumentsActivity extends Activity {
|
|||||||
mState.stackTouched = true;
|
mState.stackTouched = true;
|
||||||
|
|
||||||
if (!mRoots.isRecentsRoot(root)) {
|
if (!mRoots.isRecentsRoot(root)) {
|
||||||
new PickRootTask(root).execute();
|
new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
|
||||||
} else {
|
} else {
|
||||||
onCurrentDirectoryChanged(ANIM_SIDE);
|
onCurrentDirectoryChanged(ANIM_SIDE);
|
||||||
}
|
}
|
||||||
@ -932,7 +942,7 @@ public class DocumentsActivity extends Activity {
|
|||||||
onCurrentDirectoryChanged(ANIM_DOWN);
|
onCurrentDirectoryChanged(ANIM_DOWN);
|
||||||
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
|
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
|
||||||
// Explicit file picked, return
|
// Explicit file picked, return
|
||||||
new ExistingFinishTask(doc.derivedUri).execute();
|
new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getCurrentExecutor());
|
||||||
} else if (mState.action == ACTION_CREATE) {
|
} else if (mState.action == ACTION_CREATE) {
|
||||||
// Replace selected file
|
// Replace selected file
|
||||||
SaveFragment.get(fm).setReplaceTarget(doc);
|
SaveFragment.get(fm).setReplaceTarget(doc);
|
||||||
@ -966,16 +976,16 @@ public class DocumentsActivity extends Activity {
|
|||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
uris[i] = docs.get(i).derivedUri;
|
uris[i] = docs.get(i).derivedUri;
|
||||||
}
|
}
|
||||||
new ExistingFinishTask(uris).execute();
|
new ExistingFinishTask(uris).executeOnExecutor(getCurrentExecutor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSaveRequested(DocumentInfo replaceTarget) {
|
public void onSaveRequested(DocumentInfo replaceTarget) {
|
||||||
new ExistingFinishTask(replaceTarget.derivedUri).execute();
|
new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSaveRequested(String mimeType, String displayName) {
|
public void onSaveRequested(String mimeType, String displayName) {
|
||||||
new CreateFinishTask(mimeType, displayName).execute();
|
new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveStackBlocking() {
|
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.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
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. */
|
/** MIME types that should always be excluded from recents. */
|
||||||
private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
|
private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
|
||||||
|
|
||||||
private static ExecutorService sExecutor;
|
private final Semaphore mQueryPermits;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 RootsCache mRoots;
|
private final RootsCache mRoots;
|
||||||
private final State mState;
|
private final State mState;
|
||||||
@ -129,6 +104,20 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
|||||||
public void run() {
|
public void run() {
|
||||||
if (isCancelled()) return;
|
if (isCancelled()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mQueryPermits.acquire();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
runInternal();
|
||||||
|
} finally {
|
||||||
|
mQueryPermits.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runInternal() {
|
||||||
ContentProviderClient client = null;
|
ContentProviderClient client = null;
|
||||||
try {
|
try {
|
||||||
client = DocumentsApplication.acquireUnstableProviderOrThrow(
|
client = DocumentsApplication.acquireUnstableProviderOrThrow(
|
||||||
@ -138,6 +127,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
|||||||
final Cursor cursor = client.query(
|
final Cursor cursor = client.query(
|
||||||
uri, null, null, null, DirectoryLoader.getQuerySortOrder(mSortOrder));
|
uri, null, null, null, DirectoryLoader.getQuerySortOrder(mSortOrder));
|
||||||
mWithRoot = new RootCursorWrapper(authority, rootId, cursor, MAX_DOCS_FROM_ROOT);
|
mWithRoot = new RootCursorWrapper(authority, rootId, cursor, MAX_DOCS_FROM_ROOT);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, "Failed to load " + authority + ", " + rootId, e);
|
Log.w(TAG, "Failed to load " + authority + ", " + rootId, e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -162,12 +152,17 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
|||||||
super(context);
|
super(context);
|
||||||
mRoots = roots;
|
mRoots = roots;
|
||||||
mState = state;
|
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
|
@Override
|
||||||
public DirectoryResult loadInBackground() {
|
public DirectoryResult loadInBackground() {
|
||||||
final ExecutorService executor = getExecutor(getContext());
|
|
||||||
|
|
||||||
if (mFirstPassLatch == null) {
|
if (mFirstPassLatch == null) {
|
||||||
// First time through we kick off all the recent tasks, and wait
|
// First time through we kick off all the recent tasks, and wait
|
||||||
// around to see if everyone finishes quickly.
|
// around to see if everyone finishes quickly.
|
||||||
@ -182,7 +177,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
|||||||
|
|
||||||
mFirstPassLatch = new CountDownLatch(mTasks.size());
|
mFirstPassLatch = new CountDownLatch(mTasks.size());
|
||||||
for (RecentTask task : mTasks.values()) {
|
for (RecentTask task : mTasks.values()) {
|
||||||
executor.execute(task);
|
ProviderExecutor.forAuthority(task.authority).execute(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -224,7 +219,6 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
|
|||||||
|
|
||||||
if (LOGD) {
|
if (LOGD) {
|
||||||
Log.d(TAG, "Found " + cursors.size() + " of " + mTasks.size() + " recent queries done");
|
Log.d(TAG, "Found " + cursors.size() + " of " + mTasks.size() + " recent queries done");
|
||||||
Log.d(TAG, executor.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final DirectoryResult result = new DirectoryResult();
|
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_WEDGE = false;
|
||||||
private static final boolean CHILD_CRASH = 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_WEDGE = false;
|
||||||
private static final boolean THUMB_CRASH = false;
|
private static final boolean THUMB_CRASH = false;
|
||||||
|
|
||||||
@ -225,6 +226,12 @@ public class TestDocumentsProvider extends DocumentsProvider {
|
|||||||
includeFile(result, "localfile3", 0);
|
includeFile(result, "localfile3", 0);
|
||||||
includeFile(result, "localfile4", 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) {
|
synchronized (this) {
|
||||||
// Try picking up an existing network fetch
|
// Try picking up an existing network fetch
|
||||||
CloudTask task = mTask != null ? mTask.get() : null;
|
CloudTask task = mTask != null ? mTask.get() : null;
|
||||||
@ -292,7 +299,7 @@ public class TestDocumentsProvider extends DocumentsProvider {
|
|||||||
public AssetFileDescriptor openDocumentThumbnail(
|
public AssetFileDescriptor openDocumentThumbnail(
|
||||||
String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
|
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);
|
if (THUMB_CRASH) System.exit(12);
|
||||||
|
|
||||||
final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
|
final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
|
||||||
@ -332,6 +339,18 @@ public class TestDocumentsProvider extends DocumentsProvider {
|
|||||||
return true;
|
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) {
|
private static void includeFile(MatrixCursor result, String docId, int flags) {
|
||||||
final RowBuilder row = result.newRow();
|
final RowBuilder row = result.newRow();
|
||||||
row.add(Document.COLUMN_DOCUMENT_ID, docId);
|
row.add(Document.COLUMN_DOCUMENT_ID, docId);
|
||||||
|
Reference in New Issue
Block a user