现在基本上每个app都有自己的引导页或者欢迎界面,用来展示公司logo或者广告浏览、也有一些产品需求是达到一定的界面美化,做一个闪屏界面再进入主界面。不至于显示内容太过突兀。

项目需求最终应该实现效果:
1、用户刚安装apk,进入应用跳转欢迎界面。
2、应用被完全杀死,再进入应用,跳转欢迎界面
3、使用期间,如果应用在后台运行,再次进入程序,不显示欢迎界面
4、(冷启动修复)进入主界面等待时间减短30%。(巧用欢迎界面)
5、MainActivity必须具有桌面属性。

实现功能不难,但是根据不同的项目有不用的需求。
普遍的创建一个SplashActivity。这个应用作为起始页。

方式一:SplashActivity+Timer计时器

由于需要设置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()

方式二:SplashActivity+Handler

其实原理和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)
        }
    }
}

方式三:MainActivity+标志位

需求增加,需要设置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中)。

android 欢迎页 finish 不关闭 手机欢迎界面_android


android 欢迎页 finish 不关闭 手机欢迎界面_android_02


上面的SPHelper是对SP的一个封装方法,用于保存当前欢迎界面是否打开过一次,如果打开过,后面再进入界面句不再打开跳转到欢迎界面。

/**
     * 保存欢迎界面是否第一次打开
     * @param isLock Boolean true 第一次,false已启动
     */
    fun saveIsOpenFirst(isOpen: Boolean) {
        SPUtils.getInstance().put("isOpen", isOpen)
    }


    fun getIsOpenFirst(): Boolean {
        return SPUtils.getInstance().getBoolean("isOpen")
    }

方式四:SplashActivity+SharedUtils+标志位

除了正常的欢迎界面,这里做了一个优化,就是新建了一个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

方式五 推荐:Theme替换

上面的代码除了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启动优化:下一篇文章将会介绍。