android开发(性能篇)

来源:互联网 发布:java员工管理信息系统 编辑:程序博客网 时间:2024/05/17 04:38

This document primarily covers micro-optimizations that can improve overall app performance when combined, but it's unlikely that these changes will result in dramatic performance effects. Choosing the right algorithms and data structures should always be your priority, but is outside the scope of this document. You should use the tips in this document as general coding practices that you can incorporate into your habits for general code efficiency.

There are two basic rules for writing efficient code:


Don't do work that you don't need to do.
Don't allocate memory if you can avoid it.

One of the trickiest problems you'll face when micro-optimizing an Android app is that your app is certain to be running on multiple types of hardware. Different versions of the VM running on different processors running at different speeds. It's not even generally the case that you can simply say "device X is a factor F faster/slower than device Y", and scale your results from one device to others. In particular, measurement on the emulator tells you very little about performance on any device. There are also huge differences between devices with and without a JIT: the best code for a device with a JIT is not always the best code for a device without.

To ensure your app performs well across a wide variety of devices, ensure your code is efficient at all levels and agressively optimize your performance.


Avoid Creating Unnecessary Objects

Object creation is never free. A generational garbage collector with per-thread allocation pools for temporary objects can make allocation cheaper, but allocating memory is always more expensive than not allocating memory.

As you allocate more objects in your app, you will force a periodic garbage collection, creating little "hiccups" in the user experience. The concurrent garbage collector introduced in Android 2.3 helps, but unnecessary work should always be avoided.

Thus, you should avoid creating object instances you don't need to. Some examples of things that can help:


If you have a method returning a string, and you know that its result will always be appended to a
StringBuffer
anyway, change your signature and implementation so that the function does the append directly, instead of creating a short-lived temporary object.
When extracting strings from a set of input data, try to return a substring of the original data, instead of creating a copy. You will create a new
String
object, but it will share the
char[]
with the data. (The trade-off being that if you're only using a small part of the original input, you'll be keeping it all around in memory anyway if you go this route.)

A somewhat more radical idea is to slice up multidimensional arrays into parallel single one-dimension arrays:


An array of
int
s is a much better than an array of
Integer
objects, but this also generalizes to the fact that two parallel arrays of ints are also a lot more efficient than an array of
(int,int)
objects. The same goes for any combination of primitive types.
If you need to implement a container that stores tuples of
(Foo,Bar)
objects, try to remember that two parallel
Foo[]
and
Bar[]
arrays are generally much better than a single array of custom
(Foo,Bar)
objects. (The exception to this, of course, is when you're designing an API for other code to access. In those cases, it's usually better to make a small compromise to the speed in order to achieve a good API design. But in your own internal code, you should try and be as efficient as possible.)

Generally speaking, avoid creating short-term temporary objects if you can. Fewer objects created mean less-frequent garbage collection, which has a direct impact on user experience.


Prefer Static Over Virtual

If you don't need to access an object's fields, make your method static. Invocations will be about 15%-20% faster. It's also good practice, because you can tell from the method signature that calling the method can't alter the object's state.


Use Static Final For Constants

Consider the following declaration at the top of a class:

static int intVal = 42;
static String strVal = "Hello, world!";

The compiler generates a class initializer method, called
<clinit>
, that is executed when the class is first used. The method stores the value 42 into
intVal
, and extracts a reference from the classfile string constant table for
strVal
. When these values are referenced later on, they are accessed with field lookups.

We can improve matters with the "final" keyword:

static final int intVal = 42;
static final String strVal = "Hello, world!";

The class no longer requires a
<clinit>
method, because the constants go into static field initializers in the dex file. Code that refers to
intVal
will use the integer value 42 directly, and accesses to
strVal
will use a relatively inexpensive "string constant" instruction instead of a field lookup.

Note: This optimization applies only to primitive types and
String
constants, not arbitrary reference types. Still, it's good practice to declare constants
static final
whenever possible.


Avoid Internal Getters/Setters

