Masaryk University Faculty of Informatics

An optimization of a long-term mobile application IT project with an emphasis on sustainability

Bachelor’s Thesis

Marek Abaffy

Brno, Fall 2016 Masaryk University Faculty of Informatics

An optimization of a long-term mobile application IT project with an emphasis on sustainability

Bachelor’s Thesis

Marek Abaffy

Brno, Fall 2016 This is where a copy of the official signed thesis assignment and a copy ofthe Statement of an Author is located in the printed version of the document. Declaration

Hereby I declare that this paper is my original authorial work, which I have worked out on my own. All sources, references, and literature used or excerpted during elaboration of this work are properly cited and listed in complete reference to the due source.

Marek Abaffy

Advisor: RNDr. Jaroslav Škrabálek

i Acknowledgement

First, I would like to express gratitude to my supervisor, RNDr. Jaroslav Škrabálek, for allowing me to work on this thesis under his supervision. I am grateful for his helpful suggestions and valuable comments. I would also like to extend my thanks to family, colleagues, and friends. It is due to their continuous great encouragement that I have been able to complete this work and finish my studies.

ii Abstract

This implementation thesis describes a process of optimizing existing Android application project. It focuses on improving an application’s internal code structure using the latest Android development technolo- gies. As a part of this thesis, the application is migrated from outdated push notification provider Parse to the new Firebase Cloud Messag- ing service. The optimized application is then tested and compared with the original version. The result of this thesis is an application installation file, that is ready to be uploaded to the Play store.

iii Keywords

Android, application, refactoring, optimization, maintenance, code analysis, REST API, performance ...

iv Contents

1 Introduction 1 1.1 Mobile application maintenance ...... 2

2 Technologies overview 4 2.1 Data Binding ...... 4 2.2 RecyclerView ...... 4 2.3 ConstraintLayout ...... 5 2.4 Android Annotations ...... 6 2.4.1 TypeDef annotations ...... 6 2.5 ActiveAndroid ...... 7 2.6 EventBus ...... 7

3 About the application 8

4 Code analysis 10 4.1 Application architecture ...... 10 4.2 Package structure ...... 10 4.3 Third-party dependencies ...... 11 4.4 Code style ...... 12 4.5 Layout complexity ...... 13 4.6 Hard-coded strings ...... 13 4.7 Performance issues ...... 13

5 API optimization design 15 5.1 Optimizing data-update flow ...... 15

6 Refactoring process 18 6.1 Package structure ...... 18 6.1.1 Product flavors ...... 18 6.2 Designing code practices ...... 19 6.2.1 Naming conventions ...... 19 6.2.2 Starting activities and fragments ...... 19 6.3 Extracting hard-coded strings ...... 20 6.4 Creating application styles ...... 20 6.5 Layout optimization ...... 21 6.6 Binding data to layouts ...... 21

v 6.7 Replacing enums with static constants ...... 24 6.8 Optimizing list implementations ...... 24 6.9 Networking optimization ...... 25 6.9.1 Unifying networking libraries ...... 25 6.9.2 Creating generic requests ...... 25 6.9.3 Implementing API changes ...... 25 6.10 Storing structured data in database ...... 26 6.11 Dispatching events ...... 26 6.12 Removing unnecessary dependencies ...... 27

7 Push notifications 28 7.1 Firebase Cloud Messaging ...... 28 7.2 Implementation ...... 29

8 Testing 31 8.1 Functional testing ...... 31 8.1.1 Distribution ...... 31 8.1.2 Execution ...... 31 8.2 Performance testing ...... 34 8.2.1 Activity start time ...... 34 8.2.2 Memory usage ...... 37

9 Conclusion 39 9.1 Future improvements ...... 39

Bibliography 40

Index 42

A Naming conventions 42

B Constraint layout editor 43

C Code samples 44

vi List of

4.1 Application’s third-party dependencies. 12 4.2 Performance issues with their priority. 14 5.1 The designed API endpoints. 15 6.1 Deleted third-party dependencies. 27 A.1 String constants naming. 42 A.2 File assets naming. 42 A.3 Layout naming. 42

vii List of Figures

2.1 RecyclerView structure [7]. 5 2.2 EventBus communication. 7 3.1 Number of installations on active devices by Android platform. 9 5.1 API update pattern [15]. 16 8.1 Example of reported issues. 32 8.2 Bug tracking during development. 33 8.3 Activity start time [ms] on . 35 8.4 Activity start time [ms] on Cubot GT99. 36 8.5 General memory usage (original) on . 37 8.6 General memory usage (optimized) on Nexus 9. 37 8.7 Scrolling memory usage (original) on Nexus 9. 38 8.8 Scrolling memory usage (optimized) on Nexus 9. 38 B.1 Using ConstraintLayout to design menu buttons. 43

viii 1 Introduction

In recent years, the market with mobile applications has maintained an immense upward trajectory. As of today, the store features over 2,4 million1 apps for Android. With this in mind, it is obvious that to create a successful application it takes more than just to develop it. The market growth, new technologies, and rising users demands require developers to be flexible and always search for ways to improve their applications. Maintenance is needed to ensure that the application continues to satisfy user requirements. The maintenance part often accounts for 67% of the application life-cycle cost [1]. To achieve a state, where each optimization or feature implementation can be integrated easily and often by a different developer, it is important to have a maintain- able and clean source code. According to Pogoski, 40% to 60% of the maintenance effort is devoted to understanding the software to be modified. Trying to find a defect in a 500K line of code systemthat the maintainer did not develop is a challenge for him [2]. This thesis focuses on analyzing a source code of an already ex- isting mobile application, describing its weaknesses and proposing possible improvements. The theoretical part contains information about the latest Android development features designed to improve stability and performance of mobile applications. The implementa- tion part focuses on optimization of the application. The main goal is to refactor problematic parts, enhance performance, remove existing bugs and while doing so, set it up for future development. As a part of this bachelor thesis, the mobile application is fully migrated from outdated push notification service Parse to the new app platform Fire- base. The optimized version of the application is then tested and its performance analyzed. The result of this thesis is an apk2 file, ready to be uploaded to the Google Play store.

