现在市面上很多app上都有小游戏(或叫h5游戏),它们借助小游戏来提高整个app的粘性。像微信app、趣头条app、哈啰app上已经出现了小游戏模块。
用户聊天、刷资讯、骑车之余可以打开网络游戏休息放松,如果突然间来消息了或者玩游戏累了想看资讯,关闭游戏页面后,再次打开游戏是接着玩,不需要重新加载游戏。
如何实现这种效果呢?
我们知道activity关闭后,再次打开肯定要走onCreate生命周期,那么页面相关资源就要重建,这样就得重新加载游戏了。
微信小游戏是如何做到,关闭activity后,再次打开会保留之前的资源和状态呢?
实际上,微信小游戏关闭activity关不是finish掉了,而且跳转到其它页面了,小游戏页面还在后台运行,游戏也处于暂停状态,相关资源还在内存保留,没有被回收。再次打开同一个游戏,那么就不会再重新加载了。
那么这里面就涉及两个问题。
第一个问题,从游戏页面返回上一个页面或者跳转到其它页面,如何保证目标页面不会重建呢?
这个很简单,把目标页面启动模式设置成SingleTask或者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>
或者在跳转的Intent上加上标记:clearTop或者singleTop也能达到同样的效果:
Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); //intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent);
clearTop类似于SingleTask,是清除目标activity之上的activity来保证目标activity不重建,这个适用于回到主页面。
SginleTop是保证栈顶的activity唯一,这个适用于从一个栈跳转到另一个栈的栈顶页面。(请接着看第二个问题。)
第二个问题,再次回到游戏,如何保证游戏页面不会重建呢?
这个其实也涉及到启动模式知识。将小游戏页面启动模式设置成SingleInstance即可。SingleInstance表示,将activity存在在单独的task栈中。那么两个栈中的页面来回切换就方便自如了。配置如下:
<activity android:name="com.devnn.demo.ActivityA" android:launchMode="singleInstance"> </activity>
笔者做了一个demo实现了小游戏这种场景。其中MainActivity是主页面,ActivityA是仿小游戏页面。
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="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 = "ActivityA"; @Override protected void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); Log.i(TAG, "onCreate_task_id="+getTaskId()); } @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>