javaandroidchrome-custom-tabs

Chrome Custom Tabs pre-load webpages while browsing?


I'm testing the Chrome Custom Tabs. It is supposed to preload webpages a user is likely to click in the background. This happens through calling

session.mayLaunchUrl(uri, null, null); 

While the MainActivity is active, the webpage is preloaded nicely and when launching the URL, the webpage loads quickly as expected. However I would like to serve other webpages automatically to the user while he is already browsing (and the activity therefore is in background). Then, preloading the webpages does not seem to speed up the loading process anymore and even though the new served webpages are loading, this happens slowly.

I wrote a small app demonstrating this behavior. Preloading and launching an URL manually works nicely (as the activity is active, I suppose), serving new webpages in a loop automatically is slow (as the activity is not active and the mayLaunchUrl method does not work as expected then).

Is it possible to make use of the pre-loading mechanism while the user is browsing already? If yes, how?

I added the MainActivity code as example below:

public class MainActivity  extends AppCompatActivity {


private CustomTabsSession mCustomTabsSession;
private CustomTabsClient mClient;
private CustomTabsServiceConnection mConnection;

private EditText urlET;
private String TAG = "MainActivity";
private ArrayList<String> urlList;
private Thread cycleThread;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.v("MainActivity", "onCreate");

    urlList = new ArrayList<>();
    urlList.add("http://www.google.com");
    urlList.add("https://github.com");
    urlList.add("http://stackoverflow.com");
    urlList.add("http://www.heise.de");

    // pre launch the chrome browser, bind services etc
    warmup();

    urlET = (EditText) findViewById(R.id.urlID);

    // pre load a webpage manually 
    Button prepareBt = (Button) findViewById(R.id.prepareBt);
    assert prepareBt != null;
    prepareBt.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mayLaunch(null);
        }
    });

    //launch webpage manually
    Button launchBt = (Button) findViewById(R.id.launchBt);
    assert launchBt != null;
    launchBt.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            launch(null);
        }
    });

    //start a loop that serves webpages every 10 seconds
    Button cycleBt = (Button) findViewById(R.id.cycleBt);
    assert cycleBt != null;
    cycleBt.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(cycleThread !=null)
                cycleThread.interrupt();

            cycleThread = new Thread(cycle);
            cycleThread.start();
        }
    });

}
private Runnable cycle = new Runnable() {
    @Override
    public void run() {
        int i = 0;
        mayLaunch(Uri.parse(urlList.get(i)));
        try {
            Thread.sleep(5000);

            while (true){
                try {
                    Log.d(TAG, "launch: "+urlList.get(i));
                    launch(Uri.parse(urlList.get(i)));
                    i++;
                    if(i>=urlList.size())
                        i=0;
                    Thread.sleep(5000);
                    Log.d(TAG, "prepare: "+urlList.get(i));
                    mayLaunch(Uri.parse(urlList.get(i)));
                    Thread.sleep(5000);

                } catch (InterruptedException e) {
                   e.printStackTrace();
                    break;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};


private void mayLaunch(Uri uri){
    if(uri ==null)
        uri =  Uri.parse(urlET.getText().toString());
    CustomTabsSession session = getSession();
    session.mayLaunchUrl(uri, null, null);
}
private void launch(Uri uri){
    if(uri ==null)
        uri =  Uri.parse(urlET.getText().toString());
    CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder(getSession())

            .setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimaryDark))
            .setShowTitle(true)
            .setStartAnimations(this, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
            .setExitAnimations(this, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
            .build();

    customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
    customTabsIntent.launchUrl(this,uri);

}

public CustomTabsSession getSession() {
    if (mClient == null) {
        mCustomTabsSession = null;
    } else if (mCustomTabsSession == null) {
        mCustomTabsSession = mClient.newSession(null);
        Log.d(TAG, "getSession: created new session");
    }
    return mCustomTabsSession;
}

private void warmup(){
    if (mClient != null) return;
    String packageName = "com.android.chrome";
    if (packageName == null) return;

    mConnection = new CustomTabsServiceConnection() {
        @Override
        public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) {
            mClient = customTabsClient;
            mClient.warmup(0L);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mClient = null;
            mCustomTabsSession = null;
        }
    };
    CustomTabsClient.bindCustomTabsService(this, packageName, mConnection);
}


private void coolDown(){
    if (mConnection == null) return;
    unbindService(mConnection);
    mClient = null;
    mCustomTabsSession = null;
    mConnection = null;
}

public void onDestroy() {
    Log.v("MainActivity", "onDestroy");
    super.onDestroy();
    coolDown();
}
@Override
public void onBackPressed(){
}

}

Thanks!


Solution

  • CustomTabs ignores mayLaunchUrl() sent from the background. This is for 2 reasons:

    1. Simple measure to avoid wasted bandwidth (it is easy to check that a custom tab from the same session is not the foreground, just did not do it yet)

    2. Pages loaded through mayLaunchUrl() currently do not preserve Window.sessionStorage(), which is not a problem for App -> CustomTab transition, but for navigating within a single CustomTab this may break assumptions for users with navigation patterns like:

    SiteA -> SiteB -> SiteA

    (the second visit to SiteA does not see what is in the session). Teaching mayLaunchUrl() to choose the right sessionStorage() is complex, no plans for this yet.