1. https://www.statista.com/statistics/266210/number-of-available- applications-in-the-google-play-store/ 2. File format used by the Android for distribution and installation of applications.

1 1. Introduction 1.1 Mobile application maintenance

The software maintenance phase covers the whole procedure ranging from the delivery of the software to its retirement [3]. The IEEE 1998 Standard for Software Maintenance defines it as following [4]:

Modification of a software product after delivery to cor- rect faults, to improve performance or other attributes, or to adapt the product to a modified environment.

Software maintenance encompasses four different types [3, 5]:

• Corrective maintenance — Corrective maintenance refers to the fixing actions performed when software failures occur, that is, the bugs or relevant problems that resulting in critical failures of software products.

• Perfective maintenance — Perfective maintenance aims for the perfection of software products concerning some issues influ- encing the usability of users which most users would prefer getting them improved but could endure being without them.

• Adaptive maintenance — Adaptive maintenance deals with the issues concerning the upgrade of system or hardware envi- ronment.

• Preventive maintenance — Preventive maintenance is certain modification on the source code in order to prevent a software system from future crash, which aims for enhancement of the maintainability and credibility.

Each of those types represents a different, but equally important part of an application maintenance process. For instance, many mod- ern mobile applications collect data about bugs in a form of crashes using crash reporting tools. These bugs can be analyzed and fixed in corrective maintenance. Perfective maintenance is especially important since it focuses on users and their feedback. Developers and compa- nies usually pay a lot of attention to user reviews, as they are one of the most important resources of feedbacks. Adaptive maintenance normally occurs when there is a major update in a mobile system [3].

2 1. Introduction

In the Android world, this means that with every release of a new SDK3 developers should verify that the application behaves correctly and users are not experiencing any issues. Also, adaptive maintenance closely relates to using third-party dependencies. If, for some reason, a dependency that an application uses changes its functionality or shuts down completely, maintenance is required. This thesis primar- ily focuses on the preventive maintenance. Its goal is to enhance the source code in such way, that its stability and maintainability would improve.

3. Software development kit

3 2 Technologies overview

This chapter provides a basic theoretical background for technologies used during the optimization process.

2.1 Data Binding

Traditionally, binding data model to a layout in Android development involves multiple steps. Firstly, XML layout needs to be inflated in the activity, then find the element in the activity by using a method such as findViewById(), assign the element to a local variable, get value from the data, and finally, assign the value to an element property [6]. This has to be repeated for each element connected to the data model and generates a lot of boilerplate code. At Google I/O 2015, a new data binding library was presented. This library makes a process of binding data to layouts easier and helps with keeping the code clean by reducing the amount of written code. With this library, developers need only three steps to bind data to a layout:

1. Create a binding from a layout

2. Retrieve the data

3. Bind the data to the view

2.2 RecyclerView

RecyclerView is a new Android widget that can display a collec- tion of data in a list or grid. This widget is a more advanced and flexible version of ListView. It enables scrolling through large data sets much more efficiently by maintaining only a limited number of views. RecyclerView also forces developers to take advantage of the ViewHolder pattern. The ViewHolder design pattern enables to access each list item without the need for the lookup, saving valuable pro- cessor cycles. Specifically, it avoids a frequent call of findViewById() during the view scrolling, making the scrolling much smoother.

4 2. Technologies overview

When using RecyclerView, implementing derived types of these classes is required:

• RecyclerView.Adapter — Provides a binding from a dataset to displayed items.

• RecyclerView.ViewHolder — Speeds up resource lookups by caching references in the layout file.

• LayoutManager — Positions items withing the RecyclerView. Developers can either choose from several pre-defined layout managers, such as LinearLayoutManager or GridLayoutManager, or implement their own.

RecyclerView

LayoutManager Adapter Dataset

Figure 2.1: RecyclerView structure [7].

The looks of RecyclerView items can be easily changed by extend- ing following classes:

• RecyclerView.ItemDecoratior — Allows the application to specify custom layout drawings to item views. This is specially useful for drawing dividers and highlighting items.

• RecyclerView.ItemAnimator — Defines the animations shown during item actions.

2.3 ConstraintLayout

ConstraintLayout is a new layout class allowing to lay out child views using so-called constraints. It aims to replace less flexible and more costly RelativeLayout. ConstraintLayout allows generating complex layouts with flat a view hierarchy, removing the need for nested view

5 2. Technologies overview

groups. It is provided in a support library, meaning that it is com- patible with Android 2.3 (API level 9) and higher. Together with this layout, a new layout editor was introduced in 2.2.

2.4 Android Annotations

Android Support Annotations Library offers a solution for inspecting subtle code problems that basic code inspection tools cannot identify. Android resource IDs, for example, use int number to identify strings, graphics, colors, and other resource types, so inspection tools cannot tell the difference between them. Android Annotations provide hints to code inspection tools to help detect these problems. They are added as metadata tags that are attached to variables, parameters, and re- turn values to inspect method return values, passed parameters, local variables, and fields. When used with code inspection tools, anno- tations can help detecting problems, such as NullPointerException and resource type conflicts [8].

2.4.1 TypeDef annotations

In standard Java an enum type provides an incredibly useful way of handling constants. Enums are full-fledged Java classes, so every value of an enum is an object of that class. In terms of memory allo- cations, this makes them much more expensive than primitive-type representations. Enum often requires more than twice as much mem- ory as static constants. Even though using enums in an Android code does not rapidly influence application memory usage, it is highly recommended to use constants to avoid possible performance issues. To retain both, performance benefits of using variables as well as build-time type safety, Android Support Annotation Library provides so-called TypeDef Annotations. Using @IntDef and @StringDef ensures that a particular parameter, return value, or field reference to a specific constant. This gives the code built-time safety while also keeping the performance benefits of constants.

6 2. Technologies overview 2.5 ActiveAndroid

To locally store structured data, Android uses SQLite database that stores data to a text file on a device. Since manual management ofsuch database can be complex and time-consuming, developers often rely on using ORM tools. ActiveAndroid works like any Object Relational Mapper by mapping Java classes to database tables and class member variables to the table columns. This allows to create, modify, delete, and query our SQLite database using model objects instead of raw SQL [9]. This is done by wrapping each database model class with simple model methods, such as save() and delete().