In native languages like C++ it's common practice to use getters (
i = getCount()
) instead of accessing the field directly (
i = mCount
). This is an excellent habit for C++ and is often practiced in other object oriented languages like C# and Java, because the compiler can usually inline the access, and if you need to restrict or debug field access you can add the code at any time.

However, this is a bad idea on Android. Virtual method calls are expensive, much more so than instance field lookups. It's reasonable to follow common object-oriented programming practices and have getters and setters in the public interface, but within a class you should always access fields directly.

Without a JIT, direct field access is about 3x faster than invoking a trivial getter. With the JIT (where direct field access is as cheap as accessing a local), direct field access is about 7x faster than invoking a trivial getter.

Note that if you're using ProGuard, you can have the best of both worlds because ProGuard can inline accessors for you.


Use Enhanced For Loop Syntax

The enhanced
for
loop (also sometimes known as "for-each" loop) can be used for collections that implement the
Iterable
interface and for arrays. With collections, an iterator is allocated to make interface calls to
hasNext()
and
next()
. With an
ArrayList
, a hand-written counted loop is about 3x faster (with or without JIT), but for other collections the enhanced for loop syntax will be exactly equivalent to explicit iterator usage.

There are several alternatives for iterating through an array:

static class Foo {
int mSplat;
}
Foo[] mArray = ...
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}


zero()
is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.


one()
is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.


two()
is fastest for devices without a JIT, and indistinguishable from one() for devices with a JIT. It uses the enhanced for loop syntax introduced in version 1.5 of the Java programming language.

So, you should use the enhanced
for
loop by default, but consider a hand-written counted loop for performance-critical
ArrayList
iteration.

Tip:Also see Josh Bloch's Effective Java, item 46.


Consider Package Instead of Private Access with Private Inner Classes

Consider the following class definition:

public class Foo {
private class Inner {
void stuff() {
Foo.this.doStuff(Foo.this.mValue);
}
}
private int mValue;
public void run() {
Inner in = new Inner();
mValue = 27;
in.stuff();
}
private void doStuff(int value) {
System.out.println("Value is " + value);
}
}

What's important here is that we define a private inner class (
Foo$Inner
) that directly accesses a private method and a private instance field in the outer class. This is legal, and the code prints "Value is 27" as expected.

The problem is that the VM considers direct access to
Foo
's private members from
Foo$Inner
to be illegal because
Foo
and
Foo$Inner
are different classes, even though the Java language allows an inner class to access an outer class' private members. To bridge the gap, the compiler generates a couple of synthetic methods:

/*package*/ static int Foo.access$100(Foo foo) {
return foo.mValue;
}
/*package*/ static void Foo.access$200(Foo foo, int value) {
foo.doStuff(value);
}

The inner class code calls these static methods whenever it needs to access the
mValue
field or invoke the
doStuff()
method in the outer class. What this means is that the code above really boils down to a case where you're accessing member fields through accessor methods. Earlier we talked about how accessors are slower than direct field accesses, so this is an example of a certain language idiom resulting in an "invisible" performance hit.

If you're using code like this in a performance hotspot, you can avoid the overhead by declaring fields and methods accessed by inner classes to have package access, rather than private access. Unfortunately this means the fields can be accessed directly by other classes in the same package, so you shouldn't use this in public API.


Avoid Using Floating-Point

As a rule of thumb, floating-point is about 2x slower than integer on Android-powered devices.

In speed terms, there's no difference between
float
and
double
on the more modern hardware. Space-wise,
double
is 2x larger. As with desktop machines, assuming space isn't an issue, you should prefer
double
to
float
.

Also, even for integers, some processors have hardware multiply but lack hardware divide. In such cases, integer division and modulus operations are performed in software—something to think about if you're designing a hash table or doing lots of math.


Know and Use the Libraries

In addition to all the usual reasons to prefer library code over rolling your own, bear in mind that the system is at liberty to replace calls to library methods with hand-coded assembler, which may be better than the best code the JIT can produce for the equivalent Java. The typical example here is
String.indexOf()
and related APIs, which Dalvik replaces with an inlined intrinsic. Similarly, the
System.arraycopy()
method is about 9x faster than a hand-coded loop on a Nexus One with the JIT.

