(转)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.对于开销大的静态模块或者全局非静态模块,可以重构到另外一个类里,达到延迟初始化的作用。
摘自:http://blog.csdn.net/litton_van/article/details/21872677
(转)Android高性能编程(2)--延迟初始化的更多相关文章
- (转)Android高性能编程(1)--基础篇
关于专题 本专题将深入研究Android的高性能编程方面,其中涉及到的内容会有Android内存优化,算法优化,Android的界面优化,Android指令级优化,以及Android应用内存占 ...
- (装)Android高性能编程基本规范
最近总结了一些,Android应用开发中,需要注意的一些事项,与大家分享 1.尽量少的声明全局变量 2.声明全局静态变量,一定要加final声明 3.声明非静态的全局变量,最好不要初始 ...
- spring3: 延迟初始化Bean
3.3.1 延迟初始化Bean 延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean. 配置方式很简单只需在<bean>标签上指定 “lazy- ...
- Python猫荐书系列之五:Python高性能编程
稍微关心编程语言的使用趋势的人都知道,最近几年,国内最火的两种语言非 Python 与 Go 莫属,于是,隔三差五就会有人问:这两种语言谁更厉害/好找工作/高工资…… 对于编程语言的争论,就是猿界的生 ...
- Qt on Android 核心编程
Qt on Android 核心编程(最好看的Qt编程书!CSDN博主foruok倾力奉献!) 安晓辉 著 ISBN 978-7-121-24457-5 2015年1月出版 定价:65.00元 4 ...
- Android高性能ORM数据库DBFlow入门
DBFlow,综合了 ActiveAndroid, Schematic, Ollie,Sprinkles 等库的优点.同时不是基于反射,所以性能也是非常高,效率紧跟greenDAO其后.基于注解,使用 ...
- Android网络编程基础
Android网络编程只TCP通信 TCP 服务器端工作的主要步骤如下.步骤1 调用ServerSocket(int port)创建一个ServerSocket,并绑定到指定端口上.步骤2 调用acc ...
- Android 网络编程 Socket
1.服务端开发 创建一个Java程序 public class MyServer { // 定义保存所有的Socket,与客户端建立连接得到一个Socket public static List< ...
- 二、Android NDK编程预备之Java jni入门Hello World
转自: http://www.eoeandroid.com/forum.php?mod=viewthread&tid=264543&fromuid=588695 昨天已经简要介绍了J ...
随机推荐
- Java JDBC概要总结一(基本操作和SQL注入问题)
JDBC定义: JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API.JDBC是Java访问数据库的标准规范,可以为不同的关系 ...
- Record and accumulation
最近有同学在准备校招的问题,问我几个问题,我觉得有必要把大家的问题汇总下: 1.在设计变量的while指挥时候,可以利用弹栈的特性以及Java传值 只是传递的副本 去控制 : https://www ...
- ElasticSearch高可用集群环境搭建和分片原理
1.ES是如何实现分布式高并发全文检索 2.简单介绍ES分片Shards分片技术 3.为什么ES主分片对应的备分片不在同一台节点存放 4.索引的主分片定义好后为什么不能做修改 5.ES如何实现高可用容 ...
- eclipse文档字体大小设置
步骤如下
- 编写一个程序,将 a.txt 文件中的单词与 b.txt 文件中的单词交替合并到 c.txt 文件中,a.txt 文件中的单词用回车符分隔,b.txt 文件中用回车或空格进行分隔。
package IO; import java.io.*; public class test { public void connectWords(File file1, File file2, F ...
- gitlab库迁移
gitlab 迁移 gitlab上一共有两个分之,一级提交记录. git clone --bare http://111.222.333.xxx/jiqing/test.git 执行成功后,会多一个t ...
- JNIjw01
1.VC6(CPP)的DLL代码: #include<stdio.h> #include "jniZ_JNIjw01.h" JNIEXPORT void JNICALL ...
- redis key 通配符 查询相应的key
keys pattern 查询相应的key 在redis里,允许模糊查询key 有3个通配符 *, ? ,[] *: 通配任意多个字符 ?: 通配单个字符 []: 通配括号内的某1个字符 redis ...
- 五 web爬虫,scrapy模块,解决重复ur——自动递归url
一般抓取过的url不重复抓取,那么就需要记录url,判断当前URL如果在记录里说明已经抓取过了,如果不存在说明没抓取过 记录url可以是缓存,或者数据库,如果保存数据库按照以下方式: id URL加密 ...
- js获取一周的日期范围
function getWeek() { this.nowTime = new Date(); this.init = function() { this.dayInWeek = this.nowTi ...