Android性能优化之提高App启动速度和Splash页面设计

原创 2017年01月03日 19:54:44

1 App的启动

1.1 启动方式

通常来说,在安卓中应用的启动方式分为两种:冷启动和热启动。

(1)冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动。
(2)热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。

1.2 特点

(1)冷启动:冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。

(2)热启动:热启动因为会从已有的进程中来启动,所以热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化Application,因为一个应用从新进程的创建到进程的销毁,Application只会初始化一次。

上面说的启动是点击app的启动图标来启动的,而另外一种方式是进入最近使用的列表界面来启动应用,这种不应该叫启动,应该叫恢复。

1.3 应用启动的流程

这里写图片描述

由图分析可总结Activity大概启动流程:启动虚拟机—>启动AMS —>通过Zygote创建ApplicationProcess进程–>Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上。

2 测量一个应用的启动时间

2.1 测量方法

adb shell am start -W [PackageName]/[PackageName.MainActivity]

执行成功后将返回三个测量到的时间:
(1)ThisTime:指当前指定的MainActivity的启动时间
(2)TotalTime:整个应用的启动时间,Application+Activity的使用的时间。
(3)WaitTime:一般比TotalTime大点,包括系统影响的耗时

2.2 测量结果

adb shell am start -W com.guesslive.caixiangji/com.guesslive.caixiangji.activity.MainActivity

(1)优化前

Activity: com.guesslive.caixiangji/.activity.MainActivity
ThisTime: 1363
TotalTime: 1363
WaitTime: 1391
Complete

(2)Application优化后

Activity: com.guesslive.caixiangji/.activity.BootActivity
ThisTime: 521
TotalTime: 521
WaitTime: 547
Complete

(3)MainActivity&SplashFragment 优化后

Activity: com.guesslive.caixiangji/.activity.MainActivity
ThisTime: 340
TotalTime: 340
WaitTime: 366
Complete

3 减少应用启动时的耗时

主要是在Application初始化 + MainActivity的界面加载绘制时间。
(1)不要在Application的构造方法、attachBaseContext()、onCreate()里面进行初始化耗时操作。使用IntentService代替初始化工作。
(2)MainActivity,由于用户只关心最后的显示的这一帧,对我们的布局的层次要求要减少,自定义控件的话测量、布局、绘制的时间。不要在onCreate、onStart、onResume当中做耗时操作。
(3)对于SharedPreference的初始化。因为他初始化的时候是需要将数据全部读取出来放到内存当中。
(4)SplashActivity和MainActivity合为一个优化。
(5)合理使用延迟加载DelayLoad。达到效果:应用已经启动并加载完成,界面已经显示出来了,然后我们再去做其他的事情

4 综合方案

4.1 Application

public class MainApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        InitializeService.start(this);//启动服务执行耗时操作
    }
}
/**
 * 启动初始化Service
 */
public class InitializeService extends IntentService {

    // IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
    public static final String ACTION_INIT_WHEN_APP_CREATE = "com.guesslive.caixiangji.service.action.app.create";
    public static final String EXTRA_PARAM = "com.guesslive.caixiangji.service.extra.PARAM";

    public InitializeService() {
        super("InitializeService");
    }

    /**
     * 启动调用
     * @param context
     */
    public static void start(Context context) {
        Intent intent = new Intent(context, InitializeService.class);
        intent.setAction(ACTION_INIT_WHEN_APP_CREATE);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
                performInit(EXTRA_PARAM);
            }
        }
    }

    /**
     * 启动初始化操作
     */
    private void performInit(String param) {
        initImageLoader();//初始化图片加载控件
        initRealm();//初始化Realm数据库
        initUser();//初始化用户(Realm数据库)
        initPush();//初始化推送
        initTuSdk();//初始化图sdk
        initQiNiu();//初始化七牛
        initQiyu();//网易七鱼
        initLogger();//注释启动,打开屏蔽打印
    }
}

4.2 MainActivity&SplashFragment

(1)MainActivity.java

public class MainActivity extends FragmentActivity {

    private Handler mHandler = new Handler();
    private SplashFragment splashFragment;
    private ViewStub viewStub;

    private ImageView iv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewStub = (ViewStub)findViewById(R.id.content_viewstub);

        //首先加载并显示splash页面
        splashFragment = new SplashFragment();
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(R.id.frame, splashFragment);
        transaction.commit();

        //1.判断当窗体加载完毕的时候,立马再加载真正的布局进来
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                // 开启延迟加载
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        viewStub.inflate();//将viewstub加载进来,记载完毕后控件使用正常

//                      iv = (ImageView) findViewById(R.id.iv);
                    }
                } );
            }
        });

        //2.判断当窗体加载完毕的时候执行,延迟一段时间做动画。
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                // 开启延迟加载,实现fragment里面的动画效果
                mHandler.postDelayed(new DelayRunnable(MainActivity.this, splashFragment) ,2000);