Tip:Also see Josh Bloch's Effective Java, item 47.


Use Native Methods Carefully

Developing your app with native code using theAndroid NDKisn't necessarily more efficient than programming with the Java language. For one thing, there's a cost associated with the Java-native transition, and the JIT can't optimize across these boundaries. If you're allocating native resources (memory on the native heap, file descriptors, or whatever), it can be significantly more difficult to arrange timely collection of these resources. You also need to compile your code for each architecture you wish to run on (rather than rely on it having a JIT). You may even have to compile multiple versions for what you consider the same architecture: native code compiled for the ARM processor in the G1 can't take full advantage of the ARM in the Nexus One, and code compiled for the ARM in the Nexus One won't run on the ARM in the G1.

Native code is primarily useful when you have an existing native codebase that you want to port to Android, not for "speeding up" parts of your Android app written with the Java language.

If you do need to use native code, you should read ourJNI Tips.

Tip:Also see Josh Bloch's Effective Java, item 54.


Performance Myths

On devices without a JIT, it is true that invoking methods via a variable with an exact type rather than an interface is slightly more efficient. (So, for example, it was cheaper to invoke methods on a
HashMap map
than a
Map map
, even though in both cases the map was a
HashMap
.) It was not the case that this was 2x slower; the actual difference was more like 6% slower. Furthermore, the JIT makes the two effectively indistinguishable.

On devices without a JIT, caching field accesses is about 20% faster than repeatedly accesssing the field. With a JIT, field access costs about the same as local access, so this isn't a worthwhile optimization unless you feel it makes your code easier to read. (This is true of final, static, and static final fields too.)
Always Measure


Before you start optimizing, make sure you have a problem that you need to solve. Make sure you can accurately measure your existing performance, or you won't be able to measure the benefit of the alternatives you try.

Every claim made in this document is backed up by a benchmark. The source to these benchmarks can be found in the code.google.com "dalvik" project.

The benchmarks are built with theCaliper microbenchmarking framework for Java. Microbenchmarks are hard to get right, so Caliper goes out of its way to do the hard work for you, and even detect some cases where you're not measuring what you think you're measuring (because, say, the VM has managed to optimize all your code away). We highly recommend you use Caliper to run your own microbenchmarks.

You may also findTraceview useful for profiling, but it's important to realize that it currently disables the JIT, which may cause it to misattribute time to code that the JIT may be able to win back. It's especially important after making changes suggested by Traceview data to ensure that the resulting code actually runs faster when run without Traceview.

For more help profiling and debugging your apps, see the following documents:


Profiling with Traceview and dmtracedump
Analysing Display and Performance with Systrace

最近做一个android 的应用程序 总是出现内存高 和cpu高的问题困扰了好多天。

下面为自己从网上总结的和自己找到的问题。

1. WebView  控件:

使用了 WebView 控件一定要注意清理缓存  destroy() 方法,但之前必须调用 removeAllViews() 要不然有时出错

 

myWebView.removeAllViews();
myWebView.destroy();

 

 

2.线程

在退出活动窗口时一定要注意开启的线程是否已经关闭,可以在debug查看线程的开启情况。

如果只是刷新Ui线程 建议不用线程可以使用 Handler 来刷新 方法如下。这种方法只能做简单的操作,复杂操作建议使用线程。

 

private Handler _ui_handler = new Handler() {

 @Override
public void handleMessage(Message msg) {

 switch (msg.what) {
  case 0://下面你可以写你需要处理的代码
                _ui_handler .sendEmptyMessageDelayed(0,1000)//1000 为延时发送的时间 单位是毫秒
  break;
        }
    }
}

 

 

3 sqlite

使用sqlite是一定要注意 关闭当前指针 和数据库连接

下面为注意内存溢出的问题

