Best Practices for building Android SDKs

To hone a new skill, you need to practice and its not just the quality but also the quantity. Scientists also believe that expert-level performance is primarily the result of expert-level practice and NOT due to innate talent. This is called deliberate practice. While this can be applied to a sport like golf it also applies to software engineering.

In a previous post I talked about How to write SDKs. It was a generic article. This article is very specific to Android and states, what are some of the best practices for writing and publishing performant and good android SDKs/libraries.

The order of the topics have nothing to do with priority or importance.

1. Say No to AsyncTasks

Async Tasks internally run in a thread pool executor which has a queue implementation. A maximum of 128 pending tasks are allowed in the queue. In case you use async tasks as an event bus you will end up creating a lot of tasks and eventually end up getting a RejectedExecutionException. Well, that’s not the primary reason though. Since you are writing a library and not the app, the app developer’s async tasks might get delayed because your tasks are in the queue. This might cause the app to perform poorly since the app developer is not aware that you are using the AsyncTasks.

2. Using Android Support Annotations

With android support annotations you can remove a lot of boilerplate code for null checks. NonNull annotations enables you further to ensure null values are not passed on to your methods. Let’s say your method might return a null value, Nullable annotation helps to denote the same. All this is part of the android support library. Worker thread annotations in the same way denote that it needs to be called from a worker thread and not a UI Thread.

3. Keeping a low method count

Don’t adopt the approach of writing 800 line methods. It will make it unmanageable. Including external libraries which you might not need might also cause your SDK to bloat up. Also clearly mention method count for your library. There are tools for that as well. Don’t try to optimize even before you start writing the code. You can always iterate and publish improvements. Do not use Apache HTTP library use OKHttp if you target devices higher than 4.4 or simply use URLConnection

4. Manifest Merger and Groovy variables

Android studio inherently supports manifest merger. You can very well include your manifest declarations in your own manifest file and not bother the app developer to include it for you. Yes there might be certain cases in which you might need to explicitly ask the developer to include certain components. Groovy comes with an advantage where you might need to include the package name or certain strings. For eg, for package names you can use the variable ${applicationId}.

5. Check your threads

You might have methods which perform heavy or long running operations. You might expect these to be called only from worker threads. In case you do not want to use android support annotations then check for the calling thread is the main thread or not.

6. Leverage Platform components

Android platform has a lot of utilities and components which might help you write effective android code. This stretches to even building apps and not SDKs alone.

7. Optimize network calls

Don’t call an API until and unless you absolutely need it. If it is possible to batch APIs do that. If you need to sync data on a regular basis but do not have an urgency to do that, consider using the sync manager. Configure rules like when charging and connected to WIFI. If you need to fetch data which might be expensive, try to pre-fetch it when the user is connected to an un-metered network. You can never expect in what network conditions the app might be used in, 2G/3G/4G/WiFi. If data is critical you should account for networking issues like packet drop and partial failures.

8. Optional needs

The classic example being the Google advertising identifier. All analytics and engagement SDKs want to extract every information they can. Some enforce or include the Google Ads SDK just for the advertising identifier. But the app might not need the ads SDK. You can simple user reflection to figure out whether the app includes the ads SDK or not. If it includes it then use reflection to invoke the methods and get the information.

9. Check for permissions rather than taking them

With runtime permissions this becomes really critical. You might not always get a permission for a specific feature, code to handle the same. This does not apply for only Android M, since you are building an SDK and apps might not have all of your optional permissions.

10. Use JCenter or MavenCentral for distribution

Your library is as good as your distribution channel. If you have an awesome library but you expect people to download the jar from some drive location and you are not on github, I would say do not even take the effort to write that library. User JCenter/Bintray or MavenCentral for distribution, this makes it easily available and also helps in updating by simply changing the version number.

11. API Naming Conventions

Android API structure and naming convention has always attracted my attention. The more you dig into the framework code, the more you learn. Take the example of an OnClickListener. Everywhere you go and you want to set a listener for handling a click, you will find an interface to implement and set it as a callback. Just image if it was called OnViewClickListener, OnDialogClickListener, how difficult it would get for a developer to find it and use it.

If you have a similar behavior which exists in the android framework, it would be wise to use similar naming conventions and design patterns so that the library usage feels an extension of the framework. Be advised, I am not encouraging you to use android as part of your package name. While I can go on writing about APIs you can read up on how to design APIs which id documented well in Anup Cowkur’s article.

12. Go for minimum integration effort

The integration effort required for integrating your library should be as low as possible. You have 2 advantages here, 1st it would be more preferred by other developers, because of less setup time, 2nd the number of mistakes another developer would make to get things working will also be very low.

13. Minimum permissions

If you do not need it don’t ask for it. You are building a library not an app. But this library can be used in various apps which might and might not have permissions. If you are a networking library which uses internet, there is no need to ask for ACTIVITY_MANAGER, SENSOR, LOCATION etc.

14. Effective Obfuscation

Do not obfuscate your own library until when shipping the library; unless you have some intellectual property associated with it. If you are obfuscating your library then also provide the proguard mapping file. This would be required by folks who use your library and would need to know when an exception or error occurs. The best approach would be to write your rules in the proguard configuration file and use AAR format for shipping your library. That way when a release version of the APK is built (without rejecting your configurations), the library would be obfuscated along with the app code.

15. Testing the Integration

Testing is important but integration is also a test case. Imagine where a library upgrade scenario can have issues, or integrating with different support library versions can have issues. All this would come out if you are testing the integration yourself.

16. Versioning and release notes

I prefer Semantic Versioning. It directly tells you what this update is meant for. DO NOT skip versions. In some cultures, number 4 is considered unlucky. I have seen people going from version 3 to 5 without having a version 4. Your audience in the digital world reaches beyond countries and time zones. It would be difficult for someone else to understand why you did that. Think of it this way for someone else number 5 might be unlucky.

17. Account for changes/updates in your own dependencies

A library can have and most of the time does have dependencies. In that case you should always account for any upgrade of those dependencies. Lets take Google Play Services for an example. Sometime back Google released an update which was not compatible with older Play Services version. You might have developers using both the older and newer play services version. In that case, you would need to update your own library as soon as possible.

18. Exception handling

This is one of the most important one. When writing a library keep in mind, that it can be or will be used by many apps and crashes will affect all those who use the library. The smallest of mistakes can cause all your library users to simply move away from your library. In most cases its the stupid NullPointerException. While you are not getting the Optional class available with Java 9 anytime soon, its advisable to run the code with some static code analysis tools, like Infer from Facebook, FindBugs, PMD etc. You can very well configure these with your existing CI which would make your life really easy.

19. Follow platform and developer guidelines

While I say this always and will keep saying this. Whatever you need to know has been well documented by the Google Developers in the developer documentation and blog posts.

While I can go on writing on this, and probably have a few more points to mention here, but I’ll keep this post limited or I’ll never end up publishing it. I started this post 3 months back, I was not getting enough time to complete it. I just thought of hitting publish or else it would never end.


Originally published at www.puremetrics.io.

Like what you read? Give Abhishek Nandi a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.