阅读970 返回首页    go 阿里云 go 技术社区[云栖]


ANDROID性能调优

https://www.trinea.cn/android/android-performance-demo/#comment-115


本文主要分享自己在appstore项目中的性能调优点,包括同步改异步、缓存、Layout优化、数据库优化、算法优化、延迟执行等。

 

性能优化专题已完成五部分:

性能优化总纲——性能问题及性能调优方式
性能优化第三篇——Java(Android)代码优化
性能优化第二篇——布局优化
性能优化第一篇——数据库性能优化

性能优化实例 

 

一、性能瓶颈点
整个页面主要由6个Page的ViewPager,每个Page为一个GridView,GridView一屏大概显示4*4的item信息(本文最后有附图)。由于网络数据获取较多且随时需要保持页面内app下载进度及状态,所以出现以下性能问题
a.  ViewPager左右滑动明显卡顿
b.  GridView上下滚动明显卡顿
c.  其他Activity返回ViewPager Activity较慢
d.  网络获取到展现速度较慢


二、性能调试及定位

主要使用Traceview、monkey、monkey runner调试,traceview类似java web调优的visualvm,使用方法如下:
在需要调优的activity onCreate函数中添加

android.os.debug.startMethodTracing("Entertainment");
1
android.os.debug.startMethodTracing("Entertainment");

onDestrory函数中添加

android.os.debug.stopMethodTracing();
1
android.os.debug.stopMethodTracing();

程序退出后会在sd卡根目录下生成Entertainment.trace这个文件,cmd到android sdk的tools目录下运行traceview.bat Entertainment.trace即可,截图如下

从中可以看出各个函数的调用时间、调用次数、平均调用时间、时间占用百分比等从而定位到耗时的操作。monkey、monkey runner更详细的见后面博客介绍


三、性能调优点

主要包括同步改异步、缓存、Layout优化、数据库优化、算法优化、延迟执行。
1. 同步改异步

这个就不用多讲了,耗时操作放在线程中执行防止占用主线程,一定程度上解决anr。
但需要注意线程和service结合(防止activity被回收后线程也被回收)以及线程的数量
线程池使用可见java的线程池


2. 缓存

java的对象创建需要分配资源较耗费时间,加上创建的对象越多会造成越频繁的gc影响系统响应。主要使用单例模式、缓存(图片缓存、线程池、View缓存、IO缓存、消息缓存、通知栏notification缓存)及其他方式减少对象创建。
(1). 单例模式
对于创建开销较大的类可使用此方法,保证全局一个实例,在程序运行过程中该类不会因新建额外对象产生开销。示例代码如下:

单例模式
Java
public class Singleton { private static Object obj = new Object(); private static Singleton instance = null; private Singleton(){ } public static Singleton getInstance() { // if already inited, no need to get lock everytime if (instance == null) { synchronized (obj) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Singleton {
 
    private static Object    obj      = new Object();
    private static Singleton instance = null;
 
    private Singleton(){
    }
 
    public static Singleton getInstance() {
        // if already inited, no need to get lock everytime
        if (instance == null) {
            synchronized (obj) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
 
        return instance;
    }
}


(2). 缓存
程序中用到了图片缓存、线程池、View缓存、IO缓存、消息缓存、通知栏notification缓存等。
a. 图片缓存:ImageCacheImageSdCache


b. 线程池:
使用Java的Executors类,通过newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor、newScheduledThreadPool提供四种不同类型的线程池


c. View缓存:

可见ListView缓存机制

listView的getView缓存
Java
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = inflater.inflate(R.layout.type_item, null); holder = new ViewHolder(); holder.imageView = (ImageView)convertView.findViewById(R.id.app_icon); holder.textView = (TextView)convertView.findViewById(R.id.app_name); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.imageView.setImageResource(R.drawable.index_default_image); holder.textView.setText(""); return convertView; } /** * ViewHolder */ static class ViewHolder { ImageView imageView; TextView textView; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView == null) {
        convertView = inflater.inflate(R.layout.type_item, null);
        holder = new ViewHolder();
        holder.imageView = (ImageView)convertView.findViewById(R.id.app_icon);
        holder.textView = (TextView)convertView.findViewById(R.id.app_name);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder)convertView.getTag();
    }
    holder.imageView.setImageResource(R.drawable.index_default_image);
    holder.textView.setText("");
    return convertView;
}
 
/**
* ViewHolder
*/
static class ViewHolder {
 
    ImageView imageView;
    TextView  textView;
}

通过convertView是否为null减少layout inflate次数,通过静态的ViewHolder减少findViewById的次数,这两个函数尤其是inflate是相当费时间的


d. IO缓存:

使用具有缓存策略的输入流,BufferedInputStream替代InputStream,BufferedReader替代Reader,BufferedReader替代BufferedInputStream.对文件、网络IO皆适用。


e. 消息缓存:
通过Handler的obtainMessage回收就的Message对象,减少Message对象的创建开销
handler.sendMessage(handler.obtainMessage(1));


f. 通知栏notification缓存:
下载中需要不断改变通知栏进度条状态,如果不断新建Notification会导致通知栏很卡。这里我们可以使用最简单的缓存
Map<String, Notification> notificationMap = new HashMap<String, Notification>();如果notificationMap中不存在,则新建notification并且put into map.


(3). 其他
能创建基类解决问题就不用具体子类:
除需要设置优先级的线程使用new Thread创建外,其余线程创建使用new Runnable。因为子类会有自己的属性创建需要更多开销。
控制最大并发数量:使用Java的Executors类,通过Executors.newFixedThreadPool(nThreads)控制线程池最大线程并发
对于http请求增加timeout

 

3. Layout优化
使用抽象布局标签(include, viewstub, merge)、去除不必要的嵌套和View节点、减少不必要的infalte及其他Layout方面可调优点,顺带提及布局调优相关工具(hierarchy viewer和lint)。具体可见性能优化之布局优化
TextView属性优化:TextView的android:ellipsize=”marquee”跑马灯效果极耗性能,具体原因还在深入源码中

 

4. 数据库优化
主要包括索引和事务及针对Sqlite的优化。具体可见性能优化之数据库优化


5. 算法优化

这个就是个博大精深的话题了,只介绍本应用中使用的。
使用hashMap代替arrayList,时间复杂度降低一个数量级


6. 延迟执行

对于很多耗时逻辑没必要立即执行,这时候我们可以将其延迟执行。
线程延迟执行 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
消息延迟发送 handler.sendMessageDelayed(handler.obtainMessage(0), 1000);


四、本程序性能调优结果
1. ViewPager左右滑动明显卡顿
2. GridView上下滚动明显卡顿

(1). 去掉TextView的android:ellipsize=”marquee”
(2). 修改图片缓存的最大线程数,增加http timeout
(3). 修改设置app是否已安装的状态,具体代码修改如下:

Java
List<PackageInfo> installedPackageList = getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); List<App> installedAppList = function(installedAppList) for (App app : appList) { for (App installedApp : installedAppList) { } }
1
2
3
4
5
6
7
List<PackageInfo> installedPackageList = getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
List<App> installedAppList = function(installedAppList)
for (App app : appList) {
    for (App installedApp : installedAppList) {
 
    }
}

修改为

Java
for (App app : appList) { Pair<Integer, String> versionInfo = INSTALLED_APP_MAP.get(app.getPackageName()); if (versionInfo != null) { } else { } }
1
2
3
4
5
6
7
8
for (App app : appList) {
    Pair<Integer, String> versionInfo = INSTALLED_APP_MAP.get(app.getPackageName());
    if (versionInfo != null) {
 
    } else {
 
    }
}

从每次获取List<PackageInfo> installedAppList = getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);修改为只在有应用安装或卸载广播时获取应用列表,并且用hashMap代替installedAppList减少查询时间。

将平均执行时间从201ms降低到1ms。


3. 其他Activity返回ViewPager Activity较慢

定位:在onStart函数
解决:使用延迟策略,具体代码修改如下:

Java
@Override public void onStart() { super.onStart(); appUpdateListAdapter.notifyDataSetChanged(); }
1
2
3
4
5
@Override
public void onStart() {
    super.onStart();
    appUpdateListAdapter.notifyDataSetChanged();
}

