diff options
| author | 2021-02-24 00:10:10 +0800 | |
|---|---|---|
| committer | 2021-02-25 18:41:50 +0800 | |
| commit | 401900c9b20dfaff1a32f28071022c4438c50880 (patch) | |
| tree | 489df63acee762e13427f60d0b83ec8535d32f9c | |
| parent | b8737dac2c10558a05a8df5e6882b110136d72f9 (diff) | |
Optimization: find views in single traversal.
The app will pass a list of views for Textview translation. Currently,
we traverse the view tree for each requested view. That means we will
traverse view tree many times. If the requested size of view list is
large, it shoule be a performance issue. We refine to find views in
sinslge traversal in this change.
Use a sample app with many views and layouts and compare the time of
two implementations. The average time of original solution is 300000
~ 600000 ns. The new approach is 150000 ~ 300000 ns.
Bug:178989965
Test: Write a sample app with many views and complicate structure,
see bug for test sample screenshot. Compare the execute time of two
implementation.
Change-Id: I5a051740ad40e77ed4a54294368031eb82ab87ab
| -rw-r--r-- | core/java/android/view/translation/UiTranslationController.java | 56 |
1 files changed, 45 insertions, 11 deletions
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index b49d3c004f44..b1e45bbcd875 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -32,6 +32,9 @@ import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewRootImpl; +import android.view.WindowManagerGlobal; import android.view.autofill.AutofillId; import android.view.translation.UiTranslationManager.UiTranslationState; @@ -194,24 +197,19 @@ public class UiTranslationController { private void onUiTranslationStarted(Translator translator, List<AutofillId> views) { synchronized (mLock) { // Find Views collect the translation data - // TODO(b/178084101): try to optimize, e.g. to this in a single traversal - final int viewCounts = views.size(); final ArrayList<TranslationRequest> requests = new ArrayList<>(); - for (int i = 0; i < viewCounts; i++) { - final AutofillId viewAutofillId = views.get(i); - final View view = mActivity.findViewByAutofillIdTraversal(viewAutofillId); - if (view == null) { - Log.w(TAG, "Can not find the View for autofill id= " + viewAutofillId); - continue; - } - mViews.put(viewAutofillId, new WeakReference<>(view)); + final ArrayList<View> foundViews = new ArrayList<>(); + findViewsTraversalByAutofillIds(views, foundViews); + for (int i = 0; i < foundViews.size(); i++) { + final View view = foundViews.get(i); + final int currentCount = i; mActivity.runOnUiThread(() -> { final TranslationRequest translationRequest = view.onCreateTranslationRequest(); if (translationRequest != null && translationRequest.getTranslationText().length() > 0) { requests.add(translationRequest); } - if (requests.size() == viewCounts) { + if (currentCount == (foundViews.size() - 1)) { Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request."); mWorkerHandler.sendMessage(PooledLambda.obtainMessage( UiTranslationController::sendTranslationRequest, @@ -222,6 +220,42 @@ public class UiTranslationController { } } + private void findViewsTraversalByAutofillIds(List<AutofillId> sourceViewIds, + ArrayList<View> foundViews) { + final ArrayList<ViewRootImpl> roots = + WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken()); + for (int rootNum = 0; rootNum < roots.size(); rootNum++) { + final View rootView = roots.get(rootNum).getView(); + if (rootView instanceof ViewGroup) { + findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds, foundViews); + } else { + addViewIfNeeded(sourceViewIds, rootView, foundViews); + } + } + } + + private void findViewsTraversalByAutofillIds(ViewGroup viewGroup, + List<AutofillId> sourceViewIds, ArrayList<View> foundViews) { + final int childCount = viewGroup.getChildCount(); + for (int i = 0; i < childCount; ++i) { + final View child = viewGroup.getChildAt(i); + if (child instanceof ViewGroup) { + findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds, foundViews); + } else { + addViewIfNeeded(sourceViewIds, child, foundViews); + } + } + } + + private void addViewIfNeeded(List<AutofillId> sourceViewIds, View view, + ArrayList<View> foundViews) { + final AutofillId autofillId = view.getAutofillId(); + if (sourceViewIds.contains(autofillId)) { + mViews.put(autofillId, new WeakReference<>(view)); + foundViews.add(view); + } + } + private void runForEachView(Consumer<View> action) { synchronized (mLock) { final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews); |