前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android App优化之提升你的App启动速度之实例挑战

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

作者头像
用户2802329
发布2018-08-07 14:33:36
5480
发布2018-08-07 14:33:36
举报
文章被收录于专栏:Android先生Android先生
作者:anly_jun

地址:http://www.jianshu.com/p/4f10c9a10ac9

声明:本文是anly_jun原创,已获其授权发布,未经原作者允许请勿转载

以之前写的Github App:https://github.com/mingjunli/GithubApp为例.

代码分析

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

代码语言:javascript
复制
public class GithubApplication extends MultiDexApplication {

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

        
        AppLog.init();

        
        CrashHelper.init(this);

        
        PushPlatform.init(this);

        
        FeedbackPlatform.init(this);

        
        SharePlatform.init(this);

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

当前冷启动效果:

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

Traceview上场

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

在onCreate开始和结尾打上trace.

代码语言:javascript
复制
Debug.startMethodTracing("GithubApp");
...
Debug.stopMethodTracing();

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

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

代码语言:javascript
复制
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

通过adb pull将其导出到本地

代码语言:javascript
复制
adb pull /sdcard/GithubApp.trace ~/temp

广告: adb的众多用法, 可以参考我的另一篇文:http://www.jianshu.com/p/5980c8c282ef

打开DDMS分析trace文件

分析trace文件

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

调整Application onCreate再试

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

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

InitializeService.java代码如下:

代码语言:javascript
复制
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;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());

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

        
        CrashHelper.init(this.getApplicationContext());

        
        PushPlatform.init(this.getApplicationContext());

        
        FeedbackPlatform.init(this.getApplication());

        
        SharePlatform.init(this.getApplicationContext());

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

GithubApplication的onCreate改成:

代码语言:javascript
复制
public class GithubApplication extends MultiDexApplication {

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

        
        AppLog.init();

        InitializeService.start(this);
    }
}

看看现在的效果:

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

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

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

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

怎么做呢?

给Window加上背景

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

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

做一个logo_splash的背景:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
    <item android:drawable="@color/white" />
    
    
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/ic_github" />
    </item></layer-list>

弄一个主题:

代码语言:javascript
复制
<style name="SplashTheme" parent="AppTheme">
    <item name="android:windowBackground">@drawable/logo_splash</item></style>

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

写一个什么都不做的LogoSplashActivity.

代码语言:javascript
复制
public class LogoSplashActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        
        
        
        if (AppPref.isFirstRunning(this)) {
            IntroduceActivity.launch(this);
        }
        else {
            MainActivity.launch(this);
        }
        finish();
    }
}

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

代码语言:javascript
复制
<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>

最终的效果

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

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

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

结语

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

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

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

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

本文完整源码, 请移步Github:https://github.com/mingjunli/GithubApp

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android先生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 在onCreate开始和结尾打上trace.
  • 通过adb pull将其导出到本地
  • 打开DDMS分析trace文件
  • 分析trace文件
  • 给Window加上背景
  • 将一个什么不渲染布局的Activity作为启动屏
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档