改为

优化后代码
Java
public void onStart() { super.onStart(); // delay send message handler.sendMessageDelayed(handler.obtainMessage(MessageConstants.WHAT_NOTIFY_DATA_CHANGED), 100); } private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case MessageConstants.WHAT_NOTIFY_DATA_CHANGED: if (appUpdateListAdapter != null) { appUpdateListAdapter.notifyDataSetChanged(); } break; } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void onStart() {
    super.onStart();
    // delay send message
    handler.sendMessageDelayed(handler.obtainMessage(MessageConstants.WHAT_NOTIFY_DATA_CHANGED), 100);
}
 
private class MyHandler extends Handler {
 
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
 
        switch (msg.what) {
            case MessageConstants.WHAT_NOTIFY_DATA_CHANGED:
                if (appUpdateListAdapter != null) {
                    appUpdateListAdapter.notifyDataSetChanged();
                }
                break;
        }
    }
}


4. 网络获取到展现速度较慢

定位:在HttpURLConnection.getInputStream()之后的处理
解决:使用BufferedReader替代BufferedInputStream获取时间从100ms降低到3ms,具体代码修改如下:

Java
HttpURLConnection con = (HttpURLConnection)url.openConnection(); InputStream input = con.getInputStream(); while (input.read(buffer, 0, 1024) != -1) { }
1
2
3
4
5
HttpURLConnection con = (HttpURLConnection)url.openConnection();
InputStream input = con.getInputStream();
while (input.read(buffer, 0, 1024) != -1) {
 
}

改为

Java
HttpURLConnection con = (HttpURLConnection)url.openConnection(); BufferedReader input = new BufferedReader(new InputStreamReader(con.getInputStream())); String s; while ((s = input.readLine()) != null) { }
1
2
3
4
5
6
HttpURLConnection con = (HttpURLConnection)url.openConnection();
BufferedReader input = new BufferedReader(new InputStreamReader(con.getInputStream()));
String s;
while ((s = input.readLine()) != null) {
 
}



性能优化系列总篇

本文为性能优化系列的总纲,主要介绍性能调优专题计划、何为性能问题、性能调优方式及前面介绍的数据库优化、布局优化、Java(Android)代码优化具体对应的调优方式。

 

1、调优专题博客计划
目前调优专题已完成五部分:

性能优化总纲——性能问题及性能调优方式
性能优化第三篇——Java(Android)代码优化
性能优化第二篇——布局优化
性能优化第一篇——数据库性能优化

性能优化实例 

后续计划性能优化——诊断及工具(目前只有关于TraceView的介绍)、性能优化——内存篇、性能优化——JNI篇,性能优化——电量篇。

 

2、何为性能问题
在性能测试中存在两个概念:
(1). 响应时间
指从用户操作开始到系统给用户以正确反馈的时间。一般包括系统处理时间 + 网络传输时间 + 展现时间。对于非网络类应用不包括网络传输时间。响应时间是用户对性能最直接的感受。

(2). TPS(Transaction Per Second)
TPS为每秒处理的事务数,是系统吞吐量的指标,在搜索系统中也用QPS(Query Per Second)衡量。TPS一般与响应时间反相关。

 

通常所说的性能问题就是指响应时间过长、系统吞吐量过低。在web性能测试中,也会将高并发下内存泄漏归为性能问题。

在Android应用程序中由于系统ANR的限制,所以对主线程的响应时间提出了更高的要求。Android ANR的具体要求是指Activity对事件响应不超过5秒,BroadcastReceiver中执行时间不超过10秒。

 

3、性能调优方式

明白了何为性能问题之后,就能明白性能优化实际就是优化系统的响应时间,提高TPS。优化响应时间,提高TPS的方式包括:
(1) 降低执行时间
这部分包括:a. 缓存(包括对象缓存、IO缓存、网络缓存), b. 数据存储类型优化, c. 算法优化, d. JNI, e. 逻辑优化, f. 需求优化

(2) 同步改异步,利用多线程提高TPS

(3) 提前或延迟操作,错开时间段提高TPS

对于数据库优化、布局优化、Java代码部分优化都可以归纳到上面的几种方式中。具体见:

