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


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

        

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

       

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

         1.类的加载开销

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

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.example.smalidemo.foreach;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.List;  
    5.   
    6. public class OnInitTest {  
    7.     public static final String INIT_STRING = "initstring_fantasy";  
    8.     public static String INIT2_STRING = "for initstring_fantasy 2";  
    9.     private String INIT3_STRING = "initstring_fantasyh 3";  
    10.     public static final int INT_1 = 100;  
    11.     public static int INIT_INT = 10000;  
    12.     public static List list = new ArrayList();  
    13.     public static final ArrayList<AppBean> mAppListOnInit = new ArrayList<AppBean>();  
    14.     public static ArrayList mAppList = null;  
    15.     private static String STRING_ARRAY[] = { "jpg", "mp5", "mp4" };  
    16.     private static final String FINAL_STRING_ARRAY[] = { "pdf", "txt",  
    17.             "exe" };  
    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. }  




    反编译后的smali文件

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. .class public Lcom/example/smalidemo/foreach/OnInitTest;  
    2. .super Ljava/lang/Object;  
    3. .source "OnInitTest.java"  
    4.   
    5.   
    6. # static fields  
    7. .field private static final FINAL_STRING_ARRAY:[Ljava/lang/String; = null  
    8.   
    9. .field public static INIT2_STRING:Ljava/lang/String; = null  
    10.   
    11. .field public static INIT_INT:I = 0x0  
    12.   
    13. .field public static final INIT_STRING:Ljava/lang/String; = "initstring_fantasy"  
    14.   
    15. .field public static final INT_1:I = 0x64  
    16.   
    17. .field private static STRING_ARRAY:[Ljava/lang/String;  
    18.   
    19. .field public static list:Ljava/util/List;  
    20.   
    21. .field public static mAppList:Ljava/util/ArrayList;  
    22.   
    23. .field public static final mAppListOnInit:Ljava/util/ArrayList;  
    24.     .annotation system Ldalvik/annotation/Signature;  
    25.         value = {  
    26.             "Ljava/util/ArrayList",  
    27.             "<",  
    28.             "Lcom/example/smalidemo/foreach/AppBean;",  
    29.             ">;"  
    30.         }  
    31.     .end annotation  
    32. .end field  
    33.   
    34.   
    35. # instance fields  
    36. .field private INIT3_STRING:Ljava/lang/String;  
    37.   
    38.   
    39. # direct methods  
    40. .method static constructor <clinit>()V  
    41.     .locals 10  
    42.   
    43.     .prologue  
    44.     const/16 v9, 0x2710  
    45.   
    46.     const/4 v8, 0x3  
    47.   
    48.     const/4 v7, 0x2  
    49.   
    50.     const/4 v6, 0x1  
    51.   
    52.     const/4 v5, 0x0  
    53.   
    54.     .line 8  
    55.     const-string v3, "for initstring_fantasy 2"  
    56.   
    57.     sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->INIT2_STRING:Ljava/lang/String;  
    58.   
    59.     .line 11  
    60.     sput v9, Lcom/example/smalidemo/foreach/OnInitTest;->INIT_INT:I  
    61.   
    62.     .line 12  
    63.     new-instance v3, Ljava/util/ArrayList;  
    64.   
    65.     invoke-direct {v3}, Ljava/util/ArrayList;-><init>()V  
    66.   
    67.     sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->list:Ljava/util/List;  
    68.   
    69.     .line 13  
    70.     new-instance v3, Ljava/util/ArrayList;  
    71.   
    72.     invoke-direct {v3}, Ljava/util/ArrayList;-><init>()V  
    73.   
    74.     sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppListOnInit:Ljava/util/ArrayList;  
    75.   
    76.     .line 14  
    77.     const/4 v3, 0x0  
    78.   
    79.     sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppList:Ljava/util/ArrayList;  
    80.   
    81.     .line 15  
    82.     new-array v3, v8, [Ljava/lang/String;  
    83.   
    84.     const-string v4, "jpg"  
    85.   
    86.     aput-object v4, v3, v5  
    87.   
    88.     const-string v4, "mp5"  
    89.   
    90.     aput-object v4, v3, v6  
    91.   
    92.     const-string v4, "mp4"  
    93.   
    94.     aput-object v4, v3, v7  
    95.   
    96.     sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->STRING_ARRAY:[Ljava/lang/String;  
    97.   
    98.     .line 16  
    99.     new-array v3, v8, [Ljava/lang/String;  
    100.   
    101.     const-string v4, "pdf"  
    102.   
    103.     aput-object v4, v3, v5  
    104.   
    105.     const-string v4, "txt"  
    106.   
    107.     aput-object v4, v3, v6  
    108.   
    109.     .line 17  
    110.     const-string v4, "exe"  
    111.   
    112.     aput-object v4, v3, v7  
    113.   
    114.     .line 16  
    115.     sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->FINAL_STRING_ARRAY:[Ljava/lang/String;  
    116.   
    117.     .line 20  
    118.     const/4 v1, 0x0  
    119.   
    120.     .local v1, i:I  
    121.     move v2, v1  
    122.   
    123.     .line 21  
    124.     .end local v1           #i:I  
    125.     .local v2, i:I  
    126.     :goto_0  
    127.     add-int/lit8 v1, v2, 0x1  
    128.   
    129.     .end local v2           #i:I  
    130.     .restart local v1       #i:I  
    131.     if-lt v2, v9, :cond_0  
    132.   
    133.     .line 25  
    134.     return-void  
    135.   
    136.     .line 22  
    137.     :cond_0  
    138.     new-instance v0, Lcom/example/smalidemo/foreach/AppBean;  
    139.   
    140.     invoke-direct {v0}, Lcom/example/smalidemo/foreach/AppBean;-><init>()V  
    141.   
    142.     .line 23  
    143.     .local v0, bean:Lcom/example/smalidemo/foreach/AppBean;  
    144.     sget-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppListOnInit:Ljava/util/ArrayList;  
    145.   
    146.     invoke-virtual {v3, v0}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z  
    147.   
    148.     move v2, v1  
    149.   
    150.     .end local v1           #i:I  
    151.     .restart local v2       #i:I  
    152.     goto :goto_0  
    153. .end method  

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

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

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

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

       2.类的创建实例开销

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

       

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private String INIT3_STRING = "initstring_fantasyh 3";  


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

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. # instance fields  
    2. .field private INIT3_STRING:Ljava/lang/String;  
    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. .method public constructor <init>()V  
    2.     .locals 1  
    3.   
    4.     .prologue  
    5.     .line 6  
    6.     invoke-direct {p0}, Ljava/lang/Object;-><init>()V  
    7.   
    8.     .line 9  
    9.     const-string v0, "initstring_fantasyh 3"  
    10.   
    11.     iput-object v0, p0, Lcom/example/smalidemo/foreach/OnInitTest;->INIT3_STRING:Ljava/lang/String;  
    12.   
    13.     .line 6  
    14.     return-void  
    15. .end method  


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

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

       

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

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

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

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

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

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

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

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

    摘自:http://blog.csdn.net/litton_van/article/details/21872677

  • 相关阅读:
    easyui dialog 中 panel-body 高度太小出现 滚动条 的原因
    VS2017 未找到编译器可执行文件 csc.exe
    苹果个人开发者账号开发证书申请
    react-native android 报错 error calling Appregistry.runApplication
    react-native android 权限问题
    安卓证书生成
    node.js定时任务 node-schedule
    node.js 发送邮件
    在移动端点击事件触发时,闪烁并且有灰色背景
    antd-mobile使用报错
  • 原文地址:https://www.cnblogs.com/antyi/p/4500713.html
Copyright © 2020-2023  润新知