Android高性能编程(2)--延迟初始化


       上一篇文章,讲到了很多Android应用开发中需要注意的性能和内存方面的技巧。这一篇文章就是从smali指令级来分析性能优化和内存优化的问题。

    

       如何解决界面启动时间开销大的问题

   

      我们在编写Android应用的时候,很多情况下会遇到界面启动时间过长的问题,用户体验非常的不好。所以我们在编写代码的时候,一定要多加注意如何提高界面的启动时间。下面会讲到几个优化界面启动开销的技巧。


     1.类的加载开销

      当一个类的静态方法或者静态属性被调用或者类被实例化得时候,虚拟机首先做的第一件事情就是DexClassLoader将类的class文件加载到虚拟机,而加载到虚拟机的过程会触发class文件中clinit函数的执行。我们看下面一段代码

package com.example.smalidemo.foreach;

import java.util.ArrayList;
import java.util.List;

public class OnInitTest {
	public static final String INIT_STRING = "initstring_fantasy";
	public static String INIT2_STRING = "for initstring_fantasy 2";
	private String INIT3_STRING = "initstring_fantasyh 3";
	public static final int INT_1 = 100;
	public static int INIT_INT = 10000;
	public static List list = new ArrayList();
	public static final ArrayList<AppBean> mAppListOnInit = new ArrayList<AppBean>();
	public static ArrayList mAppList = null;
	private static String STRING_ARRAY[] = { "jpg", "mp5", "mp4" };
	private static final String FINAL_STRING_ARRAY[] = { "pdf", "txt",
			"exe" };
}



反编译后的smali文件

.class public Lcom/example/smalidemo/foreach/OnInitTest;
.super Ljava/lang/Object;
.source "OnInitTest.java"


