每日一篇学习博客——Android冷启动

  • 什么是冷启动
  • 什么是热启动
  • 冷启动的方式直接就造成的不好体验如下
  • 先分析下冷启动的产生原因
  • 先说说怎么处理白屏或者黑屏吧
  • 优化启动的耗时操作,减少启动时间


什么是冷启动

简单来说就是APP的需要初始化启动,后台没有该应用的进程,直接点就是APP第一次打开、或者进程被杀死重新打开,这些启动都是需要重新创建Application实例——本人自己的理接(阔能很片面)

什么是热启动

当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,也就是直接从进程中启动,不需要重新创建Application,这个方式叫热启动。

冷启动的方式直接就造成的不好体验如下

  1. 响应慢 ,点击应用的icon不能瞬间打开应用,会出现短暂的白屏或者是黑屏;
  2. 用户体验很差,应用短暂的黑白屏现象直接让用户第一感觉就觉得不是很友好,卡顿的感觉,因为没有直接的提示或者过度,这一到两秒就显得很苍白;

先分析下冷启动的产生原因

首先冷启动是APP应用启动流程造成的,无可避免;只能尽可能的优化启动尽可能的缩短启动时间,其次就是做一些处理,优雅美观的实现过度.

Android冷启动闪屏页发生在哪里 冷屏启动是什么意思_Android冷启动闪屏页发生在哪里


白屏或者黑屏就是应用还没有加载道布局文件,显示得window窗口背景

先说说怎么处理白屏或者黑屏吧

  1. 瞒天过海之设置透明窗口背景:

style.xml设置(注意这里的AppTheme需要在``AndroidManifest.xml里引用):

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
    </style>
  1. 设置一张背景图: 例如splash.png
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowBackground">>@mipmap/splash</item>
        <item name="android:windowIsTranslucent">true</item>
      </style>

对比两者:

1). 第一种: 给人很慢的感觉,大一点的APP甚至让用户觉得是不是手机卡了,反应慢.优点的话就是直接一次性当前页面全部加载出来.
2). 第二种:让白屏/黑屏能够通过展示启动的页面来过度,简单来说就是=====>优雅 ,现在很多应用都是这么处理的,但是这种方式会使你的App一直有这个图片背景在显示,不是我们想要的结果,所以我们在Activity中重写一个生命周期中界面加载完成后的回调方法,将背景颜色改为白色

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    //修改背景为白色
    getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
}

还阔以设置的style不全局引用,只在主要页面引用就行,比如MainActivity.
代码:

<style name="AppTheme.Launcher" parent="Theme.AppCompat.Light.DarkActionBar"> 
        <!--设置背景图-->
        <item name="android:windowBackground">>@mipmap/splash</item>
        <!--设置透明-->
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
       </style>

然后继续操作上面的背景图修改为白色的操作就行!

3. 建议还是做一个欢迎页比较好,能有效的避免白屏问题还能做广告,这个页面的操作很少,还阔以设置跳转时间,提供充足的时间满足耗时操作,具体操作代码我写个了个小demo可以参考下.。很多大一点的应用都是有自己的欢迎页,以及自己定义的背景图来过度
SplashActivity——启动页

public class SplshActivity extends AppCompatActivity implements View.OnClickListener {

    private TimeCount time;
    private Button button;
    private ImageView iv;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        iv = findViewById(R.id.iv);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        button = findViewById(R.id.btn_into);
        button.setOnClickListener(this);
        time = new TimeCount(5000, 1000);
        time.start();
    }
    

    //计时器
    class TimeCount extends CountDownTimer {

        public TimeCount(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            button.setText("跳过 " + (millisUntilFinished / 1000) + "");
        }

        @Override
        public void onFinish() {
            SharedPreferences sf = getSharedPreferences("data", MODE_PRIVATE);//判断是否是第一次进入
            boolean isFirstIn = sf.getBoolean("isFirstIn", true);
            SharedPreferences.Editor editor = sf.edit();
            if (isFirstIn) {     //若为true,则是第一次进入,进入引导页
                Intent intent = new Intent(SplshActivity.this, MainActivity.class);
                startActivity(intent);
            } else {
                editor.putBoolean("isFirstIn", false);
                Intent intent = new Intent(SplshActivity.this, MainActivity.class);
                startActivity(intent);
//                finish();
            }
            editor.commit();

        }
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_into:
                time.cancel();//关闭倒计时
                time.onFinish();
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (time != null) {
            time.cancel();
            time = null;
        }
    }
}

对应的xml代码:activity_splash.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/launch"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
    
    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:src="@drawable/splash"/>

    <Button
        android:id="@+id/btn_into"
        android:layout_width="60dp"
        android:layout_height="30dp"
        android:text="跳过"
        android:layout_marginTop="20dp"
        android:textSize="15dp"
        android:background="@drawable/splash_btn_shape_gray1"
        android:layout_alignParentRight="true"
        android:layout_marginRight="19dp"
        android:textColor="#ffffff"
        />

</RelativeLayout>

drawble文件:splash_btn_shape_gray1.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#AAB2BD" />
<corners android:radius="30dp" />
</shape>

style.xml

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowIsTranslucent">true</item>
        <!--设置背景图-->
        <item name="android:windowBackground">@mipmap/splash</item>
        <!--设置透明-->
<!--        <item name="android:windowBackground">@android:color/transparent</item>-->
    </style>

</resources>

MainActivity.class

public class MainActivity extends AppCompatActivity {
    /**
     * 退出时的时间
     */
    private long mExitTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        //修改背景为白色
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            exit();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    public void exit() {
        if ((System.currentTimeMillis() - mExitTime) > 2000) {
            Toast.makeText(MainActivity.this, "再点一次退出", Toast.LENGTH_SHORT).show();
            mExitTime = System.currentTimeMillis();
        } else {
            //关闭活动退出
//            this.finish();
            //进入后台
            Intent homeIntent = new Intent(Intent.ACTION_MAIN);
            homeIntent.addCategory(Intent.CATEGORY_HOME);
            homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(homeIntent);
        }
    }
}

Manifests:

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

    <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/AppTheme" >
        <activity android:name=".SplshActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <activity android:name=".MainActivity"
            android:screenOrientation="portrait"
            android:configChanges="orientation|keyboardHidden|screenSize"
            />
    </application>

</manifest>

效果图

Android冷启动闪屏页发生在哪里 冷屏启动是什么意思_xml_02

欢迎页的操纵我是设置这个页面为优先启动,然后每次跳转到MainActivity中,且这个欢迎页不能关了,然后在MainActivity的物理键返回的方法重写直接退出APP或者是应用进程进入后台.

优化启动的耗时操作,减少启动时间

  1. 在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化;
  2. 一些数据预取放在异步线程中,可以采取Callable实现。比如——对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。
  3. 对于MainActivity活动,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的嵌套层次,考虑StubView的延迟加载策略;
  4. 当然在Activity中的onCreate、onStart、onResume方法中避免做耗时操作,尽量采用异步操作。