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

1, 代码分析

之前写的Github App为例.

因为这个App集成了Bugly, Push, Feedback等服务, 所以Application的onCreate有很多第三方平台的初始化工作…

public class GithubApplication extends MultiDexApplication {

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

        // init logger.
        AppLog.init();

        // init crash helper
        CrashHelper.init(this);

        // init Push
        PushPlatform.init(this);

        // init Feedback
        FeedbackPlatform.init(this);

        // init Share
        SharePlatform.init(this);

        // init Drawer image loader
        DrawerImageLoader.init(new AbstractDrawerImageLoader() {
            @Override
            public void set(ImageView imageView, Uri uri, Drawable placeholder) {
                ImageLoader.loadWithCircle(GithubApplication.this, uri, imageView);
            }
        });
    }
}

当前冷启动效果:

可以看到启动时白屏了很长时间.

2, Traceview上场

接下来我们结合我们上文的理论知识, 和介绍的Traceview工具, 来分析下Application的onCreate耗时.

在onCreate开始和结尾打上trace.

Debug.startMethodTracing("GithubApp");
...
Debug.stopMethodTracing();

运行程序, 会在sdcard上生成一个”GithubApp.trace”的文件.

注意: 需要给程序加上写存储的权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

通过adb pull将其导出到本地

adb pull /sdcard/GithubApp.trace ~/temp

广告: adb的众多用法, 可以参考我的另一篇文

打开DDMS分析trace文件

分析trace文件

  1. 在下方的方法区点击”Real Time/Call”, 按照方法每次调用耗时降序排.
  2. 耗时超过500ms都是值得注意的.
  3. 看左边的方法名, 可以看到耗时大户就是我们用的几大平台的初始化方法, 特别是Bugly, 还加载native的lib, 用ZipFile操作等.
  4. 点击每个方法, 可以看到其父方法(调用它的)和它的所有子方法(它调用的).
  5. 点击方法时, 上方的该方法执行时间轴会闪动, 可以看该方法的执行线程及相对时长.

3, 调整Application onCreate再试

既然已经知道了哪些地方耗时长, 我们不妨调整下Application的onCreate实现, 一般来说我们可以将这些初始化放在一个单独的线程中处理, 为了方便今后管理, 这里我用了一个InitializeService的IntentService来做初始化工作.

明确一点, IntentService不同于Service, 它是工作在后台线程的.

InitializeService.java代码如下:

package com.anly.githubapp.compz.service;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.ImageView;

import com.anly.githubapp.common.wrapper.AppLog;
import com.anly.githubapp.common.wrapper.CrashHelper;
import com.anly.githubapp.common.wrapper.FeedbackPlatform;
import com.anly.githubapp.common.wrapper.ImageLoader;
import com.anly.githubapp.common.wrapper.PushPlatform;
import com.anly.githubapp.common.wrapper.SharePlatform;
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader;
import com.mikepenz.materialdrawer.util.DrawerImageLoader;

/**
 * Created by mingjun on 16/8/25.
 */
public class InitializeService extends IntentService {

    private static final String ACTION_INIT_WHEN_APP_CREATE = "com.anly.githubapp.service.action.INIT";

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

    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();
            }
        }
    }

    private void performInit() {
        AppLog.d("performInit begin:" + System.currentTimeMillis());

        // init Drawer image loader
        DrawerImageLoader.init(new AbstractDrawerImageLoader() {
            @Override
            public void set(ImageView imageView, Uri uri, Drawable placeholder) {
                ImageLoader.loadWithCircle(getApplicationContext(), uri, imageView);
            }
        });

        // init crash helper
        CrashHelper.init(this.getApplicationContext());

        // init Push
        PushPlatform.init(this.getApplicationContext());

        // init Feedback
        FeedbackPlatform.init(this.getApplication());

        // init Share
        SharePlatform.init(this.getApplicationContext());

        AppLog.d("performInit end:" + System.currentTimeMillis());
    }
}

GithubApplication的onCreate改成:

public class GithubApplication extends MultiDexApplication {

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

        // init logger.
        AppLog.init();

        InitializeService.start(this);
    }
}

看看现在的效果:

可以看到提升了很多, 然后还有一点瑕疵, 就是起来的时候会有一个白屏, 如果手机较慢的话, 这个白屏就会持续一段时间, 不太友好.

那么还有没有什么办法优化呢?

4, 给我们的应用窗口弄一个PlaceHolder

Android最新的Material Design有这么个建议的. 建议我们使用一个placeholder UI来展示给用户直至App加载完毕.

怎么做呢?

给Window加上背景

如第3节所言, 当App没有完全起来时, 屏幕会一直显示一块空白的窗口(一般来说是黑屏或者白屏, 根据App主题).

前文理论基础有说到, 这个空白的窗口展示跟主题相关, 那么我们是不是可以从首屏的主题入手呢? 恰好有一个windowBackground的主题属性, 我们来给Splash界面加上一个主题, 带上我们想要展示的背景.

做一个logo_splash的背景:

<?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>

弄一个主题:

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

将一个什么不渲染布局的Activity作为启动屏

写一个什么都不做的LogoSplashActivity.

public class LogoSplashActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 注意, 这里并没有setContentView, 单纯只是用来跳转到相应的Activity.
        // 目的是减少首屏渲染

        if (AppPref.isFirstRunning(this)) {
            IntroduceActivity.launch(this);
        }
        else {
            MainActivity.launch(this);
        }
        finish();
    }
}

在AndroidManifest.xml中设置其为启动屏, 并加上主题:

<activity
  android:name=".ui.module.main.LogoSplashActivity"
  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>

5, 最终的效果

让我们来看下最终的效果:

相比之前, 呈现给用户的不再是一个白屏了, 带上了logo, 当然这个背景要显示什么, 我们可以根据实际情况来自定义.

这种优化, 对于有些Application内的初始化工作不能移到子线程做的情况, 是非常友好的. 可以避免我们的App长时间的呈现给用户一个空白的窗口.

6, 结语

照例, 总结下.

这次关于App启动时间的优化, 写了两篇. 写这么多, 还是想传达下个人做技术的思想, 也算是个人的经验回顾, 抛砖引玉.

实际场景可能远比这个复杂,在此更多的提供一种分析思路~欢迎扩展

矫情了, 还是总结下本文相关的吧:

  1. Application的onCreate中不要做太多事情.
  2. 首屏Activity尽量简化.
  3. 善用工具分析.
  4. 多阅读官方文档, 很多地方貌似无关, 实际有关联, 例如这次就用了Material Design文档中的解决方案.

本文完整源码, 请移步Github

时间: 2024-10-10 16:44:35

App优化之提升你的App启动速度之实例挑战的相关文章

Android App 优化之消除卡顿

转载:http://gold.xitu.io/post/582583328ac247004f3ab124 1, 感知卡顿 用户对卡顿的感知, 主要来源于界面的刷新. 而界面的性能主要是依赖于设备的UI渲染性能. 如果我们的UI设计过于复杂, 或是实现不够好, 设备又不给力, 界面就会像卡住了一样, 给用户卡顿的感觉. 1.1 16ms原则 在剖析卡顿的原因之前, 我们先来了解下Android中著名的"16ms"原则: Android系统每隔16ms会发出VSYNC信号重绘我们的界面(A

Android App优化之ANR详解

引言 背景:Android App优化, 要怎么做? Android App优化之性能分析工具 Android App优化之提升你的App启动速度之理论基础 Android App优化之提升你的App启动速度之实例挑战 Android App优化之Layout怎么摆 Android App优化之ANR详解 Android App优化之消除卡顿 Android App优化之内存优化 Android App优化之持久电量 Android App优化之如何高效网络请求 App优化系列已近中期, 前面分

0428数字口袋精灵app优化

"数字口袋精灵app"优化 目录: 一.项目github总仓库推送 二.开发成员 三.分工与合作 四.各模块成果 五.团队成员贡献分 内容: 一.项目github总仓库: https://github.com/LiangZhilin/We-have-bing 二.开发人员: 223孙海林(商软2班) 225江志彬(商软2班) 256林集团(商软2班) 224颜文生(商软2班) 404曹嘉琪(商软1班) 142梁植淋(商软1班) 三.分工与合作: 林集团.颜文生:从开发者的角度出发,进一

Android App优化之延长电池续航时间

禁用广播接收器 确保广播接收器在真正需要时才执行指令,在onResume中其中广播接收器,在onPause中禁用, 在manifest文件中声明广播接收器时,事先默认配置成禁用的 <receiver android:name=".BatterReceiver" android:enabled="false"> 网络 1. 后台数据,后台传输数据时,判断ConnectivityManager的getBackgroundDataSetting()的返回值,如

Android性能优化系列---管理你的app内存

 文章出处:http://developer.android.com/training/articles/memory.html#YourApp Random-access memory(RAM)在任何软件开发环境都是稀有资源,在移动操作系统物理内存有限的情况下将显得更加珍贵.虽然Android的Dalvik虚拟机优化了内存回收机制,但我们也要关注你的app的内存分配合和释放 为了垃圾回收器能回收你系统的内存,你应该避免引起内存泄露(通常由全局成员hold了对象引用),而且要在合适的时间点(如生

柔弱的APP如何自我保护,浅谈APP防御手段,使用360加固助手加固/签名/多渠道打包/应用市场发布

柔弱的APP如何自我保护,浅谈APP防御手段,使用360加固助手加固/签名/多渠道打包/应用市场发布 由于JAVA和Android的平台型,所以APP很容易被反编译,这对于我们开发者来说,是一个不想要的结果,对于用户来说,就是一个噩耗,而安全性,一直是我们关注的焦点,今天,我们来聊聊这个安全性,和一起玩玩Apk加固! 一.我们为什么要提高APP的安全性 手机已经是不会离开身边了,APP更是重中之重的环节,我们衣食住行,基本上大部分都是靠APP来完成的,这样的话,APP的安全就是一个很大的挑战了,

产品经理干货,APP建摸——一套描述app的方法论

需求到原型是跳跃的,本文重点论述中间被忽略的过渡层,提出一套明确的方法论.首先描述"实体",然后描述实体之间的关系,最后描述实体的组织呈现.这样就能非常深刻的去理解一个app了. 引子一: 一个产品的生命周期中需要产出MRD(市场需求文档),然后根据市场需求文档画出产品原型图并输出PRD(产品需求文档).在许多产品经理教程或者书籍里,大家肯定没少看到与需求.原型相关的论述,不过在工作实践中往往会感觉"从需求到原型"还是跳跃的,仅仅明确需求就去构造产品原型会不清晰,中

HBuilder开发App教程01-推开前端开发App的大门

奇妙的前端,奇妙的js 众所周知,自从js有nodejs后,前端人员可以华丽的转身,去开发高并发非阻塞的服务端程序, 随着html5的出现,伴随一些amazing的特性,h5开发app的技术越发的成熟, 自从2008年国外推出phonegap后,前端人员又踏上开发app的路程, 今年来国内也对应推出了一些可以让前端人员开发app的ide, 不过大部分是收费的,强定制的,这里就不一一列举了. 这里推荐DCloud推出的HBuilder,一句话谁用谁知道. 一次开发两种app 通过HBuilder的

app开发历程---1,servlet 返回JSON作为android 接口实例

最近公司领导要做app,虽然以前自己是做app的测试的,但是好多东西都不是很明白,这里记录自己这段日子的历程. 1.搭建服务器端,以前做测试的时候,他们用的是Apache+mysql+php,而自己上学的时候接触过一些java的知识,就用java+servlet+jsp+Sqlserver+tomcat来做. 2.由于自己好长时间没有写代码了,所有写都是比较基础的例子.下面这个是根据百度文库来实现的android解析JSON数据的实例. 首先服务器端: 1 <?xml version="1