2.6 EventBus

EventBus is an open-source library for Android, that uses the pub- lisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with a few lines of code — simpli- fying the code, removing dependencies, and speeding up application development. It is fast and performs well with activities and frag- ments [10].

Event Subscriber

Event onEvent() Publisher Event Bus post() Event onEvent() Subscriber

Figure 2.2: EventBus communication.

7 3 About the application

The application Sparta Praha was developed by the hosting company Takeplace for AC Sparta Prague football club and serves as the main source of information for its supporters. The mobile application pro- vides users with various functionalities such as displaying match fix- tures, player statistics, news feed, or notifying them about important events. It also allows users to subscribe to receive push notifications. The core functionality of the application can be divided into seven sections:

• Magazines — Purchase and display magazines

• Team — Display information about players and their match statistics

• Daily question — Answer daily questions

• Matches — Display match fixtures and reports

• News — Display news feed from social networks

• Live — Follow live match feed

• Settings — Manage account settings and notification subscrip- tions

The application was released to Google Play1 store in July 2015 and is used by over 10,000 users. It supports Android devices with API higher or equal to 15 which includes 6 major Android versions - Ice Cream Sandwich, Jelly Bean, KitKat, Lollipop, Marshmallow and the latest .

1. https://play.google.com/store/apps/details?id=takeplace.sparta

8 3. About the application

Figure 3.1: Number of installations on active devices by Android plat- form.

9 4 Code analysis

To perform analysis of Android source code, Android Studio provides a code scanning tool Lint. Lint performs static analysis and checks files for potential bugs or optimization, security, and performance improvements. To analyze the selected application, Lint and Checkstyle are used. The Lint tool provides insight into a structural quality of the code and discovers problems that could affect the quality and performance of the application. Checkstyle automates the process of checking Java code and helps with enforcing a coding standard.

4.1 Application architecture

The Android framework offers a lot of flexibility when it comes to defining how to organize and architect an Android app. This freedom, whilst valuable, can also result in apps with large classes, inconsistent naming and architectures (or lack of) that can make testing, maintain- ing and extending difficult [12]. The analyzed application is developed with only basic architecture. Although it is difficult to categorize it, the architecture can be roughly approximated to follow Model-View-Controller pattern. The views are represented by XML layout files, which dictate what the appli- cation will look like and how elements are laid out. The Activity classes represent pseudo controller. They collect information from the models, build the view and set up event triggers needed to accomplish tasks.

4.2 Package structure

Project’s package structure is simple but vague. Multiple packages that are not explicit exist. For instance two packages util and tools are present and it is not clear what they contain. Moreover, the util package contains classes that handle in-app purchases and the tools package contains numerous classes named Util. This can be confusing for the developers. The package models that is supposed to contain

10 4. Code analysis

database models, contains also enums and EventBus events. All of the 16 Activity classes are located in the root directory, which is not recommended in case of having more than 3 [13].

4.3 Third-party dependencies

Using third-party dependencies can speed up and simplify the de- velopment process, but also brings potential risks. When including a third-party code in an application all the known and unknown vulner- abilities are included as well. Moreover, using too many dependencies can increase the size of an application installation file. The application uses 21 third-party dependencies, some of which are deprecated or could be replaced using the native API. All the used dependencies are listed in the table 6.1.

Library Description Parse Handles push notification. EventBus Simplifies communication be- tween components. CircleProgress Circular progress bar. MultiViewPager Extension of basic ViewPager, al- lows items to be wider or nar- rower than the ViewPager itself. ViewPagerIndicator Page indicator for ViewPager. L-Dialogs Library replicating the new dia- log in Android L. ActiveAndroid ORM tool for Android. EasyAdapter Extension for adapters to sim- plify work. Caldroid Fragment that displays calendar with dates in months. OkHttp Http client for Android and Java applications.

11 4. Code analysis

Library Description Picasso Image downloading and caching library. GridViewWithHeaderAndFooter Extension for GridView with added header and footer. DiskLruCache Handles caching. Android Saripaar UI form validation library for Android. Volley Http networking library for An- droid. Android Li- Android L components to be brary used in Android 2.2. Gson A Java serialization / deserializa- tion library. NineOldAndroids Android library for using the Honeycomb (Android 3.0) ani- mation API on all versions of the platform back to 1.0. Android View Animations Library with simple Android an- imations. ShowcaseView Highlights specific parts of apps with a distinctive overlay. Crashlytics Crash reporting tool.

Table 4.1: Application’s third-party dependencies.

4.4 Code style

Not following any consistent code style rapidly decreases readability and can be a potential reason for introducing errors into code. Even though calling a code style good is a matter of subjective opinion and very difficult to define, following specific rules is required to keepthe code clean.

12 4. Code analysis

Noticeably, this is one of the main problems. The overall quality of code is low. It is difficult to read, understand, and search in. Itisnot following any specific code style and the resources are not using nam- ing conventions. Also, the Android classes such as fragments are not using the basic notation. For instance, classes like Profile or Settings exist. These should be named ProfileFragment or SettingsFragment respectively. This lack of conventions is most likely caused by a col- laboration of multiple developers, while not agreeing on any coding style. The readability problem is also a reason for many hidden errors as well as code redundancy.

4.5 Layout complexity

Project layout files are highly problematic. The custom nature ofap- plication and support for mobile and tablet devices causes high layout complexity. This, together with not optimal implementation makes the layout files complicated and even the smallest change could break the layout. Moreover, most of the common views are not using their cus- tom extracted style, but each view specifies its own layout parameters, thus making layout files difficult to maintain.

4.6 Hard-coded strings

To improve maintainability of string resources used by an application, it is a good practice to externalize them so they can be maintained independently. Android API allows create a separate XML file in which all the necessary string values can be specified. Even though the selected application currently supports only Czech language, it is still important to follow this practice. The ap- plication’s strings.xml, which is responsible for defining the string resources contains most of them, but the static analysis of the code discovered 14 occurrences of hardcoded text.