//              mHandler.post(new DelayRunnable());//不开启延迟加载
            }
        });

        //3.同时进行异步加载数据


    }

    static class DelayRunnable implements Runnable{
        private WeakReference<Context> contextRef;
        private WeakReference<SplashFragment> fragmentRef;

        public DelayRunnable(Context context, SplashFragment f) {
            contextRef = new WeakReference<Context>(context);
            fragmentRef = new WeakReference<SplashFragment>(f);
        }

        @Override
        public void run() {
            // 移除splash页面
            if(contextRef!=null){
                SplashFragment splashFragment = fragmentRef.get();
                if(splashFragment==null){
                    return;
                }
                FragmentActivity activity = (FragmentActivity) contextRef.get();
                FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
                transaction.remove(splashFragment);
                transaction.commit();
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
    }
}

(2)activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.applicationstartoptimizedemo.MainActivity" >

    <ViewStub 
        android:id="@+id/content_viewstub"
        android:layout="@layout/activity_main_viewstub"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:id="@+id/frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <!-- android:background="@mipmap/bg_boot" 设置背景图-->
    </FrameLayout>

</RelativeLayout>

(3)activity_main_viewstub.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ProgressBar
        android:id="@+id/progressBar1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitStart"
        android:src="@drawable/content" />

</FrameLayout>

(4)SplashFragment.java

public class SplashFragment extends Fragment {

    @Override
    @Nullable
    public View onCreateView(LayoutInflater inflater,
            @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_splash, container,false);
    }
}

4.3 给Window加上背景避免白屏

(1)bg_splash.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 底层白色 -->
    <item android:drawable="@color/white" />

    <!-- 顶层Logo居中 -->
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/ic_github" />
    </item>
</layer-list>

(2)SplashTheme

<style name="SplashTheme" parent="AppTheme">
        <item name="android:windowBackground">@drawable/bg_splash</item>
</style>

(3)ActivityManifest.xml

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

    <application
        android:name=".application.MainApplication"
        android:allowBackup="true"
        android:hardwareAccelerated="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"  
        android:theme="@style/AppTheme">

        <activity
            android:name=".activity.MainActivity"
            android:screenOrientation="portrait"
            android:theme="@style/SplashTheme">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service
            android:name=".service.InitializeService"
            android:exported="false"/>

    </application>

</manifest>

5 结语

实际场景可能远比这个复杂,这只是其中一种分析思路,还有其他思路欢迎私信。

6 参考链接

Android App优化之提升你的App启动速度之实例挑战

Android优化应用启动速度

版权声明: 举报

相关文章推荐

android 打开app先显示欢迎界面后自动跳到主界面

public class WelcomeActivity extends Activity{ private final long SPLASH_LENGTH = 2000; ...

android启动界面的两种方式

希望大家多多指教,共同学习

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

android开发:正确的开发一个Splash页面

虽然是一个小小的splash页面,可能有人说在里面写个线程一段时间结束就好了哈。 为啥我要写一篇文章介绍一下呢?如下解释:我在之前看到的一些splash页面的代码看到了很多有问题的写法。运行的不完美...

带你重新认识:Android Splash页秒开 Activity白屏 Activity黑屏

严振杰带你重新认识:Android Splash页秒开,如何解决Activity白屏,如何解决Activity黑屏。本篇博客要剖析和解决的两个问题: 1. APP启动时白屏/黑屏、Activity打开...

Android性能优化之Splash页应该这样设计

目前SplashActivity的设计目前市场上的应用在启动时基本上都会先启动一个SplashActivity,作为一个欢迎界面,为什么这样设计呢? 个人总结有三个优点:1、可以给用户更好的体验比如...

Android实战技术:Splash的实现

什么是Splash Splash也就是应用程序启动之前先启动一个画面,上面简单的介绍应用程序的厂商,厂商的LOGO,名称和版本等信息,多为一张图片,显示几秒钟后会自动消息,然后显示出应用程序的主体页...

Androidの应用引导页SplashActivity的巧妙之处

Androidの应用引导页SplashActivity的巧妙之处(一) 1.众所周知,我们很多应用都会有一个SplashActivity,用来当作进入应用的第一个过度界面,显示一个logo信息。 ...

Android App 启动页(Splash)黑/白闪屏现象产生原因与解决办法

惊鸿一瞥微信的启动页,相信大家都不陌生。 不知道大家有没有发现一个现象,微信每次启动的时候,是直接进入这个启动页面。 我的意思是,很多应用,往往会先白屏停顿一下后再进入启动页面(Splash)。为...

Gradle配置及同一应用不同版本配置不同资源文件,不同签名,包名进行打包

公司的项目,一个项目也就是同一套代码要针对多个客户打出多个版本的apk,不同版本的apk中一些代码是不同的,比如服务器的地址,app的名字,图片资源以及签名文件等都是不同的。刚开始的时候,把这些相同的...

android studio中build.gradle文件让人秒懂的解释

android studio中build.gradle文件让人秒懂的分析     嗨,大四实习生我又来了,最近新的一期项目还未正式启动。小菜鸟我整整研究了两天时间研究了下一直都搞不懂的build.gr...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)