# static fields
.field private static final FINAL_STRING_ARRAY:[Ljava/lang/String; = null

.field public static INIT2_STRING:Ljava/lang/String; = null

.field public static INIT_INT:I = 0x0

.field public static final INIT_STRING:Ljava/lang/String; = "initstring_fantasy"

.field public static final INT_1:I = 0x64

.field private static STRING_ARRAY:[Ljava/lang/String;

.field public static list:Ljava/util/List;

.field public static mAppList:Ljava/util/ArrayList;

.field public static final mAppListOnInit:Ljava/util/ArrayList;
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "Ljava/util/ArrayList",
            "<",
            "Lcom/example/smalidemo/foreach/AppBean;",
            ">;"
        }
    .end annotation
.end field


# instance fields
.field private INIT3_STRING:Ljava/lang/String;


# direct methods
.method static constructor <clinit>()V
    .locals 10

    .prologue
    const/16 v9, 0x2710

    const/4 v8, 0x3

    const/4 v7, 0x2

    const/4 v6, 0x1

    const/4 v5, 0x0

    .line 8
    const-string v3, "for initstring_fantasy 2"

    sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->INIT2_STRING:Ljava/lang/String;

    .line 11
    sput v9, Lcom/example/smalidemo/foreach/OnInitTest;->INIT_INT:I

    .line 12
    new-instance v3, Ljava/util/ArrayList;

    invoke-direct {v3}, Ljava/util/ArrayList;-><init>()V

    sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->list:Ljava/util/List;

    .line 13
    new-instance v3, Ljava/util/ArrayList;

    invoke-direct {v3}, Ljava/util/ArrayList;-><init>()V

    sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppListOnInit:Ljava/util/ArrayList;

    .line 14
    const/4 v3, 0x0

    sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppList:Ljava/util/ArrayList;

    .line 15
    new-array v3, v8, [Ljava/lang/String;

    const-string v4, "jpg"

    aput-object v4, v3, v5

    const-string v4, "mp5"

    aput-object v4, v3, v6

    const-string v4, "mp4"

    aput-object v4, v3, v7

    sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->STRING_ARRAY:[Ljava/lang/String;

    .line 16
    new-array v3, v8, [Ljava/lang/String;

    const-string v4, "pdf"

    aput-object v4, v3, v5

    const-string v4, "txt"

    aput-object v4, v3, v6

    .line 17
    const-string v4, "exe"

    aput-object v4, v3, v7

    .line 16
    sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->FINAL_STRING_ARRAY:[Ljava/lang/String;

    .line 20
    const/4 v1, 0x0

    .local v1, i:I
    move v2, v1

    .line 21
    .end local v1           #i:I
    .local v2, i:I
    :goto_0
    add-int/lit8 v1, v2, 0x1

    .end local v2           #i:I
    .restart local v1       #i:I
    if-lt v2, v9, :cond_0

    .line 25
    return-void

    .line 22
    :cond_0
    new-instance v0, Lcom/example/smalidemo/foreach/AppBean;

    invoke-direct {v0}, Lcom/example/smalidemo/foreach/AppBean;-><init>()V

    .line 23
    .local v0, bean:Lcom/example/smalidemo/foreach/AppBean;
    sget-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppListOnInit:Ljava/util/ArrayList;

    invoke-virtual {v3, v0}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

    move v2, v1

    .end local v1           #i:I
    .restart local v2       #i:I
    goto :goto_0
.end method
    在上面得Java文件中,我们声明了一些变量,其中包括字符串,整数,字符串数组,其中有些声明了final,一些没有声明final。我们可以看到声明final的字符串和整数,在编译后的文件中已经成为了常量,而没有声明final的变量,我们可以看到在声明的地方,仍然是类型默认值  string的默认值是null,而int的默认值是0。所以当DexClassLoader加载class文件的时候,会在<clinit>函数里面,对以上的静态变量初始化值。我们可以看到,声明的若干个静态变量,在编译后的smali文件中,成为了更多的smali指令。

     提高DexClassLoader加载class的速度,就是要提高class中<clinit>函数的执行速度。

     通过以上的分析我们可以总结出以下几个规则:

     声明静态的变量,一定要添加final的声明    (在编译器变量就被常量代替,就不会再类加载的时候消耗CPU时间)


   2.类的创建实例开销


      一个class文件中除了静态变量外,还有很多全局非静态变量。而我们在声明全局变量的时候,都会为全局变量赋值。

   

private String INIT3_STRING = "initstring_fantasyh 3";

以上这一条Java语句,在smali文件中会变成几条指令呢  我们来看一下:


# instance fields
.field private INIT3_STRING:Ljava/lang/String;

.method public constructor <init>()V
    .locals 1

    .prologue
    .line 6
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    .line 9
    const-string v0, "initstring_fantasyh 3"

    iput-object v0, p0, Lcom/example/smalidemo/foreach/OnInitTest;->INIT3_STRING:Ljava/lang/String;

    .line 6
    return-void
.end method

可以看到,在<init>函数中,对全局的非静态变量进行了初始化。class文件在创建类实例的时候,就是执行<init>函数的过程。

   提高类创建实例的过程,就是优化<init>函数执行速度的过程。我们最好的做法就是在声明全局变量的地方,赋给默认值,在函数中真正要用的时候,再进行初始化。

   

    无论我们是静态全局变量还是非静态全局变量,在类加载和实例化的过程中,都会对这些变量,进行赋值。如果我们在声明的时候赋了值,那么在init函数中赋值,否则也会执行一条赋值null或者0的指令。因此尽量少的声明全局变量是优化的一大准则。(因为只要声明了一个全局变量就会在类加载或者初始化的时候执行一条指令。)


   以上两点都做好了优化,相信能为界面的显示速度提高不少。


   但是当我们需要在代码中定义一些常量的集合或者数组的时候,如何避免这两个过程开销大的问题呢。比如在开发中,我们需要定义一个静态的全局数组。如果定义在一个在Activity的onCreate中就实例化的类中,肯定会对Activity的启动时间消耗不少。针对这种问题如何做好优化呢?我们可以将这种静态的数组重构到另一个class中,在使用数组的时候,我们可以直接调用这个类中的静态数组。这样就避免了在Activity初始化的流程中,就初始化那么多数组的问题。

   延迟初始化是我们在优化Activity启动时间的一个很有力的技巧。在不修改算法和逻辑结构的基础上,通过延迟初始化也能达到一定程度的优化。

    通过以上的分析可以总结以下几条规则:

      1.尽可能少的声明全局变量

      2.声明静态变量一定要final声明

      3.对于开销大的静态模块或者全局非静态模块,可以重构到另外一个类里,达到延迟初始化的作用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Build fast and efficient Android apps that run as reliably as clockwork in a multi-device world About This Book Wide coverage of various topics that help in developing optimal applications Explore the concepts of Advanced Native Coding in depth A must-have for professional-standard Android developers for whom performance failures and the sloppy use of resources are simply unacceptable Who This Book Is For This book is aimed at developers with an advanced knowledge of Android and who want to test their skills and learn new techniques to increase the performance of their applications. We assume they are comfortable working with the entire Android SDK, and have been doing it for a few years. They need to be familiar with frameworks such as NDK to use native code, which is crucial for app performance What You Will Learn Create Android applications that squeeze the most from the limited resource capacity of devices Swap code that isn't performing Efficient memory management by identifying problems such as leaks Reap the benefits of multithreaded and asynchronous programming Maximize the security and encryption mechanisms natively provided by Android Perform efficient network operations and techniques to retrieve data from servers Master the NDK to write native code that can perform faster operations In Detail Performant applications are one of the key drivers of success in the mobile world. Users may abandon an app if it runs slowly. Learning how to build applications that balance speed and performance with functionality and UX can be a challenge; however, it's now more important than ever to get that balance right. Android High Performance will start you thinking about how to wring the most from any hardware your app is installed on, so you can increase your reach and engagement. The book begins by providing an introduction to state–of-the-art Android techniques and the importance of performance in an Android application. Then, we will explain the Android SDK tools regularly used to debug and profile Android applications. We will also learn about some advanced topics such as building layouts, multithreading, networking, and security. Battery life is one of the biggest bottlenecks in applications; and this book will show typical examples of code that exhausts battery life, how to prevent this, and how to measure battery consumption from an application in every kind of situation to ensure your apps don't drain more than they should. This book explains techniques for building optimized and efficient systems that do not drain the battery, cause memory leaks, or slow down with time. Style and approach The book follows a tutorial-based approach to take the reader from the basic fundamentals of debugging to advanced performance-improvement concepts. Table of Contents Chapter 1: Introduction: Why High Performance? Chapter 2: Efficient Debugging Chapter 3: Building Layouts Chapter 4: Memory Chapter 5: Multithreading Chapter 6: Networking Chapter 7: Security Chapter 8: Optimizing Battery Consumption Chapter 9: Native Coding in Android Chapter 10: Performance Tips

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值