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

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

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

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

1.类的加载开销

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

[cpp] view plaincopy

  1. package com.example.smalidemo.foreach;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. public class OnInitTest {
  5. public static final String INIT_STRING = "initstring_fantasy";
  6. public static String INIT2_STRING = "for initstring_fantasy 2";
  7. private String INIT3_STRING = "initstring_fantasyh 3";
  8. public static final int INT_1 = 100;
  9. public static int INIT_INT = 10000;
  10. public static List list = new ArrayList();
  11. public static final ArrayList<AppBean> mAppListOnInit = new ArrayList<AppBean>();
  12. public static ArrayList mAppList = null;
  13. private static String STRING_ARRAY[] = { "jpg", "mp5", "mp4" };
  14. private static final String FINAL_STRING_ARRAY[] = { "pdf", "txt",
  15. "exe" };

[cpp] view plaincopy

  1. }

反编译后的smali文件

[html] view plaincopy

  1. .class public Lcom/example/smalidemo/foreach/OnInitTest;
  2. .super Ljava/lang/Object;
  3. .source "OnInitTest.java"
  4. # static fields
  5. .field private static final FINAL_STRING_ARRAY:[Ljava/lang/String; = null
  6. .field public static INIT2_STRING:Ljava/lang/String; = null
  7. .field public static INIT_INT:I = 0x0
  8. .field public static final INIT_STRING:Ljava/lang/String; = "initstring_fantasy"
  9. .field public static final INT_1:I = 0x64
  10. .field private static STRING_ARRAY:[Ljava/lang/String;
  11. .field public static list:Ljava/util/List;
  12. .field public static mAppList:Ljava/util/ArrayList;
  13. .field public static final mAppListOnInit:Ljava/util/ArrayList;
  14. .annotation system Ldalvik/annotation/Signature;
  15. value = {
  16. "Ljava/util/ArrayList",
  17. "<",
  18. "Lcom/example/smalidemo/foreach/AppBean;",
  19. ">;"
  20. }
  21. .end annotation
  22. .end field
  23. # instance fields
  24. .field private INIT3_STRING:Ljava/lang/String;
  25. # direct methods
  26. .method static constructor <clinit>()V
  27. .locals 10
  28. .prologue
  29. const/16 v9, 0x2710
  30. const/4 v8, 0x3
  31. const/4 v7, 0x2
  32. const/4 v6, 0x1
  33. const/4 v5, 0x0
  34. .line 8
  35. const-string v3, "for initstring_fantasy 2"
  36. sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->INIT2_STRING:Ljava/lang/String;
  37. .line 11
  38. sput v9, Lcom/example/smalidemo/foreach/OnInitTest;->INIT_INT:I
  39. .line 12
  40. new-instance v3, Ljava/util/ArrayList;
  41. invoke-direct {v3}, Ljava/util/ArrayList;-><init>()V
  42. sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->list:Ljava/util/List;
  43. .line 13
  44. new-instance v3, Ljava/util/ArrayList;
  45. invoke-direct {v3}, Ljava/util/ArrayList;-><init>()V
  46. sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppListOnInit:Ljava/util/ArrayList;
  47. .line 14
  48. const/4 v3, 0x0
  49. sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppList:Ljava/util/ArrayList;
  50. .line 15
  51. new-array v3, v8, [Ljava/lang/String;
  52. const-string v4, "jpg"
  53. aput-object v4, v3, v5
  54. const-string v4, "mp5"
  55. aput-object v4, v3, v6
  56. const-string v4, "mp4"
  57. aput-object v4, v3, v7
  58. sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->STRING_ARRAY:[Ljava/lang/String;
  59. .line 16
  60. new-array v3, v8, [Ljava/lang/String;
  61. const-string v4, "pdf"
  62. aput-object v4, v3, v5
  63. const-string v4, "txt"
  64. aput-object v4, v3, v6
  65. .line 17
  66. const-string v4, "exe"
  67. aput-object v4, v3, v7
  68. .line 16
  69. sput-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->FINAL_STRING_ARRAY:[Ljava/lang/String;
  70. .line 20
  71. const/4 v1, 0x0
  72. .local v1, i:I
  73. move v2, v1
  74. .line 21
  75. .end local v1           #i:I
  76. .local v2, i:I
  77. :goto_0
  78. add-int/lit8 v1, v2, 0x1
  79. .end local v2           #i:I
  80. .restart local v1       #i:I
  81. if-lt v2, v9, :cond_0
  82. .line 25
  83. return-void
  84. .line 22
  85. :cond_0
  86. new-instance v0, Lcom/example/smalidemo/foreach/AppBean;
  87. invoke-direct {v0}, Lcom/example/smalidemo/foreach/AppBean;-><init>()V
  88. .line 23
  89. .local v0, bean:Lcom/example/smalidemo/foreach/AppBean;
  90. sget-object v3, Lcom/example/smalidemo/foreach/OnInitTest;->mAppListOnInit:Ljava/util/ArrayList;
  91. invoke-virtual {v3, v0}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
  92. move v2, v1
  93. .end local v1           #i:I
  94. .restart local v2       #i:I
  95. goto :goto_0
  96. .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

  1. private String INIT3_STRING = "initstring_fantasyh 3";

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

[html] view plaincopy

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

[html] view plaincopy

  1. .method public constructor <init>()V
  2. .locals 1
  3. .prologue
  4. .line 6
  5. invoke-direct {p0}, Ljava/lang/Object;-><init>()V
  6. .line 9
  7. const-string v0, "initstring_fantasyh 3"
  8. iput-object v0, p0, Lcom/example/smalidemo/foreach/OnInitTest;->INIT3_STRING:Ljava/lang/String;
  9. .line 6
  10. return-void
  11. .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

时间: 2024-10-17 10:18:17

(转)Android高性能编程(2)--延迟初始化的相关文章

(转)Android高性能编程(1)--基础篇

关于专题     本专题将深入研究Android的高性能编程方面,其中涉及到的内容会有Android内存优化,算法优化,Android的界面优化,Android指令级优化,以及Android应用内存占用分析,还有一些其他有关高性能编程的知识.    随着技术的发展,智能手机硬件配置越来越高,可是它和现在的 PC 相比,其运算能力,续航能力,存储空间等都还是受到很大的限制,同时用户对手机的体验要求远远高于 PC 的桌面应用程序.以上理由,足以需要开发人员更加专心去实现和优化你的代码了.选择合适的算

(装)Android高性能编程基本规范

最近总结了一些,Android应用开发中,需要注意的一些事项,与大家分享 1.尽量少的声明全局变量 2.声明全局静态变量,一定要加final声明 3.声明非静态的全局变量,最好不要初始化任何值,在使用到的地方,在进行初始化 4.函数中若干次使用全局变量,应该将全局变量赋值给本地变量,然后直接使用本地变量 5.能用Int,不要使用浮点数 6.能用乘法不用除法 7.尽量避免使用geter和setter方法 8.在Activity的onCreate函数中,尽量做少的事. 9.在Activity中声明的

Python猫荐书系列之五:Python高性能编程

稍微关心编程语言的使用趋势的人都知道,最近几年,国内最火的两种语言非 Python 与 Go 莫属,于是,隔三差五就会有人问:这两种语言谁更厉害/好找工作/高工资-- 对于编程语言的争论,就是猿界的生理周期,每个月都要闹上一回.到了年末,各类榜单也是特别抓人眼球,闹得更凶. 其实,它们各有对方所无法比拟的优势以及用武之地,很多争论都是没有必要的.身为一个正在努力学习 Python 的(准)中年程序员,我觉得吧,先把一门语言精进了再说.没有差劲的语言,只有差劲的程序员,等真的把语言学好了,必定是"

第6章 Android驱动编程

第6章  Android驱动编程 通过介绍本章设备驱动.字符设备驱动编程.GPIO驱动程序实例和4*4扫描键盘驱动等内容,熟练掌握了Android驱动编程.Android内核内核模块编程中包括设备驱动和内核模块.模块相关命令.Android内核内核模块编程和内核模块实例程序.Android内核中采用可加载的模块化设计,一般情况下编译的Android内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中.模块相关命令中lsmod列出了当前系统中加载的模块,rmmood用于当前模块卸载,in

基础篇:6.Android数据库编程---SQLite

简介: 在Android平台上,继承了一个嵌入式关系型数据库---SQLite.SQLite具有跨平台特性,可以在所有主要的操作系统上运行.SQLite通过独占性和共享锁实现独立事务处理,因此多个进程可以在同一时间从同一数据库读取数据,但只有一个可以写入数据,在进行写操作之前,必须先获得独占锁.另一方面,SQLite采取动态数据类型,当某个值插入数据库时,SQLite会检查它的类型,如果该类型与所关联的列不匹配,SQLite则会进行强制转换.SQLite支持以下几种数据类型:NULL(空值).I

Android网络编程(1)

本系列文章对整个Android网络编程进行了总结,包括基本的TCP/IP协议,HTTP协议,HTTPS协议,HttpClient,UrlConnection,一些网络通信的库到棉花糖新加入的OKHTTP. 本文主要对TCP协议的连接管理和拥塞控制两部分知识进行总结. 连接管理 TCP协议是传输层的重要协议,负责端到端的通信.为了实现面向连接的可靠传输,TCP协议使用“三次握手”和“四次挥手”的方式来创建连接,结束连接. 三次握手: 第一次握手:建立连接时,客户端C发起建立连接请求(SYN=1)到

《GPU高性能编程CUDA实战》中代码整理

CUDA架构专门为GPU计算设计了一种全新的模块,目的是减轻早期GPU计算中存在的一些限制,而正是这些限制使得之前的GPU在通用计算中没有得到广泛的应用. 使用CUDA C来编写代码的前提条件包括:(1).支持CUDA的图形处理器,即由NVIDIA推出的GPU显卡,要求显存超过256MB:(2).NVIDIA设备驱动程序,用于实现应用程序与支持CUDA的硬件之间的通信,确保安装最新的驱动程序,注意选择与开发环境相符的图形卡和操作系统:(3).CUDA开发工具箱即CUDA Toolkit,此工具箱

JDK高性能编程之容器

JDK高性能编程之容器 读书笔记内容部分来源书籍深入理解JVM.互联网等 先放一个类图util,点击打开看明细 j360-jdk调试功能 https://github.com/xuminwlt/j360-jdk 内容 容器 -Collection List  ArrayList  LinkedList  Vector   Stack Set  HashSet Queue -Map  HashMap  HashTable  WeahHashMap Collection是最基本的集合接口,一个Col

Android网络编程之使用HttpClient批量上传文件(二)AsyncTask+HttpClient并实现上传进度监听

请尊重他人的劳动成果,转载请注明出处: Android网络编程之使用HttpClient批量上传文件(二)AsyncTask+HttpClient并实现上传进度监听 运行效果图: 我曾在<Android网络编程之使用HttpClient批量上传文件>一文中介绍过如何通过HttpClient实现多文件上传和服务器的接收.在上一篇主要使用Handler+HttpClient的方式实现文件上传.这一篇将介绍使用AsyncTask+HttpClient实现文件上传并监听上传进度. 监控进度实现: 首先