美文网首页
App 启动性能优化

App 启动性能优化

作者: 石先 | 来源:发表于2017-11-06 22:04 被阅读281次

关于应用启动优化,这算是个老生常谈的话题了,相信大家对于一些基本概念应该是有一定认识了,比如启动方式的区分冷启动、热启动之类的,这篇文章主要是通过一个实例来分析各个环节需要处理的问题。下面就是直接上手环节。

明确概念

命令行: adb shell am start -W [PACKAGE-NAME]/[ACTIVITY-NAME]

有没有想过,我们启动一个只有 Empty Activity 的 App 是怎么样的,没有对比就没有伤害,那我们直接试试

冷启动 热启动

场景创建

为了说明问题,我们先来模拟一个实际开发中常见的项目环境。通常我们项目中会接入一些第三方库来支撑起应用的基础服务,有些三方库会要求在应用启动时就进行初始化。又比如应用可能会有一些日志上报和事件统计的数据要在启动时发,还有像启动时读取本地配置,页面需要预加载资源内容等等。这些呢,都是现实业务场景中经常会遇到的一些内容,如果开发只考虑业务上的实现而不考虑性能问题,那么就很有可能会出现一些不太好的用户体验。比如说用户启动应用后要长时间等待,或着页面交互时卡顿等等,这些问题在一些旧设备或低端机上面会更明显。所以说找台性能差的设备多体验下还是很有必要的,这样可以更直观的暴露问题。

为什么出现白屏
冷启动白屏持续时间可能会很长,这可是个槽糕的体验,它的启动速度是由于以下引起的:
1、Application的onCreate流程,对于大型的APP来说,通常会在这里做大量的通用组件的初始化操作;
建议:很多第三方SDK都放在Application初始化,我们可以放到用到的地方才进行初始化操作。

2、Activity的onCreate流程,特别是UI的布局与渲染操作,如果布局过于复杂很可能导致严重的启动性能问题;
建议:Activity仅初始化那些立即需要的对象,xml布局减少冗余或嵌套布局。

优化APP启动速度意义重大,启动时间过长,可能会使用户直接卸载APP。

启动耗时分析

分析启动,除了上面提到的运用 ADB 命令来看,可以通过借助 Android Studio 中的工具来分析,这里推荐结合我前面一篇文章中提到的 Android 应用性能分析工具 — CPU Profiler
来使用吧,这里不作展开了。下面直接来看看实际优化的措施。

实际优化措施

1. 设置启动页面

设置一个 Activity 作为启动页是否有必要,这要根据自身实际业务考量吧,但是提升App 的启动速度肯定是有一定帮助的,因为通常应用的Home 页肯定会复杂很多。减少启动渲染耗时,从页面交互上考虑。

public class SplashActivity extends BaseActivity {

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

        // 不设置 ContentView 可节约布局加载时间,使用主题的背景图片即可

        getWindow().getDecorView().postDelayed(new Runnable() {
            @Override
            public void run() {
                startActivity(MainActivity.class);
                       finish();
            }
        }, 1000);
    }
}

2.设置启动动画

从用户点击桌面图标到应用呈现渲染,这个过程如果只是干等着,会显得应用是在点击以后没有反应,所以及时响应用户这个点击动作就显得非常有必要了。

<activity android:name=".SplashActivity"
                android:theme="@style/AppTheme.Launcher"
                android:launchMode="singleTask"
                android:screenOrientation="portrait">
          <intent-filter>
              <action android:name="android.intent.action.MAIN"/>

              <category android:name="android.intent.category.LAUNCHER"/>
          </intent-filter>
</activity>
<activity
        android:name=".GameActivity"
        android:configChanges="keyboard|locale|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize"
        android:hardwareAccelerated="true"
        android:launchMode="singleTask"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme"
        android:windowSoftInputMode="stateHidden|adjustResize"/>
 <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="android:screenOrientation">portrait</item>
        <item name="android:windowEnableSplitTouch">false</item>
        <item name="android:splitMotionEvents">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@null</item>
        <item name="android:windowAnimationStyle">@style/Animation</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.Launcher"  parent="AppTheme">
        <item name="android:windowBackground">@drawable/launch_screens</item>
    </style>

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 顶层启动图 -->
    <item>
        <bitmap
                android:src="@drawable/splash_bg" />
    </item>

</layer-list>

效果

App 启动性能优化

3. 页面数据懒加载

关于懒加载的概念相信很多人都不会陌生,我们常常会在实际开发中接触到。这里的页面数据懒加载,其实就是为了减少用户在视觉上的等待时间,对于数据填充可以延后进行,减少用户页面切换时的等待。

 // 懒加载
public class MainActivity extends Activity {
    ....
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ....
        // 页面启动时懒加载
        this.getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                mHandler.post(mLoadingRunnable);
            }
        });
   }
}
 private Runnable mLoadingRunnable = new Runnable() {
        @Override
        public void run() {
              // 数据填充,懒加载
        }
 };

注意:如果配置上面的 Style 后发现点击桌面图标没有理解展示启动图,很有可能是配置了android:windowIsTranslucent属性导致的:

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

Application 启动异步初始化第三方库

应用中很多三方库会要求在启动时初始化,并且建议在 Application 的 onCreate 中进行初始化方法调用,这就有可能导致了启动时方法处理任务过重,影响启动速度。

一般建议把三方库进行分类,考虑优先级,把除了把部分支持库的初始化延后到实际需要时才进行,也可在 Application 中执行异步加载的方式。

