Commit 20e89d1f authored by Xi Han's avatar Xi Han Committed by Commit Bot

[Instant Start] 2-stages initialization for TabContentManager

The TabContentManager provides APIs to fetch Tab thumbnails from disk,
and we want to reuse these APIs in instant start. Thus, we split the
creation of TabContentManager into two parts:
1. creation: the constructor is called right after the TabModel is
   initialized (pre-native).
2. initWithNative: is called after native is initialized.

Also, add test fetchThumbnailsPreNativeTest() to verify that the
TabContentManager works properly without native initialization.

Bug: 1041865
Change-Id: I22eca912f396acf234118a27348edbd7054cdd29
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1992341
Commit-Queue: Xi Han <hanxi@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737114}
parent c0579a8c
...@@ -17,6 +17,7 @@ chrome_test_java_sources = [ ...@@ -17,6 +17,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/FocusedEditableTextFieldZoomTest.java", "javatests/src/org/chromium/chrome/browser/FocusedEditableTextFieldZoomTest.java",
"javatests/src/org/chromium/chrome/browser/HTTPSTabsOpenedFromExternalAppTest.java", "javatests/src/org/chromium/chrome/browser/HTTPSTabsOpenedFromExternalAppTest.java",
"javatests/src/org/chromium/chrome/browser/InstalledAppTest.java", "javatests/src/org/chromium/chrome/browser/InstalledAppTest.java",
"javatests/src/org/chromium/chrome/browser/InstantStartTest.java",
"javatests/src/org/chromium/chrome/browser/IntentHandlerTest.java", "javatests/src/org/chromium/chrome/browser/IntentHandlerTest.java",
"javatests/src/org/chromium/chrome/browser/JavaScriptEvalChromeTest.java", "javatests/src/org/chromium/chrome/browser/JavaScriptEvalChromeTest.java",
"javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java", "javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java",
......
...@@ -676,17 +676,30 @@ public class StartSurfaceLayoutTest { ...@@ -676,17 +676,30 @@ public class StartSurfaceLayoutTest {
int currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting(); int currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
assertEquals(2, currentFetchCount - oldFetchCount); assertEquals(2, currentFetchCount - oldFetchCount);
oldFetchCount = currentFetchCount; oldFetchCount = currentFetchCount;
int oldHistogramRecord = RecordHistogram.getHistogramValueCountForTesting(
TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
TabContentManager.ThumbnailFetchingResult.GOT_JPEG);
for (int i = 0; i < mRepeat; i++) { for (int i = 0; i < mRepeat; i++) {
switchTabModel(false); switchTabModel(false);
currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting(); currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
int currentHistogramRecord = RecordHistogram.getHistogramValueCountForTesting(
TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
TabContentManager.ThumbnailFetchingResult.GOT_JPEG);
assertEquals(1, currentFetchCount - oldFetchCount); assertEquals(1, currentFetchCount - oldFetchCount);
assertEquals(1, currentHistogramRecord - oldHistogramRecord);
oldFetchCount = currentFetchCount; oldFetchCount = currentFetchCount;
oldHistogramRecord = currentHistogramRecord;
switchTabModel(true); switchTabModel(true);
currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting(); currentFetchCount = mTabListDelegate.getBitmapFetchCountForTesting();
currentHistogramRecord = RecordHistogram.getHistogramValueCountForTesting(
TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
TabContentManager.ThumbnailFetchingResult.GOT_JPEG);
assertEquals(2, currentFetchCount - oldFetchCount); assertEquals(2, currentFetchCount - oldFetchCount);
assertEquals(2, currentHistogramRecord - oldHistogramRecord);
oldFetchCount = currentFetchCount; oldFetchCount = currentFetchCount;
oldHistogramRecord = currentHistogramRecord;
} }
leaveGTSAndVerifyThumbnailsAreReleased(); leaveGTSAndVerifyThumbnailsAreReleased();
} }
......
...@@ -450,6 +450,9 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -450,6 +450,9 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
mCompositorViewHolder.getCompositorView()); mCompositorViewHolder.getCompositorView());
initializeTabModels(); initializeTabModels();
setTabContentManager(new TabContentManager(this, getContentOffsetProvider(),
!SysUtils.isLowEndDevice(), mTabModelSelector::getTabById));
if (!isFinishing() && getFullscreenManager() != null) { if (!isFinishing() && getFullscreenManager() != null) {
getFullscreenManager().initialize( getFullscreenManager().initialize(
(ControlContainer) findViewById(R.id.control_container), (ControlContainer) findViewById(R.id.control_container),
...@@ -769,8 +772,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -769,8 +772,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
TraceEvent.begin("ChromeActivity:CompositorInitialization"); TraceEvent.begin("ChromeActivity:CompositorInitialization");
super.initializeCompositor(); super.initializeCompositor();
setTabContentManager(new TabContentManager(this, getContentOffsetProvider(), getTabContentManager().initWithNative();
DeviceClassManager.enableSnapshots(), mTabModelSelector::getTabById));
mCompositorViewHolder.onNativeLibraryReady(getWindowAndroid(), getTabContentManager()); mCompositorViewHolder.onNativeLibraryReady(getWindowAndroid(), getTabContentManager());
if (isContextualSearchAllowed() && ContextualSearchFieldTrial.isEnabled()) { if (isContextualSearchAllowed() && ContextualSearchFieldTrial.isEnabled()) {
......
...@@ -28,6 +28,7 @@ import org.chromium.base.TraceEvent; ...@@ -28,6 +28,7 @@ import org.chromium.base.TraceEvent;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.metrics.CachedMetrics;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.task.AsyncTask; import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.R; import org.chromium.chrome.R;
...@@ -75,8 +76,8 @@ public class TabContentManager { ...@@ -75,8 +76,8 @@ public class TabContentManager {
"GridTabSwitcher.ThumbnailFetchingResult"; "GridTabSwitcher.ThumbnailFetchingResult";
private Set<Integer> mRefectchedTabIds; private Set<Integer> mRefectchedTabIds;
private final float mThumbnailScale; private float mThumbnailScale;
private final int mFullResThumbnailsMaxSize; private int mFullResThumbnailsMaxSize;
private final ContentOffsetProvider mContentOffsetProvider; private final ContentOffsetProvider mContentOffsetProvider;
private int[] mPriorityTabIds; private int[] mPriorityTabIds;
private long mNativeTabContentManager; private long mNativeTabContentManager;
...@@ -86,6 +87,7 @@ public class TabContentManager { ...@@ -86,6 +87,7 @@ public class TabContentManager {
private boolean mSnapshotsEnabled; private boolean mSnapshotsEnabled;
private final TabFinder mTabFinder; private final TabFinder mTabFinder;
private final Context mContext;
/** /**
* Listener to receive the "Last Thumbnail" event. "Last Thumbnail" is the first time * Listener to receive the "Last Thumbnail" event. "Last Thumbnail" is the first time
...@@ -149,33 +151,39 @@ public class TabContentManager { ...@@ -149,33 +151,39 @@ public class TabContentManager {
*/ */
public TabContentManager(Context context, ContentOffsetProvider contentOffsetProvider, public TabContentManager(Context context, ContentOffsetProvider contentOffsetProvider,
boolean snapshotsEnabled, TabFinder tabFinder) { boolean snapshotsEnabled, TabFinder tabFinder) {
mContext = context;
mContentOffsetProvider = contentOffsetProvider; mContentOffsetProvider = contentOffsetProvider;
mTabFinder = tabFinder; mTabFinder = tabFinder;
mSnapshotsEnabled = snapshotsEnabled; mSnapshotsEnabled = snapshotsEnabled;
}
/**
* Called after native library is loaded.
*/
public void initWithNative() {
// Override the cache size on the command line with --thumbnails=100 // Override the cache size on the command line with --thumbnails=100
int defaultCacheSize = getIntegerResourceWithOverride( int defaultCacheSize = getIntegerResourceWithOverride(
context, R.integer.default_thumbnail_cache_size, ChromeSwitches.THUMBNAILS); mContext, R.integer.default_thumbnail_cache_size, ChromeSwitches.THUMBNAILS);
mFullResThumbnailsMaxSize = defaultCacheSize; mFullResThumbnailsMaxSize = defaultCacheSize;
int compressionQueueMaxSize = int compressionQueueMaxSize =
context.getResources().getInteger(R.integer.default_compression_queue_size); mContext.getResources().getInteger(R.integer.default_compression_queue_size);
int writeQueueMaxSize = int writeQueueMaxSize =
context.getResources().getInteger(R.integer.default_write_queue_size); mContext.getResources().getInteger(R.integer.default_write_queue_size);
// Override the cache size on the command line with // Override the cache size on the command line with
// --approximation-thumbnails=100 // --approximation-thumbnails=100
int approximationCacheSize = getIntegerResourceWithOverride(context, int approximationCacheSize = getIntegerResourceWithOverride(mContext,
R.integer.default_approximation_thumbnail_cache_size, R.integer.default_approximation_thumbnail_cache_size,
ChromeSwitches.APPROXIMATION_THUMBNAILS); ChromeSwitches.APPROXIMATION_THUMBNAILS);
float thumbnailScale = 1.f; float thumbnailScale = 1.f;
boolean useApproximationThumbnails; boolean useApproximationThumbnails;
boolean saveJpegThumbnails = FeatureUtilities.isGridTabSwitcherEnabled(); boolean saveJpegThumbnails = FeatureUtilities.isGridTabSwitcherEnabled();
DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(context); DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(mContext);
float deviceDensity = display.getDipScale(); float deviceDensity = display.getDipScale();
if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)) { if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext)) {
// Scale all tablets to MDPI. // Scale all tablets to MDPI.
thumbnailScale = 1.f / deviceDensity; thumbnailScale = 1.f / deviceDensity;
useApproximationThumbnails = false; useApproximationThumbnails = false;
...@@ -443,6 +451,10 @@ public class TabContentManager { ...@@ -443,6 +451,10 @@ public class TabContentManager {
notifyOnLastThumbnail(); notifyOnLastThumbnail();
} }
if (jpeg != null) { if (jpeg != null) {
CachedMetrics.EnumeratedHistogramSample histogram =
new CachedMetrics.EnumeratedHistogramSample(
UMA_THUMBNAIL_FETCHING_RESULT,
ThumbnailFetchingResult.NUM_ENTRIES);
if (FeatureUtilities.isAllowToRefetchTabThumbnail()) { if (FeatureUtilities.isAllowToRefetchTabThumbnail()) {
double jpegAspectRatio = jpeg.getHeight() == 0 double jpegAspectRatio = jpeg.getHeight() == 0
? 0 ? 0
...@@ -452,9 +464,8 @@ public class TabContentManager { ...@@ -452,9 +464,8 @@ public class TabContentManager {
if (!mRefectchedTabIds.contains(tabId) if (!mRefectchedTabIds.contains(tabId)
&& Math.abs(jpegAspectRatio - mExpectedThumbnailAspectRatio) && Math.abs(jpegAspectRatio - mExpectedThumbnailAspectRatio)
>= ASPECT_RATIO_PRECISION) { >= ASPECT_RATIO_PRECISION) {
RecordHistogram.recordEnumeratedHistogram(UMA_THUMBNAIL_FETCHING_RESULT, histogram.record(
ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG, ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG);
ThumbnailFetchingResult.NUM_ENTRIES);
mRefectchedTabIds.add(tabId); mRefectchedTabIds.add(tabId);
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return; if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
TabContentManagerJni.get().getEtc1TabThumbnail(mNativeTabContentManager, TabContentManagerJni.get().getEtc1TabThumbnail(mNativeTabContentManager,
...@@ -463,8 +474,7 @@ public class TabContentManager { ...@@ -463,8 +474,7 @@ public class TabContentManager {
return; return;
} }
} }
RecordHistogram.recordEnumeratedHistogram(UMA_THUMBNAIL_FETCHING_RESULT, histogram.record(ThumbnailFetchingResult.GOT_JPEG);
ThumbnailFetchingResult.GOT_JPEG, ThumbnailFetchingResult.NUM_ENTRIES);
callback.onResult(jpeg); callback.onResult(jpeg);
return; return;
......
...@@ -22,7 +22,6 @@ public class DeviceClassManager { ...@@ -22,7 +22,6 @@ public class DeviceClassManager {
private static DeviceClassManager sInstance; private static DeviceClassManager sInstance;
// Set of features that can be enabled/disabled // Set of features that can be enabled/disabled
private boolean mEnableSnapshots;
private boolean mEnableLayerDecorationCache; private boolean mEnableLayerDecorationCache;
private boolean mEnableAccessibilityLayout; private boolean mEnableAccessibilityLayout;
private boolean mEnableAnimations; private boolean mEnableAnimations;
...@@ -45,7 +44,6 @@ public class DeviceClassManager { ...@@ -45,7 +44,6 @@ public class DeviceClassManager {
private DeviceClassManager() { private DeviceClassManager() {
// Device based configurations. // Device based configurations.
if (SysUtils.isLowEndDevice()) { if (SysUtils.isLowEndDevice()) {
mEnableSnapshots = false;
mEnableLayerDecorationCache = true; mEnableLayerDecorationCache = true;
mEnableAccessibilityLayout = mEnableAccessibilityLayout =
!FeatureUtilities.isTabGroupsAndroidContinuationChromeFlagEnabled() !FeatureUtilities.isTabGroupsAndroidContinuationChromeFlagEnabled()
...@@ -54,7 +52,6 @@ public class DeviceClassManager { ...@@ -54,7 +52,6 @@ public class DeviceClassManager {
mEnablePrerendering = false; mEnablePrerendering = false;
mEnableToolbarSwipe = false; mEnableToolbarSwipe = false;
} else { } else {
mEnableSnapshots = true;
mEnableLayerDecorationCache = true; mEnableLayerDecorationCache = true;
mEnableAccessibilityLayout = false; mEnableAccessibilityLayout = false;
mEnableAnimations = true; mEnableAnimations = true;
...@@ -80,13 +77,6 @@ public class DeviceClassManager { ...@@ -80,13 +77,6 @@ public class DeviceClassManager {
} }
} }
/**
* @return Whether or not we can take screenshots.
*/
public static boolean enableSnapshots() {
return getInstance().mEnableSnapshots;
}
/** /**
* @return Whether or not we can use the layer decoration cache. * @return Whether or not we can use the layer decoration cache.
*/ */
......
...@@ -6,8 +6,11 @@ package org.chromium.chrome.browser.init; ...@@ -6,8 +6,11 @@ package org.chromium.chrome.browser.init;
import android.content.Intent; import android.content.Intent;
import org.chromium.base.CommandLine;
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer; import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -20,7 +23,7 @@ import java.util.List; ...@@ -20,7 +23,7 @@ import java.util.List;
* the library has been loaded. * the library has been loaded.
*/ */
class NativeInitializationController { class NativeInitializationController {
private static final String TAG = "NativeInitializationController"; private static final String TAG = "NIController";
private final ChromeActivityNativeDelegate mActivityDelegate; private final ChromeActivityNativeDelegate mActivityDelegate;
...@@ -68,6 +71,11 @@ class NativeInitializationController { ...@@ -68,6 +71,11 @@ class NativeInitializationController {
*/ */
public void startBackgroundTasks(final boolean allocateChildConnection) { public void startBackgroundTasks(final boolean allocateChildConnection) {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
if (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_NATIVE_INITIALIZATION)) {
Log.i(TAG, "Exit early and start Chrome without loading native library!");
return;
}
assert mBackgroundTasksComplete == null; assert mBackgroundTasksComplete == null;
boolean fetchVariationsSeed = FirstRunFlowSequencer.checkIfFirstRunIsNecessary( boolean fetchVariationsSeed = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(
mActivityDelegate.getInitialIntent(), false); mActivityDelegate.getInitialIntent(), false);
......
...@@ -78,6 +78,12 @@ public abstract class ChromeSwitches {{ ...@@ -78,6 +78,12 @@ public abstract class ChromeSwitches {{
*/ */
public static final String DISABLE_TAB_MERGING_FOR_TESTING = "disable-tab-merging"; public static final String DISABLE_TAB_MERGING_FOR_TESTING = "disable-tab-merging";
/**
* Disable native initialization for testing.
*/
public static final String DISABLE_NATIVE_INITIALIZATION = "disable-native-initialization";
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////
// Native Switches // Native Switches
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import android.content.Intent;
import android.graphics.Bitmap;
import android.support.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.chromium.base.Callback;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Integration tests of Instant Start which requires 2-stage initialization for Clank startup.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({org.chromium.chrome.browser.ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class InstantStartTest {
private Bitmap mBitmap;
private int mThumbnailFetchCount;
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule
public TestRule mProcessor = new Features.InstrumentationProcessor();
@Before
public void setUp() {
startMainActivityFromLauncherAndNotWaitForNative();
}
private void startMainActivityFromLauncherAndNotWaitForNative() {
// Only launch Chrome.
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
mActivityTestRule.prepareUrlIntent(intent, null);
mActivityTestRule.startActivityCompletely(intent);
CriteriaHelper.pollUiThread(
()
-> mActivityTestRule.getActivity().getTabContentManager() != null,
"TabContentManager never created.");
}
private Bitmap createThumbnailBitmapAndWriteToFile(int tabId) {
final Bitmap thumbnailBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
try {
File thumbnailFile = TabContentManager.getTabThumbnailFileJpeg(tabId);
if (thumbnailFile.exists()) {
thumbnailFile.delete();
}
Assert.assertFalse(thumbnailFile.exists());
FileOutputStream thumbnailFileOutputStream = new FileOutputStream(thumbnailFile);
thumbnailBitmap.compress(Bitmap.CompressFormat.JPEG, 100, thumbnailFileOutputStream);
thumbnailFileOutputStream.flush();
thumbnailFileOutputStream.close();
Assert.assertTrue(thumbnailFile.exists());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return thumbnailBitmap;
}
/**
* Test TabContentManager is able to fetch thumbnail jpeg files before native is initialized.
*/
@Test
@SmallTest
@CommandLineFlags.Add(org.chromium.chrome.browser.ChromeSwitches.DISABLE_NATIVE_INITIALIZATION)
public void fetchThumbnailsPreNativeTest() {
int tabId = 0;
mThumbnailFetchCount = 0;
Callback<Bitmap> thumbnailFetchListener = (bitmap) -> {
mThumbnailFetchCount++;
mBitmap = bitmap;
};
TabContentManager tabContentManager =
mActivityTestRule.getActivity().getTabContentManager();
final Bitmap thumbnailBitmap = createThumbnailBitmapAndWriteToFile(tabId);
tabContentManager.getTabThumbnailWithCallback(tabId, thumbnailFetchListener, false, false);
CriteriaHelper.pollInstrumentationThread(new Criteria() {
@Override
public boolean isSatisfied() {
return mThumbnailFetchCount > 0;
}
});
Assert.assertFalse(LibraryLoader.getInstance().isInitialized());
Assert.assertEquals(1, mThumbnailFetchCount);
Bitmap fetchedThumbnail = mBitmap;
Assert.assertEquals(thumbnailBitmap.getByteCount(), fetchedThumbnail.getByteCount());
Assert.assertEquals(thumbnailBitmap.getWidth(), fetchedThumbnail.getWidth());
Assert.assertEquals(thumbnailBitmap.getHeight(), fetchedThumbnail.getHeight());
}
}
\ No newline at end of file
...@@ -80,6 +80,7 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule { ...@@ -80,6 +80,7 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule {
TabModelOrderController orderController = new TabModelOrderControllerImpl(mSelector); TabModelOrderController orderController = new TabModelOrderControllerImpl(mSelector);
TabContentManager tabContentManager = new TabContentManager( TabContentManager tabContentManager = new TabContentManager(
InstrumentationRegistry.getTargetContext(), null, false, mSelector::getTabById); InstrumentationRegistry.getTargetContext(), null, false, mSelector::getTabById);
tabContentManager.initWithNative();
TabPersistencePolicy persistencePolicy = new TabbedModeTabPersistencePolicy(0, false); TabPersistencePolicy persistencePolicy = new TabbedModeTabPersistencePolicy(0, false);
TabPersistentStore tabPersistentStore = TabPersistentStore tabPersistentStore =
new TabPersistentStore(persistencePolicy, mSelector, null, null); new TabPersistentStore(persistencePolicy, mSelector, null, null);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment