diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2015-08-28 15:19:36 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2015-09-15 10:43:13 -0700 |
commit | 527c7d3460345953e6b3427a74b1dacaddcb1111 (patch) | |
tree | a743cd6b772afa341e29c19d6c3fc0f864661bbd /src/com | |
parent | ef246f52c45c84e6d7e2fd1f7c143c6f06b7280d (diff) | |
download | android_packages_apps_Trebuchet-527c7d3460345953e6b3427a74b1dacaddcb1111.tar.gz android_packages_apps_Trebuchet-527c7d3460345953e6b3427a74b1dacaddcb1111.tar.bz2 android_packages_apps_Trebuchet-527c7d3460345953e6b3427a74b1dacaddcb1111.zip |
Refactoring deferred bind logic
> Using ViewTreeObserver to listen for onDraw instead of overriding onDraw in workspace
> Loader passes the list of deferrerd runnables to launcher
Change-Id: Ie4877f746c96e9497396de8089f00f70bf867e17
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 41 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 131 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 15 | ||||
-rw-r--r-- | src/com/android/launcher3/util/ViewOnDrawExecutor.java | 96 |
4 files changed, 179 insertions, 104 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index eea01d84a..17b72b7af 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -110,6 +110,7 @@ import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.ViewOnDrawExecutor; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetsContainerView; @@ -273,6 +274,7 @@ public class Launcher extends Activity private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>(); private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>(); + private ViewOnDrawExecutor mPendingExecutor; private LauncherModel mModel; private IconCache mIconCache; @@ -948,12 +950,6 @@ public class Launcher extends Activity mPaused = false; if (mRestoring || mOnResumeNeedsLoad) { setWorkspaceLoading(true); - - // If we're starting binding all over again, clear any bind calls we'd postponed in - // the past (see waitUntilResume) -- we don't need them since we're starting binding - // from scratch again - mBindOnResumeCallbacks.clear(); - mModel.startLoader(PagedView.INVALID_RESTORE_PAGE); mRestoring = false; mOnResumeNeedsLoad = false; @@ -3612,6 +3608,19 @@ public class Launcher extends Activity } /** + * Clear any pending bind callbacks. This is called when is loader is planning to + * perform a full rebind from scratch. + */ + @Override + public void clearPendingBinds() { + mBindOnResumeCallbacks.clear(); + if (mPendingExecutor != null) { + mPendingExecutor.markCompleted(); + mPendingExecutor = null; + } + } + + /** * Refreshes the shortcuts shown on the workspace. * * Implementation of the method from LauncherModel.Callbacks. @@ -3619,11 +3628,6 @@ public class Launcher extends Activity public void startBinding() { setWorkspaceLoading(true); - // If we're starting binding all over again, clear any bind calls we'd postponed in - // the past (see waitUntilResume) -- we don't need them since we're starting binding - // from scratch again - mBindOnResumeCallbacks.clear(); - // Clear the workspace because it's going to be rebound mWorkspace.clearDropTargets(); mWorkspace.removeAllWorkspaceScreens(); @@ -3988,6 +3992,21 @@ public class Launcher extends Activity mSynchronouslyBoundPages.add(page); } + @Override + public void executeOnNextDraw(ViewOnDrawExecutor executor) { + if (mPendingExecutor != null) { + mPendingExecutor.markCompleted(); + } + mPendingExecutor = executor; + executor.attachTo(this); + } + + public void clearPendingExecutor(ViewOnDrawExecutor executor) { + if (mPendingExecutor == executor) { + mPendingExecutor = null; + } + } + /** * Callback saying that there aren't any more items to bind. * diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index e8c53274c..30072695c 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -63,6 +63,7 @@ import com.android.launcher3.util.CursorIconInfo; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.ViewOnDrawExecutor; import java.lang.ref.WeakReference; import java.net.URISyntaxException; @@ -78,6 +79,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.Executor; /** * Maintains in-memory state of the Launcher. It is expected that there should be only one @@ -123,12 +125,6 @@ public class LauncherModel extends BroadcastReceiver @Thunk boolean mWorkspaceLoaded; @Thunk boolean mAllAppsLoaded; - // When we are loading pages synchronously, we can't just post the binding of items on the side - // pages as this delays the rotation process. Instead, we wait for a callback from the first - // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start - // a normal load, we also clear this set of Runnables. - static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>(); - /** * Set of runnables to be called on the background thread after the workspace binding * is complete. @@ -184,6 +180,7 @@ public class LauncherModel extends BroadcastReceiver public interface Callbacks { public boolean setLoadOnResume(); public int getCurrentWorkspaceScreen(); + public void clearPendingBinds(); public void startBinding(); public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, boolean forceAnimateIcons); @@ -208,6 +205,7 @@ public class LauncherModel extends BroadcastReceiver public void bindSearchProviderChanged(); public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); + public void executeOnNextDraw(ViewOnDrawExecutor executor); public void dumpLogsToLocalData(); } @@ -586,11 +584,6 @@ public class LauncherModel extends BroadcastReceiver "main thread"); } - // Clear any deferred bind runnables - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.clear(); - } - // Remove any queued UI runnables mHandler.cancelAll(); // Unbind all the workspace items @@ -1338,14 +1331,16 @@ public class LauncherModel extends BroadcastReceiver // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems InstallShortcutReceiver.enableInstallQueue(); synchronized (mLock) { - // Clear any deferred bind-runnables from the synchronized load process - // We must do this before any loading/binding is scheduled below. - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.clear(); - } - // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { + final Callbacks oldCallbacks = mCallbacks.get(); + // Clear any pending bind-runnables from the synchronized load process. + runOnMainThread(new Runnable() { + public void run() { + oldCallbacks.clearPendingBinds(); + } + }); + // If there is already one running, tell it to stop. stopLoaderLocked(); mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags); @@ -1360,21 +1355,6 @@ public class LauncherModel extends BroadcastReceiver } } - void bindRemainingSynchronousPages() { - // Post the remaining side pages to be loaded - if (!mDeferredBindRunnables.isEmpty()) { - Runnable[] deferredBindRunnables = null; - synchronized (mDeferredBindRunnables) { - deferredBindRunnables = mDeferredBindRunnables.toArray( - new Runnable[mDeferredBindRunnables.size()]); - mDeferredBindRunnables.clear(); - } - for (final Runnable r : deferredBindRunnables) { - mHandler.post(r); - } - } - } - public void stopLoader() { synchronized (mLock) { if (mLoaderTask != null) { @@ -2500,9 +2480,7 @@ public class LauncherModel extends BroadcastReceiver final ArrayList<ItemInfo> workspaceItems, final ArrayList<LauncherAppWidgetInfo> appWidgets, final LongArrayMap<FolderInfo> folders, - ArrayList<Runnable> deferredBindRunnables) { - - final boolean postOnMainThread = (deferredBindRunnables != null); + final Executor executor) { // Bind the workspace items int N = workspaceItems.size(); @@ -2519,13 +2497,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - synchronized (deferredBindRunnables) { - deferredBindRunnables.add(r); - } - } else { - runOnMainThread(r); - } + executor.execute(r); } // Bind the folders @@ -2538,13 +2510,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - synchronized (deferredBindRunnables) { - deferredBindRunnables.add(r); - } - } else { - runOnMainThread(r); - } + executor.execute(r); } // Bind the widgets, one at a time @@ -2559,11 +2525,7 @@ public class LauncherModel extends BroadcastReceiver } } }; - if (postOnMainThread) { - deferredBindRunnables.add(r); - } else { - runOnMainThread(r); - } + executor.execute(r); } } @@ -2641,6 +2603,7 @@ public class LauncherModel extends BroadcastReceiver public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { + callbacks.clearPendingBinds(); callbacks.startBinding(); } } @@ -2649,28 +2612,20 @@ public class LauncherModel extends BroadcastReceiver bindWorkspaceScreens(oldCallbacks, orderedScreenIds); - // Load items on the current page + Executor mainExecutor = new DeferredMainThreadExecutor(); + // Load items on the current page. bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, - currentFolders, null); - if (isLoadingSynchronously) { - r = new Runnable() { - public void run() { - Callbacks callbacks = tryGetCallbacks(oldCallbacks); - if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) { - callbacks.onPageBoundSynchronously(currentScreen); - } - } - }; - runOnMainThread(r); - } + currentFolders, mainExecutor); - // Load all the remaining pages (if we are loading synchronously, we want to defer this - // work until after the first render) - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.clear(); - } - bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, - (isLoadingSynchronously ? mDeferredBindRunnables : null)); + // In case of isLoadingSynchronously, only bind the first screen, and defer binding the + // remaining screens after first onDraw is called. This ensures that the first screen + // is immediately visible (eg. during rotation) + // In case of !isLoadingSynchronously, bind all pages one after other. + final Executor deferredExecutor = isLoadingSynchronously ? + new ViewOnDrawExecutor(mHandler) : mainExecutor; + + bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, + otherFolders, deferredExecutor); // Tell the workspace that we're done binding items r = new Runnable() { @@ -2700,11 +2655,23 @@ public class LauncherModel extends BroadcastReceiver } }; + deferredExecutor.execute(r); + if (isLoadingSynchronously) { - synchronized (mDeferredBindRunnables) { - mDeferredBindRunnables.add(r); - } - } else { + r = new Runnable() { + public void run() { + Callbacks callbacks = tryGetCallbacks(oldCallbacks); + if (callbacks != null) { + // We are loading synchronously, which means, some of the pages will be + // bound after first draw. Inform the callbacks that page binding is + // not complete, and schedule the remaining pages. + if (currentScreen != PagedView.INVALID_RESTORE_PAGE) { + callbacks.onPageBoundSynchronously(currentScreen); + } + callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor); + } + } + }; runOnMainThread(r); } } @@ -3730,6 +3697,14 @@ public class LauncherModel extends BroadcastReceiver } } + @Thunk class DeferredMainThreadExecutor implements Executor { + + @Override + public void execute(Runnable command) { + runOnMainThread(command); + } + } + /** * @return the looper for the worker thread which can be used to start background tasks. */ diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 18f5f7feb..15396585b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -278,13 +278,6 @@ public class Workspace extends PagedView private AccessibilityDelegate mPagesAccessibilityDelegate; - private final Runnable mBindPages = new Runnable() { - @Override - public void run() { - mLauncher.getModel().bindRemainingSynchronousPages(); - } - }; - /** * Used to inflate the Workspace from XML. * @@ -1719,14 +1712,6 @@ public class Workspace extends PagedView } @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - // Call back to LauncherModel to finish binding after the first draw - post(mBindPages); - } - - @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { if (!mLauncher.isAppsViewVisible()) { final Folder openFolder = getOpenFolder(); diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java new file mode 100644 index 000000000..01808ba57 --- /dev/null +++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2015 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.launcher3.util; + +import android.view.View; +import android.view.View.OnAttachStateChangeListener; +import android.view.ViewTreeObserver.OnDrawListener; + +import com.android.launcher3.DeferredHandler; +import com.android.launcher3.Launcher; + +import java.util.ArrayList; +import java.util.concurrent.Executor; + +/** + * An executor which runs all the tasks after the first onDraw is called on the target view. + */ +public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, + OnAttachStateChangeListener { + + private final ArrayList<Runnable> mTasks = new ArrayList<>(); + private final DeferredHandler mHandler; + + private Launcher mLauncher; + private View mAttachedView; + private boolean mCompleted; + + public ViewOnDrawExecutor(DeferredHandler handler) { + mHandler = handler; + } + + public void attachTo(Launcher launcher) { + mLauncher = launcher; + mAttachedView = launcher.getWorkspace(); + mAttachedView.addOnAttachStateChangeListener(this); + + attachObserver(); + } + + private void attachObserver() { + if (!mCompleted) { + mAttachedView.getViewTreeObserver().addOnDrawListener(this); + } + } + + @Override + public void execute(Runnable command) { + mTasks.add(command); + } + + @Override + public void onViewAttachedToWindow(View v) { + attachObserver(); + } + + @Override + public void onViewDetachedFromWindow(View v) { } + + @Override + public void onDraw() { + mAttachedView.post(this); + } + + @Override + public void run() { + for (final Runnable r : mTasks) { + mHandler.post(r); + } + markCompleted(); + } + + public void markCompleted() { + mTasks.clear(); + if (mAttachedView != null) { + mAttachedView.getViewTreeObserver().removeOnDrawListener(this); + mAttachedView.removeOnAttachStateChangeListener(this); + } + if (mLauncher != null) { + mLauncher.clearPendingExecutor(this); + } + } +} |