Blog
  • Features
  • Pricing
  • About
  • Blog
  • Log In
  • Try Free
  • Menu
Features Pricing About Blog Log In
  • Category ▾
  • Recent ▾
  • Popular ▾
  • Abacus Updates
  • Business Travel
  • Cloud Accounting & Finance
  • Employee Happiness
  • Modern Office Operations
  • view all posts
    • Why Fintech Wants to Free Your Credit Card Data

      Inside the movement to give small and medium-sized businesses an enterprise level of data ownership.

      Modern Office Operations
    • Abacus Completes SSAE 18 / SOC 1 Type II Certification

      Abacus is excited to announce that we have completed our Service Organization Controls (SSAE 18 / SOC 1) examination.

      Abacus Updates
    • The 5 Biggest Takeaways From the 2017 IRS Data Book

      The IRS has released a report on the agency’s activities in fiscal year 2017, from October 2016 through September 2017. Here are the 5 biggest takeaways.

      Modern Office Operations
    view all posts
    • 7 Questions We Have About Deductible Expenses Under The New Tax Law

      With the most sweeping tax law in a generation taking effect, here are the questions we have outstanding about deductible employee expenses.

      Cloud Accounting & Finance Modern Office Operations
    • 2017 at Abacus
      Reviewing (And Approving) 2017 at Abacus

      At Abacus, 2017 was full of feature launches, new faces, and big initiatives. Here’s a look back on how we spent the year.

      Abacus Updates
    • A selection of woodworking tools.
      Stop Bashing Excel. Start Using It Right.

      Spreadsheets aren’t meant to manage your critical finance processes. Here’s how to use the right tools for the job alongside Excel.

      Cloud Accounting & Finance Modern Office Operations

    Taking Ownership of the Abacus Android App

    By: Prashanth Sadasivan - January 13, 2016

    Abacus-Andoid-App

    When I was hired at Abacus, my job was to take ownership of the Android application. I was thrown in the metaphorical deep end, tasked with some pretty large and exciting changes to how the application would work. We wanted to completely makeover the compose expense workflow, as well as implement the ability for admins and managers to approve expenses on Android.

    This post covers my journey of taking ownership of the Abacus Android app, and hopefully helps other Android developers in similar situations.

    Setting the stage: the state of the Abacus Android app

    The app was originally written by Josh, our CTO, and extended over time by other engineers at Abacus. Considering that none of the engineers specialized in developing for Android, the app wasn’t in bad shape. I went through the app code, and noted a few good things and a few things that I wanted to improve.

    What I liked:

    • It had a very flat package directory, which made it easy to navigate.
    • It used 1:1 Activity-Fragment pairs everywhere.
    • It used Volley to do network requests, which is better than HttpURLConnection.

    Things I wanted to change:

    • No Entity/Model layer
    • No tests
    • No JSON Object mapper, only JSONObject and JSONArray

    After talking with Josh and the other engineers who had worked on it, familiarizing myself with Abacus, and reviewing the app, I decided to jump head first into the code.

    Make a model/entity layer

    Wanting to learn more about the capabilities of the Abacus app, the first thing I looked for was a model or entity layer, but there weren’t any to be found. As I sifted through the code, I saw that the app mainly used JSONObject and JSONArray from the org.json[2] library (the one that comes default with Android) directly, as opposed to mapping them to POJOs [0]. While org.json has it’s uses, I prefer solutions like Gson[1] or Jackson [2], which allow you to map JSON to classes.

    As an example, let’s say you had a JSON structure describing an entity that looks like this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
        “id”: “id_123”,
        “company_id”: “company_id_2”,
        “is_admin”: false,
        “employees” : [ “user_id_1”, “user_id_2” ]
        “metadata” : {
            “is_remote”: true
        }
    }

    In order to read the “is_remote” key in the “metadata” object using org.json, you’d have to write something like

    1
    2
    3
    4
    5
    6
    7
    8
    try {
         JSONObject entity = new JSONObject(jsonString);
         is_remote = entity.getJSONObject(“metadata”).getBoolean(“is_remote”);
        } catch (JSONException e) {
        //Since org.json throws checked exceptions,
        //you have to catch all exceptions, which are
        //usually not recoverable, and just ignored.
        }

    While it’s not completely terrible, it has some flaws:

    • The JSON keys appear as strings everywhere.
    • If the JSON structure changes, it’s hard to know where in the code base the keys are used (not checked at compile-time)
    • Checked exceptions, which leads to a lot of boilerplate try-catch blocks.

    On the other hand, using a JSON-Object mapper like Gson or Jackson helps with some of these flaws. If you write a POJO and directly map the JSON string to the class, it means you get compile time checking everywhere that entity is used.

    That would look something like this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Entity {
         public String id;
         public String company_id;
         public boolean isAdmin;
         public List<String> employees;
            //Gson and jackson can deserialize lists
         public Metadata metadata;
            // and even more complex objects!
    };
     
    // To deserialize from JSON
    Gson gson = new Gson();
    //Usually this gson would be a singleton somewhere in the app.
     
    Entity e = gson.fromJson(jsonString, Entity.class);
    // e has all it’s fields filled from the JSON
     
    e.getMetadata().isRemote();

    (If you’re curious about these libraries, I posted the links below)

    With the Abacus application, I began by writing an entity class for each object the API returns, digging through the Abacus Node.js backend source code to understand what the different fields meant and how they related to each other. After that, I included Gson, configured it for our needs (Gson has tons of awesome configuration options), and integrated it with Volley, the HTTP request library the app used. As I went about this process, I realized that if I planned to make a lot of changes across the app’s code, I couldn’t confidently say that I didn’t break anything without tests.

    Testing 1, 2, 3

    Before I got too deep into making changes throughout the app, I decided the safest way to confirm I don’t break anything was to build some tests. There are a lot of resources when it comes to testing android(articles, github repositories, etc) but the gist is: it’s hard. There is no silver bullet, but you should do it even if it’s not perfect. It helps prevent you from breaking features when you want to refactor the code, and gives you more confidence than coding blind. Before I changed anything, I decided to start with an End-to-End test, and try to write smaller unit tests whenever possible.

    It sounds self-evident, but it’s difficult to write unit tests for an Android app if the code wasn’t written with unit testing in mind. Specifically, unit testing Android activities and fragments is possible, but unless the app was written with strong separation of concerns, it’s daunting to jump into unit tests, even with a framework like Robolectric. End-to-End tests, on the other hand, can be written (mostly) without changing any of the app code. This meant that once I started making changes across the code base, I could be reasonably certain that those changes didn’t break core user flows across the app.

    I used Espresso [5] for my end-to-end tests. I decided to go with an approach where the app communicates with the server that runs locally, as opposed to an approach which involves mock responses from a fake server or stubbing. The benefit is that there are drastically fewer differences between the testing environment and the production environment. The app talks to a server, makes network requests, and serializes and deserializes requests and responses like it would in the production environment. The test runs through the full stack of the app, as opposed to orchestrating different layers of the app. The cost of this approach is that it greatly increases the dependencies that the tests need to run, and makes it more difficult to get started running tests. You need to have an up-to-date copy of the API running locally and seeded with some test data. If your test

    There are always tradeoffs with tests, but I choose to because it gives me more peace of mind.

    Mindfully refactor as you go

    With tests in place and a well built entity layer, I began implementing my first feature at Abacus! As I began implementation of the feature, I started running into blocks of code which were using the old style JSONObject. I really wanted to refactor those blocks to start using the entity classes that I had added to the code base, as well as start to incorporate more of the libraries that I included. While these blocks of refactoring ultimately improved the state of the code, I found myself much less productive during these stretches. The context switches between implementing the feature and reworking the code to use entities and the new libraries were extremely draining.

    I found that depth-first refactoring was not sustainable. It was a rabbit hole and exhausting. I decided to change my process and instead of refactoring as I touched blocks of code, I started by implementing the feature using the original conventions of the app. I would then commit that code with a test to make sure that it was working properly. Now that I had the test in place, I could refactor the code I just committed, and once the refactor was done I could be sure that I hadn’t broken anything. Although this iterative process required self-constraint, I was much happier when I stopped myself from refactoring the world and wrote down the next steps I wanted to take instead.

    Putting your own spin on things

    In the first few weeks, I was hesitant to change the app too much. I didn’t want to add more complexity to the app without fully understanding how it was architected or why it was built that way. I was also new to Abacus and was navigating our engineering process, and wanted to maintain the Abacus culture as I started touching the code. But once I became comfortable with the app code, I decided to start switching to the tools that I’m more comfortable with which are well vetted by the Android community. Things became easier to change and test once I started using tools like Dagger[6], Retrofit[7], Gson, and others.

    Taking ownership means owning and deciding what is best for the app’s future. You can’t do that successfully without thinking about the architecture and managing legacy code. I read several articles and posts about the different architectures which are becoming popular on Android and I strongly recommend checking out the recent trends for yourself. While it might not be the best idea to migrate the entire app to the CLEAN[8][9] architecture, understanding it may help inform you on how best to proceed with implementing new features.

    I hope my experiences help you figure out where to start when taking ownership of an Android app. Feel free to drop me a message at prashanth@abacus.com if you have any questions!

    [0] https://en.wikipedia.org/wiki/Plain_Old_Java_Object

    [1] Gson: https://github.com/google/gson

    [2] Jackson: https://github.com/FasterXML/jackson

    [3] org.JSON: http://www.json.org/java/

    [4] Volley: http://developer.android.com/intl/es/training/volley/index.html

    [5] Espresso: http://developer.android.com/intl/es/training/testing/ui-testing/espresso-testing.html

    [6] Dagger 2: https://github.com/google/dagger

    [7] Retrofit: http://square.github.io/retrofit/

    [8] CLEAN Architecture: https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

    [9] Android CLEAN Architecture: http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/

    [10] Android Weekly: http://androidweekly.net/

    Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

    No Comments ▾

    Leave a Reply

    Click here to cancel reply.




    Related Posts


    • Abacus Completes SSAE 18 / SOC 1 Type II Certification

      April 9, 2018



    • Laying The Groundwork For More Powerful Expense Reporting

      February 19, 2018



    • Reviewing (And Approving) 2017 at Abacus

      December 26, 2017


    • Speak to a Solutions Consultant

      Get a personalized look at how real time expense reporting can improve your workflow.

      Schedule Now

    Popular view all posts

    • 7 Questions We Have About Deductible Expenses Under The New Tax Law

      With the most sweeping tax law in a generation taking effect, here are the questions we have outstanding about deductible employee expenses.

      Cloud Accounting & Finance Modern Office Operations
    • 2017 at Abacus
      Reviewing (And Approving) 2017 at Abacus

      At Abacus, 2017 was full of feature launches, new faces, and big initiatives. Here’s a look back on how we spent the year.

      Abacus Updates
    • A selection of woodworking tools.
      Stop Bashing Excel. Start Using It Right.

      Spreadsheets aren’t meant to manage your critical finance processes. Here’s how to use the right tools for the job alongside Excel.

      Cloud Accounting & Finance Modern Office Operations

    Get the Buyer's Guide to Expense Reporting Software

    Download

    Product

    Features Pricing Feature List Recruiting Security Why Real Time?

    Company

    About Blog Careers Press

    Integrations

    QuickBooks Online QuickBooks Desktop Xero Netsuite

    Resources

    Pinterest Case Study Betterment Case Study Flexport Case Study

    Get In Touch

    Support hi@abacus.com
    We're hiring!

    © Abacus Labs, Inc.

    Terms of Use Privacy Policy