性能优化第三篇——Java(Android)代码优化
性能优化第二篇——布局优化
性能优化第一篇——数据库性能优化

性能优化实例 


性能优化之布局优化

本文为Android性能优化的第二篇——布局优化,主要介绍使用抽象布局标签(include, viewstub, merge)、去除不必要的嵌套和View节点、减少不必要的infalte及其他Layout方面可调优点,顺带提及布局调优相关工具(hierarchy viewer和lint)

 

性能优化专题已完成五部分:

性能优化总纲——性能问题及性能调优方式
性能优化第三篇——Java(Android)代码优化
性能优化第二篇——布局优化
性能优化第一篇——数据库性能优化

性能优化实例 

 

1、抽象布局标签 

(1) <include>标签
include标签常用于将布局中的公共部分提取出来供其他layout共用,以实现布局模块化,这在布局编写方便提供了大大的便利。
下面以在一个布局main.xml中用include引入另一个布局foot.xml为例。main.mxl代码如下:

Java
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android: android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="@dimen/dp_80" /> <include layout="@layout/foot.xml" /> </RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <ListView
        android:id="@+id/simple_list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="@dimen/dp_80" />
 
    <include layout="@layout/foot.xml" />
 
</RelativeLayout>

其中include引入的foot.xml为公用的页面底部,代码如下:

Java
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" > <Button android: android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_above="@+id/text"/> <TextView android: android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_alignParentBottom="true" android:text="@string/app_name" /> </RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_40"
        android:layout_above="@+id/text"/>
 
    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_40"
        android:layout_alignParentBottom="true"
        android:text="@string/app_name" />
 
</RelativeLayout>

<include>标签唯一需要的属性是layout属性,指定需要包含的布局文件。可以定义android:id和android:layout_*属性来覆盖被引入布局根节点的对应属性值。注意重新定义android:id后,子布局的顶结点i就变化了。

 

(2) <viewstub>标签
viewstub标签同include标签一样可以用来引入一个外部布局,不同的是,viewstub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。
viewstub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。
下面以在一个布局main.xml中加入网络错误时的提示页面network_error.xml为例。main.mxl代码如下:

Java
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" > …… <ViewStub android: android:layout_width="match_parent" android:layout_height="match_parent" android:layout="@layout/network_error" /> </RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    ……
    <ViewStub
        android:id="@+id/network_error_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/network_error" />
 
</RelativeLayout>

其中network_error.xml为只有在网络错误时才需要显示的布局,默认不会被解析,示例代码如下:

Java
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" > <Button android: android:layout_width="@dimen/dp_160" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:text="@string/network_setting" /> <Button android: android:layout_width="@dimen/dp_160" android:layout_height="wrap_content" android:layout_below="@+id/network_setting" android:layout_centerHorizontal="true" android:layout_marginTop="@dimen/dp_10" android:text="@string/network_refresh" /> </RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <Button
        android:id="@+id/network_setting"
        android:layout_width="@dimen/dp_160"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="@string/network_setting" />
 
    <Button
        android:id="@+id/network_refresh"
        android:layout_width="@dimen/dp_160"
        android:layout_height="wrap_content"
        android:layout_below="@+id/network_setting"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="@dimen/dp_10"
        android:text="@string/network_refresh" />
 
</RelativeLayout>

在java中通过(ViewStub)findViewById(id)找到ViewStub,通过stub.inflate()展开ViewStub,然后得到子View,如下:

Java
private View networkErrorView; private void showNetError() { // not repeated infalte if (networkErrorView != null) { networkErrorView.setVisibility(View.VISIBLE); return; } ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout); networkErrorView = stub.inflate(); Button networkSetting = (Button)networkErrorView.findViewById(R.id.network_setting); Button refresh = (Button)findViewById(R.id.network_refresh); } private void showNormal() { if (networkErrorView != null) { networkErrorView.setVisibility(View.GONE); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private View networkErrorView;
 
private void showNetError() {
    // not repeated infalte
    if (networkErrorView != null) {
        networkErrorView.setVisibility(View.VISIBLE);
        return;
    }
 
    ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout);
    networkErrorView = stub.inflate();
    Button networkSetting = (Button)networkErrorView.findViewById(R.id.network_setting);
    Button refresh = (Button)findViewById(R.id.network_refresh);
}
 