4.7 Performance issues

While looking for possible performance problems, the Lint tool was ex- tremely useful. It helped discover multiple performance issues. Some

13 4. Code analysis

Issue Description Priority Ignoring ViewHoldern pat- Adapter classes are not us- high tern ing ViewHolder pattern. Static field leaks Android context classes are high placed in static fields caus- ing memory leaks. Storing data in file Storing structured data medium in a plain file instead of database increases loading time. Using enums Enum instances require medium more memory than static constants. Nested layout weights Nesting layout weights medium increase number of layout measurements exponen- tially. Unused resources Unused resources make ap- low plications larger and slow down application builds.

Table 4.2: Performance issues with their priority.

of those issues such as unused resources can be easily fixed and do not cause any critical performance degradation. Some, however, are more serious and need to be revised. All the major issues are listed in the table 4.2. The consequence of those issues is high memory usage and fre- quent occurrence of OutOfMemoryException. The quick fix applied to solve this problem is to increment the application maximal heap size. However, the ability to request a large heap is intended only for a small set of apps and should not be applied only as a quick fix to running out of memory [14]:

14 5 API optimization design

The application is communicating with a back-end server using REST API. All the available endpoints are documented in the Apiary (API design tool used by the hosting company). The API is designed rel- atively well, although a few improvements were required. The aim of API optimization is to lower data flow and fix existing flaws. The designed API endpoints are listed in the table 5.1.

Method URI [v2] Description GET /players/stats?lastUpdate Fetch players statistics. GET /players?lastUpdate Fetch players data. GET /matches?lastUpdate Fetch matches. GET /matches/{matchId}/details?lastUpdate Fetch matches. GET /magazines/active?lastUpdate Fetch maga- zines.

Table 5.1: The designed API endpoints.

5.1 Optimizing data-update flow

The application is locally storing various data so they can be accessed in offline mode. Many of those data can be updated at any time,soa regular update is required. The API design tries to optimize this flow, using an update pattern (5.1), so that with every call to fetch data only the updated ones are returned.

15 5. API optimization design

Figure 5.1: API update pattern [15].

