From d01571e6d4e1c403534e19142720530d324eac9b Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 1 Oct 2013 17:57:41 -0700 Subject: [PATCH] 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 --- .../java/android/content/AsyncTaskLoader.java | 11 +++- .../documentsui/CreateDirectoryFragment.java | 7 +- .../documentsui/DirectoryFragment.java | 39 ++++++++--- .../android/documentsui/DirectoryLoader.java | 11 ++-- .../documentsui/DocumentsActivity.java | 22 +++++-- .../android/documentsui/ProviderExecutor.java | 64 +++++++++++++++++++ .../com/android/documentsui/RecentLoader.java | 56 ++++++++-------- .../TestDocumentsProvider.java | 21 +++++- 8 files changed, 177 insertions(+), 54 deletions(-) create mode 100644 packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java index 612c67f01cc05..eb7426e4b261e 100644 --- a/core/java/android/content/AsyncTaskLoader.java +++ b/core/java/android/content/AsyncTaskLoader.java @@ -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 extends Loader { } } + private final Executor mExecutor; + volatile LoadTask mTask; volatile LoadTask mCancellingTask; @@ -131,7 +134,13 @@ public abstract class AsyncTaskLoader extends Loader { 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 extends Loader { } } if (DEBUG) Slog.v(TAG, "Executing: " + mTask); - mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + mTask.executeOnExecutor(mExecutor, (Void[]) null); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index 23a3f22f4226d..22dd6e40285fa 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 6ff47f815926a..59caad0ecf573 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; @@ -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() { @Override public Loader onCreateLoader(int id, Bundle args) { @@ -260,7 +279,7 @@ public class DirectoryFragment extends Fragment { public void onLoadFinished(Loader 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 loader) { - mAdapter.swapResult(null, null); + mAdapter.swapResult(null); } }; @@ -654,13 +673,13 @@ public class DirectoryFragment extends Fragment { private List