How I Approached Modernizing a Legacy Android App
selfie by James Bray

How I Approached Modernizing a Legacy Android App

I have been developing Android apps since late 2008 when Android 1.0 was released.  Over the years I have developed dozens of applications on the Android platform, both for myself and for clients.  The Android platform has evolved and matured immensely over the past 12 years and best practices for developing apps have progressed along with these changes.  When  I developed my first Android apps, there was only one hardware form factor to target, Fragments did not exist, so development was Activity centered, and there were no design guidelines for the platform.

The Old Days or a Brief History of Android

In discussing the “Old Days” of the Android platform, I should start by discussing why I decided to develop an app on this platform versus some other mobile platforms available at that time.  The three alternative platforms I considered were iPhone (now iOS), Windows Mobile, and RIM (Blackberry).  The iPhone platform was tempting, but I was it had been out for at least a year, and I figured I would be late to the game with this platform.  Windows Mobile didn’t seem to have much traction outside of the Microsoft ecosystem and Microsoft didn’t make it that attractive to developers because of the licensing at that time.  Blackberry had potential, but they made life difficult for developers and missed their opportunity to run away with the market.  The Android platform was open source, used a language I and many other developers were already familiar with, Java, and used an open hardware platform.  I believed that the Android platform would eventually do to the mobile market, what WinTel did to the PC market, and become the dominant platform in the future, so I chose Android.

By Michael Oryl - originally posted to Flickr as T-Mobile G1 launch event, CC BY-SA 2.0, https://commons.wikimedia.org/w/index.php?curid=4871597

The HTC Dream was the first commercially available Android phone and the first apps were geared toward this hardware form factor.

Modern Android

The modern Android platform supports a wide variety of device types and provides tools and frameworks for many scenarios.  I will not go through an extensive description of all that the modern Android platform offers.  Google and others provide plenty of detailed documentation along these lines and I have included links to some of these resources at the end of this article.  I do want to highlight a few features that have had the most impact on my development.

Fragments

Fragments have been a part of the Android platform for a long time, but I started developing for the platform before they existed, so I am mentioning them here.  When I released the first app I developed on the Android platform in 2009, the only controller-like component in the Android architecture was the Activity.  The Activity handled everything from UI navigation, to a delegation of data retrieval, along with most other logic.  Complex apps required multiple Activity classes.  Using activities to control an Android app felt a bit clumsy and awkward, but it worked in a world where there was only one hardware form factor.  Once multiple hardware form factors became available in the Android ecosystem, the Activity centric architecture became unworkable and the Android community provided the Fragment as a more elegant controller component.  

Android introduced Fragments in Android 3.0 (API level 11) in 2011.  Fragments have allowed Android developers to manage the view hierarchies of larger hardware form factors, such as tablets and larger phones, without having to create complex custom code.  I consider this change, the most important in the evolution of the Android platform.  One of the many key benefits of the Fragment is how this object allows a developer to have some control over the lifecycle of elements within an Android application.  Activities do not offer developers this level of control.

Material Design

Google announced the introduction of Material Design to the Android platform at their Google I/O conference on June 25, 2014.  Prior to this, there were no real design guidelines for Android, consequently, developers were free to use any design philosophy they wanted not to use one at all, and as a result, they did just that.  Android app design was inferior to what was available on the iOS platform and in general, was awful.  

The most fundamental thing that Material Design brought to the Android platform is how an Android platform should look and behave in a broad scope.  This is something the iOS/iPhone platform had from inception.  Material Design brought some design discipline to the Android platform without greatly inhibiting design innovation.  Android designers and developers can follow some general guidelines and pick and choose specific guidelines and tools as desired.  

In addition to providing a design standard for Android applications, Android introduced frameworks and stock icons and images that developers can use to implement the Material Design paradigm.  The core Material Design framework can be added to an Android app "gradle.build" configuration as shown below.

implementation "com.google.android.material:material:$supportlibVersion"

Android app design has become much easier and much more consistent with the introduction of this design framework.  I have also found that it is easier to apply some principles of Domain Driven Design using Material Design.  In particular, I have found it easier to follow the principle of using a ubiquitous language in my development of Android applications, because I don’t get overly bogged down in the details of design implementation.

Kotlin

Google announced support for Kotlin as a first class Android development language at Google I/O 2017.  Support for the language was first included in the Android Studio 3.0 IDE.  I could write an entire article on the benefits Kotlin has brought to my Android development and refactoring efforts, but since that is not the primary focus of this article, I will try to keep this brief.

Some advantages Kotlin brought to the platform include concise language syntax, coroutines to allow potential blocking tasks to run in the background, and compatibility with Java to allow a gradual transition if required.  I have also found it easier to context switch if I am working on server-side logic to support my mobile apps since I can code both in Kotlin.

I have provided an example below of some of the differences in my Kotlin versus my legacy Java implementation of inflating a UI layout.

Jetpack

Google introduced Android Jetpack at Google I/O 2018.  This is one of the newest major major updates to the Android development platform.  The libraries included in this framework cover areas that include:

  • Improved Activity Management
  • Fragment Management
  • Easy Navigation control
  • Updated Camera libraries and utilities
  • New Dependency Injection library

