
App performance plays a major role in making an application successful. An application might have much more features than its competitors but if it lags in performance then the features won’t help to increase the userbase. In this blog, we will see about improving the performance of an Android application.
String vs StringBuffer vs StringBuilder:
String:
The string is the most used class Java programming language. It is immutable. When we create a new string with double quotes, Initially it looks for the same value in the memory pool. If the memory pool has any string with the same value, the new string is stored in that reference. Otherwise, it creates a new object in the memory pool. But if we create a new string with a new keyword, it explicitly creates a new object in heap memory.
If we manipulate the string, it creates the new memory for each manipulation due to its immutable character. If we use + operator in a loop for string append, it creates the new object for each append operation. So it consumes a lot of memory. Hence, String is not the best choice for large concatenation operations.
StringBuffer and StringBuilder are two other best ways for large concatenation operations because both are mutable. We will see about both of them in detail.
StringBuffer & StringBuilder:
Both StringBuffer and StringBuilder are mutable. String manipulation is done without creating any new unwanted objects. The only difference between them is, StringBuffer is “thread safe” because it is synchronised while StringBuilder is not. Due to this StringBuffer is much slower than StringBuilder.
We must avoid the String when we need more string manipulation. When we use non-multi threaded case, StringBuilder is the best choice as per memory and speed. If we want a multi-threaded operation to access the String, the best choice is StringBuffer.
For loop performance:
Program:
List<Integer> list = new ArrayList<>(); long startTime; long endTime; for (int i = 0; i < 100000; i++) { list.add(i); } //Type 1 startTime = Calendar.getInstance().getTimeInMillis(); for (Integer i : list) ; endTime = Calendar.getInstance().getTimeInMillis(); Log.d("tag","for loop performance :"+" For each loop : " + (endTime - startTime) + “ ms"); //Type 2 startTime = Calendar.getInstance().getTimeInMillis(); for (int j = 0; j < list.size(); j++) ; endTime = Calendar.getInstance().getTimeInMillis(); Log.d("tag","for loop performance :"+" Using collection.size() in condition check : " + (endTime - startTime) + " ms"); //Type 3 startTime = Calendar.getInstance().getTimeInMillis(); int size = list.size(); for (int j = 0; j < size ;j++); endTime = Calendar.getInstance().getTimeInMillis(); Log.d( "tag","for loop performance :"+" Assign collection.size() in separate variable : "+(endTime - startTime) + " ms"); //Type 4 startTime = Calendar.getInstance().getTimeInMillis(); for (int j = list.size(); j > size ;j--); endTime = Calendar.getInstance().getTimeInMillis(); Log.d("tag","for loop performance :"+ "Assign collection.size() as initialization : "+(endTime - startTime) + " ms");
Output:
for loop performance: For each loop: 12 ms
for loop performance : Using collection.size() in condition check : 3 ms
for loop performance : Assign collection.size() in separate variable : 1 ms
for loop performance :Assign collection.size() as initialization : 0 ms
Analysis:
– Type 1 is slow because the iterator is internally created in for each loop.
– In Type 2 size() method is called in every iteration. So it also takes a lot of time to execute.
– Type 3 & Type 4 is almost the same. Both are fetching the size initially. But Type 4 don’t have an extra memory. Type 3 have additional memory to save the size. If we no need to consider the order of the list, type 4 is the best choice. Otherwise, type 3 is the best choice.
Save the Instance State:
The current state of the activities should be saved to avoid recalculation when the application is opened again. The data loaded by your activities or the result of any long-running operation should be saved when the onSaveInstanceState event is raised and restored when the onRestoreInstanceState event is raised. Since the state is saved with a serializable bundle object, the easiest way to manage state is to have a serializable state object containing all the information needed to restore the activity so only this object needs to be saved.
The information entered by the user in View controls is already saved automatically by the Android SDK and does not need to be kept in the state. Remember, the activity state may be lost when the user leaves your application or rotates the screen, not only when the user navigates to another activity.
Slow Rendering:
Android is trying to refresh the activities for every 16 ms. Hence the app has to do all the logic to update the screen in 16 ms. If the app can’t complete the logic in 16 ms, Android OS tries to draw a new picture but it won’t be ready. As a result, it won’t refreshing anything. So the animation will not be smooth.
Optimise Layout Hierarchies:
Using the basic layout structures leads to the most efficient layouts. However, each widget and layout you add to your application requires initialisation, layout, and drawing.
For example, using nested instances of LinearLayout can lead to an excessively deep view hierarchy. Furthermore, nesting several instances of LinearLayout that use the layout weight parameter can be especially expensive as each child needs to be measured twice. This is particularly important when the layout is inflated repeatedly, such as when used in a ListView or GridView.
We have seen in detail about few methods with which the performance of the application can be improved. This gives you a good idea about how the small things act as a major game changer in terms of performance.
–
Sivavishnu R,
Android Development Team,
Mallow Technologies.