现在基本上每个app都有自己的引导页或者欢迎界面,用来展示公司logo或者广告浏览、也有一些产品需求是达到一定的界面美化,做一个闪屏界面再进入主界面。不至于显示内容太过突兀。
项目需求最终应该实现效果:
1、用户刚安装apk,进入应用跳转欢迎界面。
2、应用被完全杀死,再进入应用,跳转欢迎界面
3、使用期间,如果应用在后台运行,再次进入程序,不显示欢迎界面
4、(冷启动修复)进入主界面等待时间减短30%。(巧用欢迎界面)
5、MainActivity必须具有桌面属性。
实现功能不难,但是根据不同的项目有不用的需求。
普遍的创建一个SplashActivity。这个应用作为起始页。
由于需要设置3秒后跳转主界面,进行耗时操作,Timer里面已经创建子线程,使用handler处理。
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//隐藏标题栏以及状态栏
this.window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN)
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.jmgk_item_layout)
val timer = Timer()
val task: TimerTask = object : TimerTask() {
override fun run() {
val intent = Intent(this@SplashActivity, MainActivity::class.java)
startActivity(intent)
finish()
}
}
timer.schedule(task, 3000)//3s后跳转
}
}
如果觉得有必要,可以在onDestory()进行计时器的销毁。
timer.cancle()
其实原理和Timer计时器差不多,都是使用创建handler在子线程里面进行耗时操作。
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//隐藏标题栏以及状态栏
this.window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN)
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.jmgk_item_layout)
handler.sendEmptyMessageDelayed(0,3000)
}
private val handler: Handler = @SuppressLint("HandlerLeak")
object : Handler() {
override fun handleMessage(msg: Message?) {
val intent = Intent(
this@SplashActivity,
MainActivity::class.java
)
startActivity(intent)
finish()
super.handleMessage(msg)
}
}
}
需求增加,需要设置Mactivity.class为launcher桌面属性,所以起始页不能是SplashActivity欢迎界面。sp存储标识位,或者全局定义标识位。
就是说MainActivity里面必须设置为LAUNCHER,而不能在SplashActivity里面设置该属性:
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="sensorLandscape"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!--设置为桌面-->
<category android:name="android.intent.category.MONKEY" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
所以,很多人就会想到,虽然刚开始启动的是MainActivity主界面,可以在里面设置一个值判断是否已经显示过一次(这个值可以作为全局常量或者保存到SP中)。
上面的SPHelper是对SP的一个封装方法,用于保存当前欢迎界面是否打开过一次,如果打开过,后面再进入界面句不再打开跳转到欢迎界面。
/**
* 保存欢迎界面是否第一次打开
* @param isLock Boolean true 第一次,false已启动
*/
fun saveIsOpenFirst(isOpen: Boolean) {
SPUtils.getInstance().put("isOpen", isOpen)
}
fun getIsOpenFirst(): Boolean {
return SPUtils.getInstance().getBoolean("isOpen")
}
除了正常的欢迎界面,这里做了一个优化,就是新建了一个SharedUtils的activity.
public class SharedUtils extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String str=new SharedUtils().getShared("tag", SharedUtils.this);
Intent intent=new Intent(SharedUtils.this, SplashActivity.class);
startActivity(intent);
finish();
}else {
Intent intent=new Intent(SharedUtils.this, MainActivity.class);
startActivity(intent);
finish();
}
private String name="jianbao";
private String touxiang = "touxiang";
/*
* 保存数据的方法
* */
public void saveShared(String key,String value,Context ctx){
SharedPreferences shared=ctx.getSharedPreferences(name,0);
SharedPreferences.Editor edit = shared.edit();
edit.putString(key, value);
edit.commit();
}
/*
* 从本地获取数据
* */
public String getShared(String key, Context ctx){
String str=null;
SharedPreferences shared = ctx.getSharedPreferences(name, 0);
str = shared.getString(key, "");
return str;
}
}
利用SharedUtils 进行判断先跳转到那个界面。如果获取到的str为空,说明欢迎界面之前没有跳转过。即跳转SplashActivity.反之跳转到,主界面MainActivity
然后在Application里面进行初始化。
class AppApplication : App() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
private var utils: SharedUtils? = null
override fun onCreate() {
super.onCreate()
....
//初始化工具类antivity
utils = SharedUtils()
}
}
最后要在AndroidManifest.xml里面定义activty.
上面的方法我觉得都存在又一些瑕疵,一方面使用持久化存储占用一点内存,一方面声明一个Activity降低代码的简洁。
延伸:系统在运行apk的时候首先执行application的SharedUtils初始化,就会执行SharedUtils,在里面判读标识位状态,进行拦截跳转到对应的Activity
上面的代码除了SplashActivity.activity,其他的代码删除。
然后app的style文件里面定义
<resources>
<!-- 欢迎界面 主题 -->
<style name="splash" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@drawable/il_activity_splash</item>
</style>
<!-- 主界面 主题 -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@drawable/window_bg</item>
</style>
</resources>
然后在application里面先定义引导页的主题style为splash
<application
android:name=".AppApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/splash"
tools:replace="android:theme">
然后在Mainactivity重新定义回AppTheme,注意要在super.onCreate(savedInstanceState)之前。
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
}
延伸:系统一开始执行AndroidManifest.xml的application标签,就已经设置主题为splash,这个时候也正在执行 AppApplication.kt 的初始化,正好利用它初始化的时间,显示欢迎界面,只要app重新启动,就会出现欢迎界面,在app使用期间也不会再次出现欢迎界面
其实这里面涉及到 app启动的优化,对于一些冷启动,可以解决掉APP启动白屏问题。
在做个功能的时候我就想到利用显示欢迎界面的期间来进行application里面各项的初始化。因为项目太大了,使用到的第三方SDK也不少。每次启动app的时候,总是需要等待一段时间才能显示内容,其实这很大一部分时间都是用在application里面了。
为了区分文章侧重内容,关于app启动优化:下一篇文章将会介绍。