[Android] Realize that WeChat mini-games can be closed and opened again to continue playing without reloading

Many apps on the market now have mini games (or called h5 games), and they use mini games to improve the stickiness of the entire app. Small game modules have already appeared on the WeChat app, Qutoutiao app, and Hello app.

Users can open mini-games to rest and relax after chatting, browsing information, and cycling. If a message suddenly comes or is tired and wants to read information while playing a game, close the game page and open it again. Generally speaking, the game needs to be reloaded , because memory resources are reclaimed after the game is closed. But as a game, the experience of reloading is not very good. It is possible that the user temporarily leaves for a while and then reloads. Not only does loading take time, but the previous game state is gone.

So how does the WeChat Mini Game close the game and reopen it, retaining the previous game state without reloading?

We know that after the activity is closed, it must go through the onCreate life cycle when it is opened again, then the page-related resources must be rebuilt, and the game must be reloaded.

In fact, when the WeChat mini-game closes the page, it’s not because it’s finished, but because it jumps to other pages. The mini-game page is still running in the background, the game is also in a paused state, and related resources are still retained in memory and have not been recycled. Open the same game again, then it won't reload again.

So there are two issues involved here.

The first question, how to ensure that the target page will not be rebuilt when returning to the previous page or jumping to another page from the game page?

This is very simple, just set the target page startup mode to SingleTask or SingleTop:

  <activity
         android:name="com.devnn.demo.MainActivity"
         android:launchMode="singleTask">
        <intent-filter>
             <action android:name="android.intent.action.MAIN" />
             <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
   </activity>

Or add a mark on the redirected Intent: clearTop or singleTop can also achieve the same effect:

     Intent intent = new Intent(this, MainActivity.class);
     intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
     //intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
     startActivity(intent);

clearTop is similar to SingleTask. It clears the activity above the target activity to ensure that the target activity is not rebuilt. This is suitable for returning to the main page.
SginleTop is the only activity that guarantees the top of the stack. This is suitable for jumping from one stack to another stack top page. (Go on to the second question.)

The second question, back to the game again, how to ensure that the game page will not be rebuilt?

This actually involves the knowledge of the startup mode. Just set the startup mode of the mini game page to SingleInstance. SingleInstance means that the activity exists in a separate task stack, and the next time the activity is started again, it will directly switch to this activity. Then it is convenient to switch back and forth between the pages in the two stacks. The configuration is as follows:

  <activity
            android:name="com.devnn.demo.ActivityA"
            android:launchMode="singleInstance">
  </activity>

Switch back to the previous page from the small game, use startActivity, and add the SingleTop tag to the intent to prevent the reconstruction of the activity at the top of the stack:

 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

In fact, the usage scenario of SingleInstance is very rare, and small games are a very typical scenario.

The author made a demo to realize the scene of a small game. Among them, MainActivity is the main page, and ActivityA is the imitation mini-game page. Jumping between two pages will not finish the page or rebuild the page. code show as below:

MainActivity.java

package com.devnn.demo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;


public class MainActivity extends AppCompatActivity {
    
    
    private String TAG="Demo_MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        Log.i(TAG,"onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
    
    
        Log.i(TAG,"onResume");
        super.onResume();
    }

    @Override
    protected void onNewIntent(Intent intent) {
    
    
        Log.i(TAG,"onNewIntent");
        super.onNewIntent(intent);
    }

    public void toActivityA(View view) {
    
    
        Intent intent = new Intent(this, ActivityA.class);
        startActivity(intent);
    }

}

ActivityA.java