The idea behind this is correct, however, the implementation turned out to be erroneous. The update pattern requires a key, which is used by a server side to return only updated data. For this, the original API used a device’s local time. Although it works in most cases, synchronization problems can occur if, for instance, the device’s time zone changes. Many of previously reported bugs were related to this problem. To implement this correctly, the device’s local time is replaced by the server time. A timestamp of the server is bundled in the response of the request for changed data (5.1). A slight modification is required for data that can be not only updated, but deleted as well. For such data, together with a server timestamp, an array of deleted identifiers is returned (5.1.

16 5. API optimization design

[GET] v2/matches?lastUpdate

{ "leagues": [{ "id":1, "name": "League name", "matches": [{ "id":1, }] }], "currentTime": 1475500651310 }

Listing 1: API response with server time.

[GET] v2/players?lastUpdate

{ "players": [{ "playerId":1, "firstName": "Name", "lastName": "Surname" }], "deleted":[2,3], "currentTime": 1475502292027 }

Listing 2: API response with server time and deleted entities.

17 6 Refactoring process

This chapter focuses on the process of code refactoring. Code refac- toring is a key issue in increasing internal software quality during its life-cycle. It is the process of changing a software system in such way that it does not alter the external behavior of the code yet improves its internal structure [16]. This chapter describes the process of improving the internal code stability, performance and networking optimization.

6.1 Package structure

Application package structure is changed to better reflect included classes: • adapter — List adapters implementations. • component — Custom UI components. • event — EventBus events. • fcm — Push notification related classes. • listener — Listeners and interfaces. • model — Database models. • net — Networking related classes (custom requests, responses...). • tool — Utility classes. • ui — Activities, fragments and dialogs.

6.1.1 Product flavors A product flavor defines a customized version of the application build. For example, developers might want to build a free version of an application, that provides only limited functionality, and another, paid, that provides more features. To differentiate between versions for developers, testers, and the production version, three flavors are created. Each of those variants is communicating with a different back-end server:

18 6. Refactoring process

• dev — Build variant for developers.

• beta — Build variant for testing purposes.

• sparta — Build variant for production.

6.2 Designing code practices

To achieve an easily manageable source code, it is extremely impor- tant to follow specific patterns and practices. This can reduce various problems such as code redundancy or even decrease a probability of introducing new errors into the code. The aim of this step is to design practices which follow recommended development patterns and can help improve code stability.

6.2.1 Naming conventions Proper naming conventions are a crucial part of every IT project. They help to keep the code clean and understandable. Even though there are no strict rules for naming variables and resources, many recommended ones exist. Using those recommendations the general conventions (A) are designed.

6.2.2 Starting activities and fragments When starting new Activity and Fragment with multiple possible states, it is generally needed to pass custom arguments to the instance of that class. In the case of activities, arguments are passed using Intent, which can carry data as key-value pairs called extras. Each Activity and Fragment should provide a factory method that grant a template for starting that class. These simple factory meth- ods provide two major benefits. Firstly, it ensures that the Activity or Fragment always contains arguments that are essential for correct be- havior. The second main benefit of this pattern is keeping the code that builds starting Intent close to the code that consumes the Intent so that, if the notion of what data Activity requires changes, a developer is already in the right place to make the corresponding fixes. [17].

19 6. Refactoring process

private static final String EXTRA_MATCH_ID= ,→ "EXTRA_MATCH_ID";

public static void startActivity(Context context, Long ,→ matchId){ Intent intent= new Intent(context, ,→ MatchActivity.class); intent.putExtra(EXTRA_MATCH_ID, matchId); context.startActivity(intent); }

Listing 3: Factory method to start an activity.

6.3 Extracting hard-coded strings

The static analysis discovered multiple occurrences of strings hard- coding into the source code (4.6). In this step, all the hard-coded resources are extracted into the project’s strings.xml file. In addition, the strings.xml structure is improved using the designed naming con- ventions.

6.4 Creating application styles

Android provides a possibility to generate custom collections of prop- erties that can specify the look and format for View. This practice helps separate design and content and has a positive impact on code sus- tainability. To take advantage of this, custom styles for common views are created. These styles are then used during the layout optimization.

20 6. Refactoring process

Listing 4: Example of extracted style.

6.5 Layout optimization

The process of layout optimization consists of five steps: 1. Rename files to follow naming conventions — The designed naming conventions (6.2.1) are applied to all layout files. 2. Divide complex layouts into smaller units — Layouts are di- vided into smaller subunits, which are then included using the tag. 3. Optimize layout hierarchy — Complex layouts are simpli- fied using the ConstraintLayout in order to reduce rendering time (B.1). 4. Unify mobile and tablet layouts — Layout files created for both mobile and tablet, while only having different hard-coded di- mensions, are merged together by extracting dimensions into a separate file. 5. Apply custom styles — The extracted styles (6.4) are applied to common views.

6.6 Binding data to layouts

To bring the layout optimization even further, the new Data Binding library (2.1) is used. This library helps with removing the necessity to

21 6. Refactoring process find views in the Activity class and map data there, by allowing to map data model straight in the XML layout file. First of all, the XML layout files are transformed to use the tag, so databinding can be applied. Then the appropriate model is included using the tag and all the data are mapped to cor- responding views (5). Thanks to this it is possible to remove all the boilerplate code (6), from the Activity classes. All the code required to map data model into an activity now consists of only a single line (7).

Listing 5: Layout with data binding.

22 6. Refactoring process

@Override protected void onCreate(Bundle savedInstanceState){ ((FuturaTextView) findViewById(R.id.home_team_name)) .setText(mMatch.getHome()); ((FuturaTextView) ,→ findViewById(R.id.visitor_team_name)) .setText(mMatch.getVisitors()); ((FuturaTextView) findViewById(R.id.score)) .setText(mMatch.getScore()); if(mMatch.getHalfTimeScore() != null &&!mMatch.getHalfTimeScore().isEmpty()) ((FuturaTextView) findViewById(R.id.score_halftime)) .setText(String.format("(%s)", ,→ mMatch.getHalfTimeScore())); String date= MyUtils.getIsoDate(mMatch.getDateTime()) .split("T")[1].substring(0,5)+""+ MyUtils.getIsoDate(mMatch.getDateTime()) .split("T")[0].split("-")[2]+ "."+(Integer .valueOf(MyUtils.getIsoDate(mMatch.getDateTime()) .split("T")[0].split("-")[1]))+ "."+ (MyUtils.getIsoDate(mMatch.getDateTime()) .split("T")[0].split("-")[0]); }

Listing 6: Binding data to layout without DataBinding.

@Override protected void onCreate(Bundle savedInstanceState){ mBinding.setMatch(mMatch); }

Listing 7: Binding data to layout with DataBinding.

23 6. Refactoring process 6.7 Replacing enums with static constants

Following Android development best practices, all the enum classes are replaced by static fields. The build-time type safety of enums is achieved using the TypeDef Annotations (2.4.1).

@StringDef({QuestionType.TEXT, QuestionType.RADIOBOX, ,→ QuestionType.CHECKBOX, QuestionType.RATING}) @Retention(RetentionPolicy.SOURCE) public @interface QuestionType{ String TEXT= "TEXT"; String RADIOBOX= "RADIOBOX"; String CHECKBOX= "CHECKBOX"; String RATING= "RATING"; }

Listing 8: Question types defined using Android Annotations.

6.8 Optimizing list implementations

The next part focuses on optimizing the application listing functional- ity. All the existing ListView implementations are altered to use the new RecyclerView widget (2.2). There are multiple reasons for this change. First of all, RecyclerView forces to use ViewHolder design pattern and has a positive impact on performance. Secondly, RecyclerView is more flexible than ListView, of which a future development could take advantage. The final reason for refactoring of the previous im- plementation is to optimize existing Adapter classes. In the previ- ous implementation, advanced features such as headers took advan- tage of external dependencies and the implementations were compli- cated. MagazinesFragment, for instance, was the only component us- ing the library GridViewWithHeaderAndFooter. By overriding method getItemViewType() in Adapter class and handling view types (13) it was possible to remove this library from project’s dependencies .

24 6. Refactoring process 6.9 Networking optimization

6.9.1 Unifying networking libraries

The communication with the back-end server was implemented using two different libraries — Volley from Google and OkHttp from Square. Both Volley and OkHttp are great networking libraries and each has its own strengths and weaknesses, but using both to perform simple requests without any valid reason can bring more harm than profit. Considering the fact that all the application’s API calls are basic JSON requests, which can be handled by each, this was the case. Using both libraries often led to using wrong practices and caused code redundancy. Authorization handling, for instance, was defined at multiple code segments and could not be unified. Taking this into account, all the API calls are migrated to use the Volley library. The reason for choosing this library is that most of the API calls are already using it and only a few are implemented with OkHttp.

6.9.2 Creating generic requests

After the unification of networking libraries generic request that han- dles common tasks, such as user authorization and response handling, are created. These requests extend basic Volley requests and override their methods to provide such functionality. A generic request that handles user authorization is shown in listing 11.

6.9.3 Implementing API changes

The next step is to implement the designed API changes (5). The pri- mary focus is on fetching data. The previous implementation turned out to be erroneous and not optimal, so a change was required. All the new API endpoints (5.1) are implemented. To simplify work with the data-update calls a custom utility class is implemented. This class pro- vides basic methods to set and get the token used for synchronization of data.

25 6. Refactoring process 6.10 Storing structured data in database

Some of the application’s structured data were initially stored in a plain file instead of the SQLite database. All those data are migrated to the database using the ActiveAndroid ORM tool (2.5). Besides the fact that it is a good practice to store structured data in a database, it also provides many benefits. Firstly, the SQLite database helps with queries optimization that speeds up the loading time. This can possibly result in faster activities loading. Furthermore, storing structured data in a database works well with the update pattern implemented during the networking optimization (5.1), so when the latest data are fetched, they can be updated using SQLite UPDATE query, or in case of ActiveAndroid using simple update() call.

6.11 Dispatching events

The communication between application components is optimized using EventBus (2.6). This brings many benefits. Firstly, the code be- comes more modular and looks cleaner. The modularity also removes a chance that a memory leak occurs by removing component refer- ences discovered during the analysis (4.2). Secondly, using events allows broadcasting an event to multiple receivers. This is helpful especially when doing an asynchronous job such as a network call, and not knowing which component will be in the foreground by the time the job finishes. Most of the events are implemented to dispatch fetched data. An example of such event is shown in listing 9.

@Subscribe public void onEvent(LiveMatchStreamEvent event){ liveMatchMessages.addAll(0, event.); liveMatchMessageAdapter.notifyItemRangeInserted(0, ,→ event.messages.size()); }

Listing 9: Live stream data fetched event.

26 6. Refactoring process 6.12 Removing unnecessary dependencies

Thanks to the code refactoring and using the native Android API, it was possible to remove 8 third-party dependencies in total. Two of which were deprecated at the time:

Library EasyAdapter Replaced using native API. (dep- recated) OkHttp Functionality implemented us- ing Volley. GridViewWithHeaderAndFooter Replaced using native API. DiskLruCache Not necessary after optimiza- tion. Material Design Android Li- Replaced using native compo- brary nents. NineOldAndroids Replaced using native API. (dep- recated) Android View Animations Replaced using native API. ShowcaseView Replaced using native API.

Table 6.1: Deleted third-party dependencies.

27 7 Push notifications

Push notifications are a way of notifying users of new messages and events, even when the user is not actively using an application. On Android devices, when a device receives a push notification, the ap- plication icon and a message appear in the status bar. When the user taps the notification, he is sent to the application. Notifications canbe broadcasted to all users, such as for a marketing campaign, or sent to just a subset of users, to give personalized information [18]. To implement this functionality, application takes advantage of the Parse service. Parse is a MBaaS (Mobile back-end as a service) owned by Facebook. It provides mobile app developers with a way to link their applications to back-end cloud storage and APIs exposed by back-end applications while also providing features, such as user management, push notifications, and integration with social networking services. As of 28 January 2016, Facebook announced that it will close down Parse, with services shutting down on 28 January 20171. This is the primary reason for searching for a new push notification provider. Many services that provide push notification functionality exist. Two aspects were considered when choosing a new provider. First of all, the support for both Android and iOS devices is essential. Sec- ondly, the service durability is considered. The aim is to minimize the probability of a need to migrate to different provider again.

7.1 Firebase Cloud Messaging

Firebase is a unified app platform for Android, iOS and mobile web development. It contains tools and infrastructure to develop faster, improve app quality, acquire and engage users, and monetize apps. On May 20162 Google introduced a new cross-platform messaging solution Firebase Cloud Messaging (FCM). Firebase Cloud Messaging is the successor to Messaging with Google strongly recommending developers to migrate to this new service.

1. http://blog.parse.com/announcements/moving-on/ 2. https://firebase.googleblog.com/2016/05/introducing-firebase- cloud-messaging.html

28 7. Push notifications

FCM provides a set of APIs to send messages from a server to an application efficiently and reliably. The FCM service handles all aspects of queueing of messages and delivery to client applications running on target devices. To distribute messages to client applications, it is possible to use any of three ways — single device, a group of devices, or devices subscribed to a topic. To set up Firebase Cloud Messaging in the application, two follow- ing dependencies are required:

dependencies{ compile 'com.google.firebase:firebase-core:9.8.0' compile 'com.google.firebase:firebase-messaging:9.8.0' }

7.2 Implementation

The application allows users to subscribe to different events. After an event occurs, the notification is shown on the user’s device. The application requires that notification settings are synchronized across all user’s devices and because the application itself does not have information about other devices, the back-end server is responsi- ble for subscribing to available channels. The process of subscription and sending push notifications to de- vices consists of five steps:

• FCM registration — On the initial startup of the application, the FCM SDK generates a registration token for the client app instance.

• Token storing — If there is a user logged in, the generated registration token is sent to the back-end server, where it is linked to the account and stored.

• Channel subscription — User subscribes to a channel using back-end server. Two possible use-cases exist. If there is no user logged in, the device subscribes using the registration token.

29 7. Push notifications

Otherwise, only session information is needed, since the back- end server possess information about all linked registration tokens. All linked devices are subsequently subscribed.

• Notification pushing — The back-end server sends a request to the FCM server to notify devices subscribed to a channel. An example of such a request is shown in listing 10.

• Notification receiving — Application receives and processes the message in onMessageReceived callback.

{ "to": "/topics/magazine", "notification":{ "title": "Sparta", "body": "New magazine!", "sound": "default", "click_action": "SPARTA_OPEN_MAGAZINES" }, "delay_while_ide": false, "priority": "high" }

Listing 10: Example FCM topic message [19].

30 8 Testing

Application testing is an integral part of the development process. Before releasing a public version of the application, it it essential to verify its correctness and try to eliminate introduced bugs. This is especially important since the application is being released as an update and a critical error could discourage the existing audience from using the application.

8.1 Functional testing

8.1.1 Distribution

To allow parallel testing and implementation, the project flavors were created (6.1.1). This helps differentiate the current development ver- sion from the testing version. The beta version of the application is distributed using the Fabric’s Beta tool. After registering the device, testers are able to download the latest version of the application and receive a push notification with every release.

8.1.2 Execution

The testing was executed in two phases. First of all, the application was tested by the developer. This was done in parallel with the devel- opment. After each new function implemented, the feature was tested to verify its correctness. In the second phase, the company’s QA team was assigned to test the application. Multiple test cases were designed and divided into 5 categories:

• Networking — After changing the existing API (6.9) it was necessary to verify that the new implementation works correctly.

• Push notifications — Push notifications were migrated from Parse to the Firebase platform and correct behavior needed to be verified.

31 8. Testing

• Application update — The used ORM tool ActiveAndroid is sensitive to database changes, so updating the application with erroneous migration could cause the application to crash.

• Devices compatibility — The application layout files were op- timized, so testing the application on multiple devices with different resolutions was necessary.

• General functionality — The last step of the testing was an overall functionality testing. The testers were assigned to use the application and report any discovered bugs.

All bugs found were reported using the Redmine issue tracking tool (8.1). The bug tracking timeline, which presents data from the project’s Redmine, is displayed in figure 8.1.2. Total 15 new bugs were introduced during the code refactoring. 13 of those were fixed, while the 2 remaining low-priority bugs were postponed for a future devel- opment.

Figure 8.1: Example of reported issues.

32 8. Testing

16

14

12

10

8

6

4

2

0 01.08.16 15.08.16 01.09.16 15.09.16 01.10.16

Bugs reported Bugs resolved

Figure 8.2: Bug tracking during development.

33 8. Testing 8.2 Performance testing

Performance testing is a non-functional testing technique performed to determine the system parameters in terms of responsiveness and stability under various workload. Performance testing measures the quality attributes of the system, such as scalability, reliability and resource usage [20]. To find out how the optimization influenced the application perfor- mance, both of the application versions are tested and the are results compared. Two major elements are measured — application memory usage and activity launch times.

8.2.1 Activity start time

Users expect apps to be responsive and fast to load. An app with a slow startup time does not meet this expectation and can be disappointing to users. 50% of mobile phone engagements last less than 30 seconds and 90% of engagements last less than 4 minutes. With such brief periods of interaction, it is crucial that interaction is rapid and responsive [21]. Activity start times are analyzed using Android log value Displayed, which represents the amount of time elapsed between launching the process and finishing drawing the corresponding activity on the screen. To compare the measurements on different device types, two devices are selected, one of them being a high-end device Nexus 5X and the other one low-end Cubot GT99. The results are presented in graphs 8.3 and 8.4, where each shows 2 types of values. Firstly, the activity start time is measured on the initial application run. During this run, most of the application data are being down- loaded. Obviously, this is where the original version struggles. The MainActivity start time, which corresponds to the application launch time, on the low-end device reaches almost 9 seconds, while the opti- mized version is able to start in just over 3 seconds. The difference on the high-end device is not that significant, but still noticeable. Secondly, to get as relevant results as possible, 10 measurements were done and an average value was calculated. The differences here are mostly visible on the low-end device, while the high-end device handles both applications relatively well.

34 8. Testing

2,000

1,832 1,800

1,600

1,400

1,200

1,000 949

800 714

600 576

339 400 314 320 306 321 258 254 258 230 241 230 220 227 241 145 200 122

0 Main Magazine Team Cup News

Original (1st run) Optimized (1st run) Original (average) Optimized (average)

Figure 8.3: Activity start time [ms] on Nexus 5X.

35 8. Testing

9,000 8,355 8,000

7,000

6,000

5,000

3,998 4,000 3,331 3,000

2,000 1,855 1,548 1,326 1,314 1,314

813 801 1,000 633 650 653 586 421 433 371 410 370 430 0 Main Magazine Team Cup News

Original (1st run) Optimized (1st run) Original (average) Optimized (average)

Figure 8.4: Activity start time [ms] on Cubot GT99.

36 8. Testing

8.2.2 Memory usage Random-access memory (RAM) is a valuable resource in any software development environment, but it is even more valuable on a mobile operating system, where physical memory is often constrained [22]. Developers should always optimize their application to avoid applica- tion lagging or even frequent OutOfMemoryException. Android Studio contains several tools that allow memory usage investigation. One of those tools, Memory Monitor, shows how an application allocates memory over the course of a single session. This tool is used to analyze and compare both versions. Two use-cases are analyzed:

1. General memory usage — While using the application in a basic way — opening sections, viewing and fetching data the memory allocations are tracked. This use-case represents an average 1-minute long user session.

Figure 8.5: General memory usage (original) on Nexus 9.

Figure 8.6: General memory usage (optimized) on Nexus 9.

37 8. Testing

The results are shown in graphs in figures 8.5 and 8.6. Accord- ing to the graphs, the average memory usage dropped by over a third and the maximum allocated memory dropped from cca 160MB to below 90MB. This means that the implemented practices have a positive effect.

2. List scrolling memory usage — The second measurement fo- cuses on list scrolling performance. It shows the difference be- tween the original ListView implementation and the optimized RecyclerView with a ViewHolder pattern implementation. The memory is scanned while scrolling through 200 items in the News category. The results are displayed in figures 8.7 and 8.8.

Figure 8.7: Scrolling memory usage (original) on Nexus 9.

Figure 8.8: Scrolling memory usage (optimized) on Nexus 9.

38 9 Conclusion

This thesis focuses on maintenance of the selected mobile application project. Its main objective is to optimize the application and increase its stability using the latest Android development technologies. To identify main issues in terms of code quality and application perfor- mance an analysis of the source code was performed. Subsequently, the existing API was inspected to diagnose problems and to optimize application networking traffic, improvements were proposed. The implementation part focused on using code refactoring as a tool to improve code quality and implement the designed changes. A focus was put on removing unnecessary third-party dependencies in or- der to maximize the application stability. Thanks to the optimization and refactoring it was possible to remove 8 third-party dependencies in total. As the next step, the application’s push notification func- tionality was migrated from the outdated Parse service to the new Firebase Cloud Messaging service. After the implementation part, the application was tested. The company testers had discovered multiple introduced bugs that were according to their priority either fixed or postponed for future development. To determine the success of the performance optimization a performance analysis was executed to compare the optimized version with the original one. The analysis showed that the performance efficiency of the application improved most visibly on the low-end devices. The difference on the high-end devices was not that significant, but still observable. The product of this thesis is an optimized version of the selected application, that is prepared for a future development.

9.1 Future improvements

• Implement automated tests to improve application maintain- ability even more.

• Depending on the scope of future development, switch to a different, more sustainable application architecture.

39 Bibliography

[1] Stephen Schah. Object-Oriented Software Engineering. McGraw- Hill Education, 2007. isbn: 007352333X. [2] Thomas M. Pogoski. Practical Software Maintenance: Best Practices for Managing Your Software Investment. Wiley, 2008. [3] Xiaozhou Li. “Research on Agile Process Models in Mobile Application Maintenance”. MA thesis. University of Tampere, School of Information Sciences, 2014. [4] IEEE Standard for Software Maintenance. Inst of Elect & Electronic, 1998. isbn: 0738103365. [5] Chris Newton. 2012. url: http://clarityincode.com/software- maintenance (visited on 12/12/2016). [6] Vipul Patel. How to Use Android Data Binding. Mar. 2016. url: http://www.developer.com/ws/android/programming/how- to-use-android-data-binding.html (visited on 09/10/2016). [7] Google Inc. Creating Lists and Cards. url: https://developer. android.com/training/material/lists-cards.html (visited on 09/10/2016). [8] Google Inc. Improve Code Inspection with Annotations. url: https: //developer.android.com/studio/write/annotations.html (visited on 11/22/2016). [9] CodePath. ActiveAndroid Guide. url: https://guides.codepath. com/android/activeandroid-guide (visited on 11/20/2016). [10] Greenrobot. Events for Android. url: http://greenrobot.org/ eventbus/ (visited on 11/20/2016). [11] Jan Volmut. “Techniques for Code Readability Enhancement in Existing Projects”. MA thesis. Masaryk University, Faculty of Informatics, 2009. [12] Google Inc. url: https : / / github . com / googlesamples / android-architecture (visited on 11/12/2016). [13] Futurice. Best practices in Android development. url: https : / / github.com/futurice/android-best-practices (visited on 12/12/2016). [14] Google Inc. url: https : / / developer . android . com / guide / topics / manifest / application - element . html (visited on 11/25/2016).

40 BIBLIOGRAPHY

[15] Zach McCormickm and Douglas C. Schmidt. “Data Synchro- nization Patterns in Mobile Application Design”. In: (Oct. 2012). [16] Martin Fowler Fowlen and Kent Back. Refactoring: Improving the Design of Existing Code (Addison-Wesley Object Technology Se- ries). Addison-Wesley Professional, 2012. isbn: 013306526X. [17] Michael Morris. Android Patterns: Manufacturing Intents. July 2013. url: http : / / onemikro2nd . blogspot . cz / 2013 / 07 / android-patterns-manufacturing-intents.html (visited on 11/12/2016). [18] Parse. url: https://parse.com/tutorials/android- push- notifications (visited on 10/09/2016). [19] Google Inc. url: https://firebase.google.com/docs/cloud- messaging/http-server-ref (visited on 11/15/2016). [20] Tutorialspoint. 2012. url: https://www.tutorialspoint.com/ software _ testing _ dictionary / performance _ testing . htm (visited on 12/15/2016). [21] Tingxin Yan and David Chu. “Fast App Launching for Mobile Devices Using Predictive User Context”. In: ACM MobiSys. ACM, June 2012. [22] Google Inc. Manage Your App’s Memory. url: https://developer. android.com/topic/performance/memory.html (visited on 11/28/2016). [23] Robert Martin. Clean code : a handbook of agile software craftsman- ship. Prentice Hall, 2009. isbn: 978-0132350884.

41 A Naming conventions

Constant type Prefix Shared Preferences PREF_ Intent Extra EXTRA_ Intent Action ACTION_ Fragment Argument ARGUMENT_

Table A.1: String constants naming.

Asset type Prefix Example Background bg_ bg_news Button btn_ btn_home Icon ic_ ic_menu_live Image image_ image_main_player Placeholder placeholder_ placeholder_player

Table A.2: File assets naming.

Component Class Name Layout Name Activity LoginActivity activity_login Fragment NewsFragment fragment_news Dialog SelectLeagueDialog dialog_select_league Adapter item - item_magazine Custom view - view_panel_team

Table A.3: Layout files naming.

42 B Constraint layout editor

Figure B.1: Using ConstraintLayout to design menu buttons.

43 C Code samples

public class JsonObjectRequestWithSession extends ,→ JsonObjectRequest{

@Override public Map getHeaders() throws ,→ AuthFailureError{ Map params= new HashMap<>(); if (mSession != null){ params.put("session", mSession); } return params; }

@Override protected VolleyError parseNetworkError(VolleyError ,→ volleyError){ if (volleyError.networkResponse != null && ,→ volleyError.networkResponse.statusCode == 401){ MyUtils.forceLogout(mContext); } MyUtils.showRequestResponse(mContext, volleyError); return super.parseNetworkError(volleyError); } }

Listing 11: Generic request to handle user authorization.

44 C. Code samples public class DataUpdateUtils {

private static final long DEFAULT_VALUE=-1l;

public static long getLastUpdate(Context context, ,→ @DataType String type){ return context.getSharedPreferences( PREFERENCES_DATA_UPDATE, Context.MODE_PRIVATE) .getLong(type, DEFAULT_VALUE); }

public static void setLastUpdate(Context context, ,→ @DataType String type, long date){ context.getSharedPreferences( PREFERENCES_DATA_UPDATE, Context.MODE_PRIVATE) .edit() .putLong(type, date) .apply(); }

public static void clearLastUpdate(Context context, ,→ @DataType String type){ context.getSharedPreferences( PREFERENCES_DATA_UPDATE, Context.MODE_PRIVATE) .edit() .remove(type) .apply(); } }

Listing 12: Data update helper class.

45 C. Code samples

@Override public int getItemViewType(int position){ return position ==0? TYPE_HEADER: TYPE_ITEM; }

@Override public MagazineAdapter.BindingHolder ,→ onCreateViewHolder(ViewGroup parent, int viewType){ View v; switch (viewType){ case TYPE_HEADER: v= LayoutInflater.from(parent.getContext()) .inflate(R.layout.header_magazine, parent, false); break; default: v= LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_magazine, parent, false); } return new BindingHolder(v); }

@Override public void ,→ onBindViewHolder(MagazineAdapter.BindingHolder ,→ holder, int position){ final Magazine magazine= mMagazines.get(position); holder.getBinding().setVariable(BR.magazine, ,→ magazine); holder.getBinding().executePendingBindings(); }

Listing 13: Sample RecyclerView with header implementation.

46