private void showNormal() {
    if (networkErrorView != null) {
        networkErrorView.setVisibility(View.GONE);
    }
}

在上面showNetError()中展开了ViewStub,同时我们对networkErrorView进行了保存,这样下次不用继续inflate。这就是后面第三部分提到的减少不必要的infalte。

viewstub标签大部分属性同include标签类似。

 

上面展开ViewStub部分代码

Java
ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout); networkErrorView = stub.inflate();
1
2
ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout);
networkErrorView = stub.inflate();

也可以写成下面的形式

Java
View viewStub = findViewById(R.id.network_error_layout); viewStub.setVisibility(View.VISIBLE); // ViewStub被展开后的布局所替换 networkErrorView = findViewById(R.id.network_error_layout); // 获取展开后的布局
1
2
3
View viewStub = findViewById(R.id.network_error_layout);
viewStub.setVisibility(View.VISIBLE);   // ViewStub被展开后的布局所替换
networkErrorView =  findViewById(R.id.network_error_layout); // 获取展开后的布局

效果一致,只是不用显示的转换为ViewStub。通过viewstub的原理我们可以知道将一个view设置为GONE不会被解析,从而提高layout解析速度,而VISIBLE和INVISIBLE这两个可见性属性会被正常解析。

 

(3) <merge>标签
在使用了include后可能导致布局嵌套过多,多余不必要的layout节点,从而导致解析变慢,不必要的节点和嵌套可通过hierarchy viewer(下面布局调优工具中有具体介绍)或设置->开发者选项->显示布局边界查看。

 

merge标签可用于两种典型情况:
a.  布局顶结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity内容试图的parent view就是个FrameLayout,所以可以用merge消除只剩一个。
b.  某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。

以(1) <include>标签的示例为例,用hierarchy viewer查看main.xml布局如下图:


可以发现多了一层没必要的RelativeLayout,将foot.xml中RelativeLayout改为merge,如下:

Java
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" > <Button android: android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_above="@+id/text"/> <TextView android: android:layout_width="match_parent" android:layout_height="@dimen/dp_40" android:layout_alignParentBottom="true" android:text="@string/app_name" /> </merge>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_40"
        android:layout_above="@+id/text"/>
 
    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_40"
        android:layout_alignParentBottom="true"
        android:text="@string/app_name" />
 
</merge>

运行后再次用hierarchy viewer查看main.xml布局如下图:

这样就不会有多余的RelativeLayout节点了。

 

2、去除不必要的嵌套和View节点
(1) 首次不需要使用的节点设置为GONE或使用viewstub
(2) 使用RelativeLayout代替LinearLayout
大约在Android4.0之前,新建工程的默认main.xml中顶节点是LinearLayout,而在之后已经改为RelativeLayout,因为RelativeLayout性能更优,且可以简单实现LinearLayout嵌套才能实现的布局。
4.0及以上Android版本可通过设置->开发者选项->显示布局边界打开页面布局显示,看看是否有不必要的节点和嵌套。4.0以下版本可通过hierarchy viewer查看。

 

3、减少不必要的infalte
(1) 对于inflate的布局可以直接缓存,用全部变量代替局部变量,避免下次需再次inflate
如上面ViewStub示例中的

Java
if (networkErrorView != null) { networkErrorView.setVisibility(View.VISIBLE); return; }
1
2
3
4
if (networkErrorView != null) {
    networkErrorView.setVisibility(View.VISIBLE);
    return;
}

 

(2) ListView提供了item缓存,adapter getView的标准写法,如下:

Java
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = inflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); …… convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } } /** * ViewHolder * * @author trinea@trinea.cn 2013-08-01 */ private static class ViewHolder { ImageView appIcon; TextView appName; TextView appInfo; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    最后更新:2017-04-03 12:55:27

  上一篇:go Android MTK平台修改开机动画,开机logo
  下一篇:go C# 使用LINQ、泛型、Index函数优化switch(或者多条if)语句