Plus there are many other libraries and tools that make Android development much easier.

How I Approached Modernization

One of the first questions I asked myself once I decided to modernize my existing apps, was “how do I modernize my apps?”.  I started by asking myself some basic questions about my existing apps:

  1. What features do I currently use in each of my apps?
  2. Why do I have those features in each app?
  3. Are those features needed, useful, or desired by the end users of each app?
  4. What features should exist in each rewritten app?
  5. What is the best way to implement the desired features?
  6. What architectural patterns should apply to my app

As I asked myself these questions, I realized that I should focus on a single app to see which patterns might work for modernization.  I decided to select an app that I recently worked on, but that had code going back more than 5 years.  The first app that I put on the predecessor to Google Play, was a fitness app with a very simple interface.  I have made various versions of the app over the past 11 years, but have not yet brought it up to modern standards.  

Rethinking the App

I started by rethinking the fitness app that I developed years earlier.  I listed all of the features that I included in the original app.  More importantly I looked at the target end user and who actually downloaded the app over the years.  I have received feedback on the app from users on Google Play over the years and have an idea of the strengths and weaknesses of the app.

User Focus

I put effort into identifying the users of my app.

Architectural Patterns

I decided to apply some basic architectural patterns in my code in order to create a consistent and easy to maintain codebase.  Two architectural patterns I applied to my modernized code are the Model-View-ViewModel (MVVM)  pattern and basic dependency injection.

Modern Approaches

I started modernizing the core functionality of the app, such as navigation and display, to better align with modern Android development.

Fragment and UI Management

Fragments were introduced to the Android ecosystem only a few years after the platform had been in existence, so I hardly consider using Fragments a modernization step in this day and age.  I moved away from the Activity centric controller model long ago in favor of Fragments for apps I develop.  The modernization I undertook reflects the architectural choices I made in my app rewrite.  My legacy app used Fragments as the controllers in a Model-View-Controller (MVC) architecture.  My modern Android apps still use Fragments as UI controllers but use the Model-View-ViewModel (MVVM) pattern.  The MVVM architectural pattern allowed me to move logic that wasn’t purely in the domain of UI management out of the Fragment.

Technology Updates

There were several technology updates I made to the application.  These updates included adding a modern dependency injection framework, updating persistence management, and implementing navigation controls.

Dependency Injection

Adding the Hilt dependency injection framework is probably the biggest change that I made to the infrastructure of the app outside of switching from Java to Kotlin.

Hilt is a dependency injection framework that is built on top of the Dagger DI.  It bills itself as an opinionated DI framework which removes a lot of the boilerplate code that developers using Dagger had to implement.  Hilt is to Dagger what Spring Boot is to the Spring framework.  Like many of the Jetpack technologies, the “magic” in Hilt comes from auto-generated code.

Alternatives to Hilt include manual dependency injection, Dagger, and Koin.  A full comparison of these frameworks is beyond the scope of this article, but I will highlight a few of the advantages and disadvantages of each one. 

Manual Dependency Injection

I can write an application container class and expose objects that I want to inject in my application via my own custom code.  

Dagger

Dagger is a relatively mature DI framework in the Android world and has a lot of support.  Dagger also has a rather steep learning curve and requires developers to create more boilerplate code than the alternatives.

Koin

Koin is relatively mature and is less verbose than Dagger, however, Koin does require some infrastructure coding to make it work.

Hilt

Hilt offers developers the most concise code for implementing DI and is built on top of a mature framework.  The flip side of the previous statement is Hilt is the least mature of the three major DI frameworks.  The opinionated nature of Hilt can be an advantage or disadvantage depending on whether the developer using the framework agree with its opinion.

Hilt allowed me to reduce the amount of boilerplate code in my app which has resulted in a cleaner codebase so far.  It also makes unit testing a bit easier since mocking objects is easier than before.

Navigation

The Android Navigation graph provides an elegant and relatively easy way to manage the navigation logic in an Android application.  It is a vast improvement of the legacy callback scheme that was often used for navigating between screens in legacy applications. The navigation graph is as close to drag and drop navigation as I could imagine. The navigation code is autogenerated in Android Studio. I am not a big fan of autogenerated code, but I must admit, they did an excellent job of implementing this in Android Studio.

No alt text provided for this image

The three major components of the Android navigation framework are the Navigation Graph, the NavHost, and the Navigation Controller. I could write an entire article dedicated to the navigation framework, but that is beyond the scope of this article. I have provided a link in the references section for anyone who would like to take a deep dive into this.

Data Binding

Another big change in my code is the use of the Jetpack data binding framework in my code.

Honorable Mentions

  • Data Persistence with Room
  • Espresso testing with JUnit 5

References


Becker Cuellar

Owner, bsc inc

10 个月

Good break down I have a few legacy apps to upgrade and I'm trying to decide on just make them work or redo them either with Angular or the latest stuff from Android

回复
Timothy Wilson

Software Engineer

4 年

Nice article James.

回复

要查看或添加评论,请登录

社区洞察

其他会员也浏览了