Blog Infos
Author
Published
Topics
Published

While we all talk about different architectures, there are other important and simple things that can be done to keep code clean and manageable in the long run. In this article, I will share how adding abstraction for using external libraries can benefit in the long run and make your code more resilient to changes when decisions to remove or replace external libraries are taken.

We don’t use libraries because of the features they provide we use libraries because we need certain features to be fulfilled.

So there are things which as a product/developer you want to have control on it can be enabling and disabling features on the fly, enabling for certain cities, user segment, showing different messages for different users, changing messages, etc. so there are multiple ways of doing it and Firebase Remote Config does that for you. One can check how to set up Firebase remote config and other use-cases here.

As a developer, we need something which helps us auto-sync our changes on the fly, and data can be primitive or of a specific type while auto-sync is something that the library provides and maintains for us we are focused only consuming.
Once you decide you want to use any particular library you can go ahead add it to your project and start using it but it is always better to think how easy it will be for me/someone on the project to change/replace the library without impacting the code. In the case of Firebase Remote Config, it is as easy as below

remoteConfig.getBoolean(key)
remoteConfig.getString(key)
.
. other available options

The above might look tempting but slowly you will see Firebase Remote Config getting into your code everywhere which is not good in the long run.

So we create simple rules for consumption that guard our code for the future.

abstract class IConfigProvider {
abstract fun getString(key: String): String
abstract fun getBoolean(key: String): Boolean
abstract fun getDouble(key: String): Double
abstract fun getLong(key: String): Long
abstract fun getInt(key: String): Int
inline fun <reified T> dataFromJson(data: String?): T? {
var configData: T? = null
try {
configData = Gson().fromJson(
data,
object : TypeToken<T?>() {}.type
)
} catch (e: JsonParseException) {
e.printStackTrace()
}
return configData
}
}

Here Gson is used and other such or your own impl can be used for converting to specific types

 

Adding Implementation for this abstraction:

class ConfigProviderImpl(private val remoteConfig: FirebaseRemoteConfig) :
IConfigProvider() {
override fun getString(key: String): String {
return remoteConfig.getString(key)
}
override fun getBoolean(key: String): Boolean {
return remoteConfig.getBoolean(key)
}
override fun getDouble(key: String): Double {
return remoteConfig.getDouble(key)
}
//and other implementations
}

Config Provider which wraps the required options and usages for its consumer

Job Offers

Job Offers


    iOS Engineer

    American Express
    London | Phoenix | New York
    • Full Time
    apply now

    Kotlin Multiplatform Mobile Developer

    Touchlab
    Remote
    • Full Time
    apply now

    Android Entwicklerin Echtzeitkommunikation

    sipgate GmbH
    Germany
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

, ,

The Evolution of Android Graphics in Android 12/13

Android 12 and 13 both added significant new capabilities to Android platform graphics, including RenderEffect, RuntimeShader, and more. At the same time, RenderScript has been deprecated and we’ve introduced the RenderScript Intrinsics Replacement Toolkit. This…
Watch Video

The Evolution of Android Graphics in Android 12/13

Daniel Galpin
Android Developer Advocate and Fast Talking YouTuber
Google

The Evolution of Android Graphics in Android 12/13

Daniel Galpin
Android Developer Ad ...
Google

The Evolution of Android Graphics in Android 12/13

Daniel Galpin
Android Developer Advocat ...
Google

Jobs

Start using it now with simple data providers one can create different data providers based on different feature modules and use them. The consumers of this implementation are least concerned about the underlying implementation of the library itself and we won’t even need to add Firebase Remote Config dependencies in Gradle for other modules.

/**
* common remote config data source for configs used in more than 1 module
*/
class CommonRemoteConfigData(private val config: IConfigProvider) {
fun getListOfEnabledCities(): List<String>? {
return config.dataFromJson(
config.getString(
"SOMEKEY"
)
)
}
fun isServiceEnabled(): Boolean {
return config.getBoolean("SOMEKEY")
}
// and other such implementations.
}

Simple usage without even knowing what is being used for this feature

 

In the same way, other modules can create a config data source and use the feature which will lead to further separation.

Benefits of such implementation:

  • Library-related code is not scattered in the code.
  • The library becomes plug and play
  • Developers can move out of Firebase Remote Config and add new implementation and it will not require code changes given we adhere to rules.
  • We hide features that we don’t want of the library as the user can only use the available options.

We can write simple wrapper around the remote config provider as well and use instead of this implementation

Another use case where such abstraction can be implemented is image libraries. As these features deal with specific functionality with image libraries we want to load the image in a specific view and have some caching mechanism and it should be able to load images from drawable, network, files, etc so creating simple abstraction with this thought process can help in the long run and we can keep changing libraries without impacting our code e.g. initially we think Picasso is good and later Glide, Coil or any other library works better in some scenarios so changing it will be way easier.

That’s it for now 🙂 If this helped you please share it with others as well and do give some claps. Also, do share your feedback as well.

This article was originally published on proandroiddev.com on March 24, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
Nowadays authentication has become common in almost all apps. And many of us know…
READ MORE
blog
Collections are a set of interfaces and classes that implement highly optimised data structures.…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu