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

为了便于阅读, 应邀将Android App性能优化系列, 转移到掘金原创上来.
掘金的新出的"收藏集"功能能够用来作系列文集了.html

下面实例分析下App启动优化怎么作.java

1, 代码分析

以前写的Github App为例.android

由于这个App集成了Bugly, Push, Feedback等服务, 因此Application的onCreate有不少第三方平台的初始化工做...git

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);
            }
        });
    }
}复制代码

当前冷启动效果:
github

能够看到启动时白屏了很长时间.性能优化

2, Traceview上场

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

在onCreate开始和结尾打上trace.

Debug.startMethodTracing("GithubApp");
...
Debug.stopMethodTracing();复制代码

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

注意: 须要给程序加上写存储的权限:工具

 复制代码

经过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的背景:

 

  
  
  

 
    
  
  
  

 
    
  
  
  

 

    
  
  
  

 
    
  
  
  

 
        
  
  
  

 
    
复制代码

弄一个主题:

 
  
  @drawable/logo_splash 

 
复制代码

将一个什么不渲染布局的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中设置其为启动屏, 并加上主题:

 
  
  
  
  

 
      
  
  
  

 
      
  
  
  

 
  
复制代码

5, 最终的效果

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

相比以前, 呈现给用户的再也不是一个白屏了, 带上了logo, 固然这个背景要显示什么, 咱们能够根据实际状况来自定义.

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

6, 结语

照例, 总结下.
此次关于App启动时间的优化, 写了两篇. 写这么多, 仍是想传达下我的作技术的思想, 也算是我的的经验回顾, 抛砖引玉.

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

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

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

本文完整源码, 请移步Github

相关文章
相关标签/搜索