下面就举例通过 IntentService 的方式来进行三方库的集中异步初始化。


/**
 * An {@link IntentService} subclass for handling asynchronous task requests in
 * a service on a separate handler thread.
 * helper methods.
 * @author baishixian
 */
public class InitializeService extends IntentService {
    /**
     *    IntentService can perform
     */
    private static final String ACTION_INIT_WHEN_APP_CREATE = "gdut.bai.service.action.INIT";

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

    /**
     * Starts this service to perform action init with the given parameters. If
     * the service is already performing a task this action will be queued.
     *
     * @see IntentService
     */
    public static void startActionInit(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)) {
                handleActionInit();
            }
        }
    }

    /**
     * Handle action Init in the provided background thread with the provided
     * parameters.
     */
    private void handleActionInit() {
        LogUtils.d("InitializeService handleActionInit begin " + System.currentTimeMillis());

        DataCacheHelper.initDataCacheHelper(this.getApplicationContext());

        String channelName = ChannelUtil.getChannelName(this.getApplicationContext());
        // App ID: 在TalkingData Game Analytics创建应用后会得到App ID。
        // 渠道 ID: 是渠道标识符,可通过不同渠道单独追踪数据。
        TalkingDataGA.init(this.getApplicationContext(), "xxxxxxxx", channelName);
     
        // AdTrack
        TalkingDataAppCpa.init(this.getApplicationContext(),"xxxxxxxx", channelName);

        // App Analytics
        TCAgent.LOG_ON=true;
        // App ID: 在TalkingData创建应用后,进入数据报表页中,在“系统设置”-“编辑应用”页面里查看App ID。
        // 渠道 ID: 是渠道标识符,可通过不同渠道单独追踪数据。
        TCAgent.init(this.getApplicationContext(), "xxxxxxxx", channelName);
        // 如果已经在AndroidManifest.xml配置了App ID和渠道ID,调用TCAgent.init(this)即可;或与AndroidManifest.xml中的对应参数保持一致。
        TCAgent.setReportUncaughtExceptions(true);

        // 开启反作弊功能
        TCAgent.setAntiCheatingEnabled(this.getApplicationContext(), true);

        // 为了提高webview场景稳定性,及时发现并解决x5相关问题,
        // 当客户端发生crash等异常情况并上报给服务器时带上x5内核相关信息
        // 以bugly日志上报为例
        CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(this.getApplicationContext());
        strategy.setCrashHandleCallback(new CrashReport.CrashHandleCallback() {
            @Override
            public Map<String, String>  onCrashHandleStart(int crashType, String errorType, String errorMessage, String errorStack) {
                LinkedHashMap<String, String> map = new LinkedHashMap<>();
                String x5CrashInfo = com.tencent.smtt.sdk.WebView.getCrashExtraMessage(InitializeService.this.getApplicationContext());
                map.put("x5crashInfo", x5CrashInfo);
                return map;
            }

            @Override
            public byte[] onCrashHandleStart2GetExtraDatas(int crashType, String errorType, String errorMessage, String errorStack) {
                try {
                    return "Extra data.".getBytes("UTF-8");
                } catch (Exception e) {
                    return null;
                }
            }
        });

        Bugly.setAppChannel(this.getApplicationContext(), channelName);
        Bugly.init(this.getApplicationContext(), "xxxxxxxx", false, strategy);

        LogUtils.d("InitializeService handleActionInit end " + System.currentTimeMillis());
    }
}

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        InitializeService.startActionInit(this);
    }
}

参考

Android应用启动优化:一种DelayLoad的实现和原理(下篇)
Android性能优化典范 - 第6季
Android Performance

相关文章

  • iOS 性能优化三

    主要讲解APP冷启动的优化 iOS 性能优化一iOS 性能优化二iOS 性能优化三 1. APP 启动的分类 冷...

  • iOS App 启动性能优化

    iOS App 启动性能优化

  • iOS性能优化 - 整理

    本文主要包含: 性能优化 - 卡顿性能优化 - 耗电优化性能优化 - APP启动优化安装包瘦身 一  性能优化 -...

  • Android系统原理

    Android性能优化(一)App启动原理分析及启动时间优化 - CSDN博客 Android性能优化(二)布局渲...

  • App优化 - ANR优化

    前言 App优化 - 需要优化哪些? App优化 - 性能分析工具 App的3种启动方式 App优化 - App启...

  • 冷启动优化

    冷启动优化主要优化两个方面 Application 性能优化 App启动页性能优化业务优化不在本章优化范围内。本章...

  • iOS性能优化篇小结(二)

    iOS性能优化篇小结(一) 4. APP启动优化 APP启动可以分为2种: 冷启动:从零开始启动APP 热启动:A...

  • APP性能优化

    一、APP启动性能优化。 APP启动主要分冷启动和热启动,主要优化冷启动。 1.尽量减少didFinishLauc...

  • APP性能优化(一)启动优化

    iOS App 启动性能优化WWDC之优化App启动速度 Instruments 包含的工具有很多,Time Pr...

  • iOS性能优化-APP启动

    前言:本文旨在介绍iOS性能优化中有关APP启动流程的介绍和优化。 一、APP启动流程 1、APP的冷启动流程 点...

网友评论

      本文标题:App 启动性能优化

      本文链接:https://www.haomeiwen.com/subject/awsrmxtx.html