各位兄弟姐妹,Java开发中的内存泄露的问题经常会给我们带来很多烦恼。特别是对一些新手,如果平时不注意一些细节问题,最后很可能会导致很严重的后果。

    在Android中的Java开发也同样会有这样的问题。附件中的pdf整理了一些关于Android中的Java开发,在内存使用方面需要注意的一些问题,希望能够对大家有所帮助。

 

接下篇: [Android] 内存泄漏调试经验分享 (二) 

 

Android 内存泄漏调试

一、概述 1

二、Android(Java)中常见的容易引起内存泄漏的不良代码 1

(一) 查询数据库没有关闭游标 2

(二) 构造Adapter时,没有使用缓存的 convertView 3

(三) Bitmap对象不在使用时调用recycle()释放内存 4

(四) 释放对象的引用 4

(五) 其他 5

三、内存监测工具 DDMS --> Heap 5

四、内存分析工具 MAT(Memory Analyzer Tool) 7

(一) 生成.hprof文件 7

(二) 使用MAT导入.hprof文件 8

(三) 使用MAT的视图工具分析内存 8

 

 

一、概述

    Java编程中经常容易被忽视,但本身又十分重要的一个问题就是内存 使用的问题。Android应用主要使用Java语言编写,因此这个问题也同样会在Android开发中出现。本文不对Java编程问题做探讨,而是对于 在Android中,特别是应用开发中的此类问题进行整理。

    由于作者接触Android时间并不是很长,因此如有叙述不当之处,欢迎指正。

二、Android(Java)中常见的容易引起内存泄漏的不良代码

    Android主要应用在嵌入式设备当中,而嵌入式设备由于一些众所 周知的条件限制,通常都不会有很高的配置,特别是内存是比较有限的。如果我们编写的代码当中有太多的对内存使用不当的地方,难免会使得我们的设备运行缓 慢,甚至是死机。为了能够使得Android应用程序安全且快速的运行,Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行, 它是由Zygote服务进程孵化出来的,也就是说每个应用程序都是在属于自己的进程中运行的。一方面,如果程序在运行过程中出现了内存泄漏的问题,仅仅会 使得自己的进程被kill掉,而不会影响其他进程(如果是system_process等系统进程出问题的话,则会引起系统重启)。另一方面 Android为不同类型的进程分配了不同的内存使用上限,如果应用进程使用的内存超过了这个上限,则会被系统视为内存泄漏,从而被kill掉。 Android为应用进程分配的内存上限如下所示:

位置: /ANDROID_SOURCE/system/core/rootdir/init.rc 部分脚本

# Define the oom_adj values for the classes of processes that can be

# killed by the kernel.  These are used in ActivityManagerService.

    setprop ro.FOREGROUND_APP_ADJ 0

    setprop ro.VISIBLE_APP_ADJ 1

    setprop ro.SECONDARY_SERVER_ADJ 2

    setprop ro.BACKUP_APP_ADJ 2

    setprop ro.HOME_APP_ADJ 4

    setprop ro.HIDDEN_APP_MIN_ADJ 7

    setprop ro.CONTENT_PROVIDER_ADJ 14

    setprop ro.EMPTY_APP_ADJ 15

 

# Define the memory thresholds at which the above process classes will

# be killed.  These numbers are in pages (4k).

    setprop ro.FOREGROUND_APP_MEM 1536

    setprop ro.VISIBLE_APP_MEM 2048

    setprop ro.SECONDARY_SERVER_MEM 4096

    setprop ro.BACKUP_APP_MEM 4096

    setprop ro.HOME_APP_MEM 4096

    setprop ro.HIDDEN_APP_MEM 5120

    setprop ro.CONTENT_PROVIDER_MEM 5632

    setprop ro.EMPTY_APP_MEM 6144

 

# Write value must be consistent with the above properties.

# Note that the driver only supports 6 slots, so we have HOME_APP at the

# same memory level as services.

    write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15

 

    write /proc/sys/vm/overcommit_memory 1

    write /proc/sys/vm/min_free_order_shift 4

    write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144

 

    # Set init its forked children's oom_adj.

    write /proc/1/oom_adj -16

 

    正因为我们的应用程序能够使用的内存有限,所以在编写代码的时候需要特别注意内存使用问题。如下是一些常见的内存使用不当的情况。

 

