summaryrefslogtreecommitdiffstats
path: root/packages/DocumentsUI
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-10-02 10:15:14 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2013-10-02 10:15:14 -0700
commitba0591c9e3e22b975567011df784a825a49dcf4f (patch)
tree8be65aeb5102f484f35146ed9ba8b6aaa176f760 /packages/DocumentsUI
parent4a8d08285c0218d8d4cf7bc7f63feed2b2ccdb08 (diff)
parent21d8e96d840be42fae56483a2e291fc10677b2d9 (diff)
downloadframeworks_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')
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java7
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java39
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java11
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java22
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java64
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java56
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();