package com.devnn.demo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class ActivityA extends AppCompatActivity {
    
    

    private String TAG = "Demo_ActivityA";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
    }

    @Override
    public void onBackPressed() {
    
    
        Log.i(TAG, "onBackPressed");
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        //intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
//        moveTaskToBack(true);//回到上一个任务栈,不推荐使用,如果上一个任务栈是最近任务,就会回到桌面

    }

    @Override
    protected void onNewIntent(Intent intent) {
    
    
        Log.i(TAG, "onNewIntent");
        super.onNewIntent(intent);
    }

    @Override
    protected void onResume() {
    
    
        super.onResume();
        Log.i(TAG, "onResume");
    }

    @Override
    protected void onPause() {
    
    
        Log.i(TAG, "onPause");
        super.onPause();
    }

    @Override
    protected void onDestroy() {
    
    
        Log.i(TAG, "onDestroy");
        super.onDestroy();
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.devnn.demo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Devnn">
        <activity
            android:name="com.devnn.demo.ActivityA"
            android:launchMode="singleInstance"></activity>
        <activity
            android:name="com.devnn.demo.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

The MainActivity page is displayed as follows:

The ActivityA page is displayed as follows:

Among them, ActivityA returns to MainActivity. The author rewrites the physical return key, which does not finish the current page, but explicitly jumps to MainActivity, simulating the jump button.

After jumping back and forth between MainActivty and Activity many times, the LogCat log is as follows:

2021-06-09 19:13:42.687 8098-8098/com.devnn.demo I/Demo_MainActivity: onCreate
2021-06-09 19:13:42.766 8098-8098/com.devnn.demo I/Demo_MainActivity: onResume
2021-06-09 19:13:44.677 8098-8098/com.devnn.demo I/Demo_ActivityA: onCreate
2021-06-09 19:13:44.711 8098-8098/com.devnn.demo I/Demo_ActivityA: onResume
2021-06-09 19:13:46.552 8098-8098/com.devnn.demo I/Demo_ActivityA: onBackPressed
2021-06-09 19:13:46.573 8098-8098/com.devnn.demo I/Demo_ActivityA: onPause
2021-06-09 19:13:46.631 8098-8098/com.devnn.demo I/Demo_MainActivity: onNewIntent
2021-06-09 19:13:46.634 8098-8098/com.devnn.demo I/Demo_MainActivity: onResume
2021-06-09 19:14:10.446 8098-8098/com.devnn.demo I/Demo_ActivityA: onNewIntent
2021-06-09 19:14:10.451 8098-8098/com.devnn.demo I/Demo_ActivityA: onResume
2021-06-09 19:14:24.356 8098-8098/com.devnn.demo I/Demo_ActivityA: onBackPressed
2021-06-09 19:14:24.386 8098-8098/com.devnn.demo I/Demo_ActivityA: onPause
2021-06-09 19:14:24.431 8098-8098/com.devnn.demo I/Demo_MainActivity: onNewIntent
2021-06-09 19:14:24.434 8098-8098/com.devnn.demo I/Demo_MainActivity: onResume
2021-06-09 19:14:26.228 8098-8098/com.devnn.demo I/Demo_ActivityA: onNewIntent
2021-06-09 19:14:26.231 8098-8098/com.devnn.demo I/Demo_ActivityA: onResume
2021-06-09 19:14:29.044 8098-8098/com.devnn.demo I/Demo_ActivityA: onBackPressed
2021-06-09 19:14:29.075 8098-8098/com.devnn.demo I/Demo_ActivityA: onPause
2021-06-09 19:14:29.113 8098-8098/com.devnn.demo I/Demo_MainActivity: onNewIntent
2021-06-09 19:14:29.115 8098-8098/com.devnn.demo I/Demo_MainActivity: onResume
2021-06-09 19:19:08.836 8098-8098/com.devnn.demo I/Demo_ActivityA: onNewIntent
2021-06-09 19:19:08.840 8098-8098/com.devnn.demo I/Demo_ActivityA: onResume

Judging from the log, the two page switches are not recreated, but go through the onNewIntent life cycle.

The WeChat mini-game will not reload when you open the same game again, but it will reload when you open a different game. How does this work? This is very simple, get the passed intent data in the onNewIntent callback, take out the game id, ignore it if it is the same game, and reload it if it is different.

Guess you like

Origin blog.csdn.net/devnn/article/details/117673748