冷启动

介绍

冷启动指用户点APP图标至启动页调onCreate()方法阶段。

打开一Activity,若该Activity所属Application没启动,则系统为该Activity创建一进程(即zygote进程首先创建一新进程运行APP)。每创建一进程调一次Application,故Application之onCreate()方法可能被调多次。进程创建和初始化势必消耗时间,该阶段内WindowManager先加载APP主题样式中窗口背景windowBackground作为预览元素,即Preview Window(预览窗口),然后加载真正布局。该时间过长且默认背景黑或白色便给用户造成错觉,APP卡顿不流畅,影响用户体验。

思路

点APP图标后所见黑或白屏是界面渲染前第一帧,故对Theme中windowBackground设置即可。

方案一

窗口背景Logo

<!--窗口背景-->
<item name="android:windowBackground">@drawable/splash_page</item>

方案二

窗口背景透明,用户点APP图标后不立即进入APP而桌面停留片刻。其实此时APP已启动,只是Theme中windowBackground透明。

<!--窗口背景-->
<item name="android:windowBackground">@color/transparent</item>
<!--窗口透明-->
<item name="android:windowIsTranslucent">true</item>

方案三

禁止加载Preview Window

<item name="android:windowDisablePreview">true</item>

方案四

窗口背景设能解析出图片资源的XML文件
drawable

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque">
    <item>
        <!--android:gravity="center"仅显图中部分-->
        <bitmap
            android:gravity="fill"
            android:src="@drawable/splash" />
    </item>
</layer-list>

styles

<resources>
    <!-- Base Application Theme -->
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <!--去除ActionBar-->
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <!--标题栏颜色-->
        <item name="colorPrimary">@color/colorPrimary</item>
        <!--状态栏颜色 5.0+有效-->
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <!--控件选中颜色-->
        <item name="colorAccent">@color/colorPrimaryDark</item>
        <!--窗口背景-->
        <!--<item name="android:windowBackground">@drawable/preview_window</item>-->
        <!--窗口透明-->
        <!--<item name="android:windowIsTranslucent">true</item>-->
        <!--页面切换动画-->
        <item name="android:windowAnimationStyle">@style/AnimationActivity</item>
        <!--取消字体默认大写-->
        <item name="android:textAllCaps">false</item>
        <!--更改系统状态栏字体颜色 6.0+-->
        <!--<item name="android:windowLightStatusBar">true</item>-->
        <item name="android:windowDisablePreview">true</item>
    </style>

<style name="AppTheme" parent="AppBaseTheme"></style>

<style name="AnimationActivity" parent="@android:style/Animation.Activity">
    <item name="android:activityOpenEnterAnimation">@anim/slide_in_left</item>
    <item name="android:activityOpenExitAnimation">@anim/slide_out_left</item>
    <item name="android:activityCloseEnterAnimation">@anim/slide_in_right</item>
    <item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
</style>

<style name="Theme.Splash" parent="AppTheme">
    <item name="android:windowBackground">@drawable/preview_window</item>
    <item name="android:windowFullscreen">true</item>
</style>

清单文件

<activity
    android:name=".SplashActivity"
    android:theme="@style/Theme.Splash">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

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

布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/ivBack"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <ImageView
        android:id="@+id/ivTarget"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/splash_icon" />
</RelativeLayout>

主代码

package com.self.zsp.rd;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.ViewPropertyAnimation;

import base.BaseActivity;

/**
 * @decs: 闪屏
 * @date: 2017/11/6 17:16
 * @version: v 1.0
 */
public class SplashActivity extends BaseActivity {
    /**
     * 控件
     */
    private ImageView imgBack;
    private ImageView ivTarget;
    /**
     * 属性动画
     */
    ViewPropertyAnimation.Animator animator = new ViewPropertyAnimation.Animator() {
        @Override
        public void animate(View view) {
            view.setAlpha(0f);
            ObjectAnimator objAnimator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
            objAnimator.setDuration(2000);
            objAnimator.start();
        }
    };

    @Override
    protected void initContentView() {
        // 处理PreviewWindow背景图,避免图片一直占用内存,过渡绘制
        getWindow().setBackgroundDrawable(null);
        // 初始化状态栏
        initStatus();
        setContentView(R.layout.activity_splash);
    }

    @Override
    protected void stepUI() {
        imgBack = findViewById(R.id.ivBack);
        ivTarget = findViewById(R.id.ivTarget);
    }

    @Override
    protected void initConfiguration() {

    }

    @Override
    protected void initData() {

    }

    @Override
    protected void startLogic() {
        ivTarget.post(new Runnable() {
            @Override
            public void run() {
                Glide.with(SplashActivity.this).load(R.drawable.splash).animate(animator).into(imgBack);
                startAnimation();
            }
        });
    }

    @Override
    protected void setListener() {

    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
    }

    /**
     * 初始化状态栏
     */
    private void initStatus() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            View decoderView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decoderView.setSystemUiVisibility(option);
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        } else {
            WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
            localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
        }
    }

    /**
     * 动画执行
     */
    private void startAnimation() {
        int imgHeight = ivTarget.getHeight() / 5;
        int height = getWindowManager().getDefaultDisplay().getHeight();
        int dy = (height - imgHeight) / 2;
        AnimatorSet set = new AnimatorSet();
        ObjectAnimator animatorTranslate = ObjectAnimator.ofFloat(ivTarget, "translationY", 0, dy);
        ObjectAnimator animatorScaleX = ObjectAnimator.ofFloat(ivTarget, "ScaleX", 1f, 0.2f);
        ObjectAnimator animatorScaleY = ObjectAnimator.ofFloat(ivTarget, "ScaleY", 1f, 0.2f);
        ObjectAnimator animatorAlpha = ObjectAnimator.ofFloat(ivTarget, "alpha", 1f, 0.5f);
        set.play(animatorTranslate).with(animatorScaleX).with(animatorScaleY).with(animatorAlpha);
        set.setDuration(1200);
        set.setInterpolator(new AccelerateInterpolator());
        set.start();
        set.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                ivTarget.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(new Intent(SplashActivity.this, LoginActivity.class));
                        SplashActivity.this.finish();
                    }
                }, 3000);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }
}

优化

如上方法可实现APP秒开效果但非真实速度。Activity创建过程会经一系列Framework层操作,日常开发会重写Application类,然后在Application进行一些初始化操作,比如存放用户标识的静态化TOKEN、第三方SDK初始化等。

  • 尽量不让Application参与业务逻辑操作
  • 尽量不在Application中进行耗时操作。比如查询一系列文件夹或文件,这些I/O操作应在必要使用时创建或利用数据库操作。
  • 尽量不以静态变量方式在Application保存数据等。
  • 尽量减少布局复杂性、布局深度,最好不超三层嵌套。因在View绘制过程中测量相当耗费性能。

注意

  1. 预览窗口全屏则android:windowBackgroundandroid:windowFullscreen结合使用,即剥离并设给SplashActivity。如下:

    <style name="Theme.Splash" parent="AppTheme">
        <item name="android:windowBackground">@drawable/preview_window</item>
        <item name="android:windowFullscreen">true</item>
    </style>
    <activity
        android:name=".SplashActivity"
        android:theme="@style/Theme.Splash">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
    
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
  2. Preview Window背景图不做处理会致图一直存于内存并过渡绘制,故进入欢迎页时需把背景图置空或将主题重置默认。如下:

    getWindow().setBackgroundDrawable(null);
    setTheme(R.style.AppTheme);

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

snpmyn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值