(一) 查询数据库没有关闭游标

描述:

    程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。

 

示例代码:

Cursor cursor = getContentResolver().query(uri ...);

if (cursor.moveToNext()) {

    ... ... 

}

 

修正示例代码:

Cursor cursor = null;

try {

    cursor = getContentResolver().query(uri ...);

    if (cursor != null && cursor.moveToNext()) {

        ... ... 

    }

} finally {

    if (cursor != null) {

        try { 

            cursor.close();

        } catch (Exception e) {

            //ignore this

        }

    }

 

(二) 构造Adapter时,没有使用缓存的 convertView

描述:

    以构造ListView的BaseAdapter为例,在BaseAdapter中提高了方法:

public View getView(int position, View convertView, ViewGroup parent)

来向ListView提供每一个item所需要的view对象。初始时 ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当 向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造 过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list item的 view对象(初始化时缓存中没有view对象则convertView是null)。

    由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。ListView回收list item的view对象的过程可以查看:

android.widget.AbsListView.java --> void addScrapView(View scrap) 方法。

 

示例代码:

public View getView(int position, View convertView, ViewGroup parent) {

    View view = new Xxx(...);

    ... ...

    return view;

}

 

修正示例代码:

public View getView(int position, View convertView, ViewGroup parent) {

    View view = null;

    if (convertView != null) {

        view = convertView;

        populate(view, getItem(position));

        ...

    } else {

        view = new Xxx(...);

        ...

    }

    return view;

 

(三) Bitmap对象不在使用时调用recycle()释放内存

描述:

    有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不在被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。可以看一下代码中的注释:

    /**

     * Free up the memory associated with this bitmap's pixels, and mark the

     * bitmap as "dead", meaning it will throw an exception if getPixels() or

     * setPixels() is called, and will draw nothing. This operation cannot be

     * reversed, so it should only be called if you are sure there are no

     * further uses for the bitmap. This is an advanced call, and normally need

     * not be called, since the normal GC process will free up this memory when

     * there are no more references to this bitmap.

     */

(四) 释放对象的引用

描述:

    这种情况描述起来比较麻烦,举两个例子进行说明。

示例A:

假设有如下操作

public class DemoActivity extends Activity {

    ... ...

    private Handler mHandler = ...

    private Object obj;

    public void operation() {

     obj = initObj();

     ...

     [Mark]

     mHandler.post(new Runnable() {

            public void run() {

             useObj(obj);

            }

     });

    }

}

    我们有一个成员变量 obj,在operation()中我们希望能 够将处理obj实例的操作post到某个线程的MessageQueue中。在以上的代码中,即便是mHandler所在的线程使用完了obj所引用的对 象,但这个对象仍然不会被垃圾回收掉,因为DemoActivity.obj还保有这个对象的引用。所以如果在DemoActivity中不再使用这个对 象了,可以在[Mark]的位置释放对象的引用,而代码可以修改为:

... ...

public void operation() {

    obj = initObj();

    ...

    final Object o = obj;

    obj = null;

    mHandler.post(new Runnable() {

        public void run() {

            useObj(o);

        }

    }

}

... ...

 

示例B:

    假设我们希望在锁屏界面(LockScreen)中,监听系统中的电 话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个PhoneStateListener的对象,同时将它注册到 TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面 消失的时候LockScreen对象就会被释放掉。

    但是如果在释放LockScreen对象的时候忘记取消我们之前注册 的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的 LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。

    总之当一个生命周期较短的对象A,被一个生命周期较长的对象B保有其引用的情况下,在A的生命周期结束时,要在B中清除掉对A的引用。

(五) 其他

    Android应用程序中最典型的需要注意释放资源的情况是在 Activity的生命周期中,在onPause()、onStop()、onDestroy()方法中需要适当的释放资源的情况。由于此情况很基础,在 此不详细说明,具体可以查看官方文档对Activity生命周期的介绍,以明确何时应该释放哪些资源

0 0
原创粉丝点击