diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-10-02 10:15:14 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-10-02 10:15:14 -0700 |
commit | ba0591c9e3e22b975567011df784a825a49dcf4f (patch) | |
tree | 8be65aeb5102f484f35146ed9ba8b6aaa176f760 /packages/DocumentsUI | |
parent | 4a8d08285c0218d8d4cf7bc7f63feed2b2ccdb08 (diff) | |
parent | 21d8e96d840be42fae56483a2e291fc10677b2d9 (diff) | |
download | frameworks_base-ba0591c9e3e22b975567011df784a825a49dcf4f.zip frameworks_base-ba0591c9e3e22b975567011df784a825a49dcf4f.tar.gz frameworks_base-ba0591c9e3e22b975567011df784a825a49dcf4f.tar.bz2 |
am 21d8e96d: am ef4c3790: am 27d3c0fe: Merge "Isolate calls to each remote DocumentsProvider." into klp-dev
* commit '21d8e96d840be42fae56483a2e291fc10677b2d9':
Isolate calls to each remote DocumentsProvider.
Diffstat (limited to 'packages/DocumentsUI')
6 files changed, 147 insertions, 52 deletions
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index 23a3f22..22dd6e4 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java @@ -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); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 6ff47f8..59caad0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -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; @@ -204,6 +206,19 @@ public class DirectoryFragment extends Fragment { } @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(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index da0f526..163615d 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -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; diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 7a45641..7660779 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -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() { diff --git a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java new file mode 100644 index 0000000..2105cb4 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java @@ -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. + } + } + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java index 47dbcdf..3a8a3fb 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java @@ -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(); |