Summary of tips for Android code optimization

Summary of tips for Android code optimization

Preface

This article mainly introduces some small details of optimization techniques. When these small techniques are used in combination, they are still useful for improving the performance of the entire Android App, but they cannot significantly improve the performance. Choosing the right algorithm and data structure should be your primary consideration, which will not be covered in this article. You should use the tips in this article as a habit for writing code in your daily life, which can improve the efficiency of your code.

[[148948]]

Code performance optimization suggestions

Generally speaking, efficient code needs to meet the following two rules:

  • Don't do redundant actions
  • Don't allocate memory if you can avoid it.

The execution effect of the code will be affected by many factors such as device CPU, device memory, system version, etc. In order to ensure that the code can run well on different devices, it is necessary to optimize the efficiency of the code.

Avoid creating unnecessary objects

Although GC can recycle unused objects, allocating memory for these objects and recycling them also consumes resources. Therefore, please try to avoid creating unnecessary objects. Here are some examples to illustrate this problem:

  • If you need to return a String object, and you know it will eventually need to be concatenated to a StringBuffer, modify your implementation to avoid concatenating directly and instead create a temporary object to do the operation.
  • When extracting Strings from an input dataset, try to return a substring object of the original data instead of creating a duplicate object.
  • A slightly more radical approach is to decompose all multi-dimensional data into 1-dimensional arrays:
  • An array of int data is much better than an array of Integer objects. It can be seen that two sets of 1D arrays are more efficient than a 2D array. Similarly, this principle can be extended to other primitive data types.
  • If you need to implement an array to store (Foo, Bar) objects, it is much better to try to decompose it into Foo[] and Bar[] rather than (Foo, Bar). (Of course, for the design of some good APIs, some compromises can be made. But in your own code, you should use the decomposed convenience more often.
  • Generally speaking, you need to avoid creating more objects. Fewer objects means fewer GC actions, and GC will have a more direct impact on user experience.

Choose Static instead of Virtual

If you don't need to access the value field of an object, make sure the method is static, so that the method call will be 15%-20% faster. This is a good habit because you can know from the method declaration that the call cannot change the state of the object.

Constants are declared as Static Final

First look at the following statement

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

The compiler will use the initialization method to initialize the above value when the class is used. When accessing it later, it will need to look it up first before returning the data. We can use static final to improve performance:

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

At this time, you no longer need the above method to perform redundant lookups. Therefore, please declare constants as static final types as much as possible.

Avoid Internal Getters/Setters

In native languages ​​like C++, it is common to use getters (i = getCount()) instead of accessing variables directly (i = mCount). This is a good habit to write in C++, and is often adopted by other object-oriented languages ​​such as C# and Java, because the compiler usually inlines access, and you can add code in getters/setters at any time if you need to restrict or debug variables. However, on Android, this is a bad practice. Virtual method calls are more expensive than direct access to variables. So the reasonable approach is: use getters/setters in object-oriented design, but you should access variables directly inside the class. Without JIT (Just In Time Compiler), direct access to variables is 3 times faster than calling getters. With JIT, direct access to variables is 7 times faster than accessing through getters. Note that if you use ProGuard, you can get the same effect because ProGuard can inline accessors for you.

Using the enhanced For loop

Compare the following three looping methods:

  1. static   class Foo {
  2. int mSplat;
  3. }
  4.  
  5. Foo[] mArray = ...
  6.  
  7. public   void zero() {
  8. int sum = 0 ;
  9. for ( int i = 0 ; i < mArray.length; ++i) {
  10. sum += mArray[i].mSplat;
  11. }
  12. }
  13.  
  14. public   void one() {
  15. int sum = 0 ;
  16. Foo[] localArray = mArray;
  17. int len ​​= localArray.length;
  18.  
  19. for ( int i = 0 ; i < len; ++i) {
  20. sum += localArray[i].mSplat;
  21. }
  22. }
  23.  
  24. public   void two() {
  25. int sum = 0 ;
  26. for (Foo a : mArray) {
  27. sum += a.mSplat;
  28. }
  29. }

zero() is the slowest because the JIT cannot optimize it.

one() is slightly faster.

two() is the fastest without JIT, but after JIT, it is almost as fast as method one(). It uses the enhanced loop method for-each.
So please try to use the for-each method, but for ArrayList, please use the one() method.

Use package-level access instead of private access for inner classes

Refer to the following code

  1. public   class Foo {
  2. private   class Inner {
  3. void stuff() {
  4. Foo. this .doStuff(Foo. this .mValue);
  5. }
  6. }
  7.  
  8. private   int mValue;
  9.  
  10. public   void run() {
  11. Inner in = new Inner();
  12. mValue = 27 ;
  13. in.stuff();
  14. }
  15.  
  16. private   void doStuff( int value) {
  17. System.out.println( "Value is " + value);
  18. }
  19. }

We have defined a private inner class (Foo$Inner) that directly accesses private methods and private member objects in the outer class. This is legal and the code will print "Value is 27" as expected.

The problem is that VM thinks it is illegal to directly access private members of Foo class in (Foo.Inner) because Foo and (Foo.Inner) are different classes. Even though Java language allows inner classes to access private members of outer classes. To remove this difference, the compiler generates some mock functions:

  1. /*package*/   static   int Foo.access$ 100 (Foo foo) {
  2. return foo.mValue;
  3. }
  4. /*package*/   static   void Foo.access$ 200 (Foo foo, int value) {
  5. foo.doStuff(value);
  6. }

Whenever the inner class needs to access the mValue member in the outer class or needs to call the doStuff() function, it calls these static methods. This means that the code above can be reduced to accessing member variables through accessor functions. Earlier we said that accessing through accessors is slower than accessing fields directly. So, this is an example of a specific language usage causing performance degradation.

If you are using code like this in a performance hotspot, you can declare the fields and methods that inner classes need to access as package-level access instead of private access. Unfortunately, this means that other classes in the same package can also access these fields directly, so you can't do this in the public API.

Avoid using float types

In the Android system, the data access speed of the float type is half that of the int type. Try to give priority to the int type.

Using library functions

Try to use some encapsulated library functions such as System.arraycopy(), which are more than 9 times more efficient than manually writing copy implementations.

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

Use native functions with caution

When you need to migrate existing native code to Android, please use JNI with caution. If you want to use JNI, please learn JNI Tips

Misconceptions about performance

Before JIT, using a specific data type is indeed more efficient than using an abstract data type. (For example, using HashMap is more efficient than Map.) There is a misconception that the efficiency is twice as high, but it is actually only about 6%. Moreover, after JIT, there is not much difference between them.

About Measurement

The data in the above document is the actual running effect of Android. We can use Traceview to measure, but the measured data is not JIT optimized, so the actual effect should be slightly better than the measured data.

<<:  The last unknown blue ocean of Android emulator mobile games

>>:  Anti-aliasing processing method for image deformation

Recommend

A formula explains: Why do the powerful ones become small brands?

In recent years, you must have noticed these chan...

Firefox for Android is testing credit card and address autofill features

After switching to Fenix, Mozilla has added most ...

All the information flow advertising optimization tips you want are here!

Account optimization refers to the process of con...

3 steps to improve the conversion of information flow, learn them!

Why is my ad not getting any exposure no matter h...

The dust in the house can never be swept clean, what’s going on?

When you vacuum, sweep the floor, and wipe the fu...

Tips for operating top Tik Tok accounts!

The first Douyin follower growth list in the seco...

Jay Abraham's 35 Strategies for App Product Marketing

A good friend of mine is a very famous business c...

The fission gameplay of Weibo traffic diversion!

I have been in the Internet circle for 6 years an...

How to build a good reputation for a product during its introduction period?

Today let’s talk about a not very interesting que...

How to build a community O2O user operation system from 3 aspects

The o2o company I work for is a one-stop communit...