Applying Android Architecture Components with Kotlin

In this document we are going to look at the Android Architecture Components (AAC). We will do this by applying it on a demo App. This app uses bol.com OpenAPI

Architectural Pattern

Android Architecture Components (AAC) is a new collection of libraries that contains the lifecycle-aware components, which helps you design a robust, testable, and maintainable app.

Demo App

Enough talking, let’s get our hands dirty with code.

Screenshot of the app
interface CatalogApi {
@GET("catalog/v4/search")
abstract fun search(@Query("q") q: String, @Query("limit") limit: Int = 10, @Query("offset") offset: Int = 0): Call<SearchResponse>
}
class CatalogRepository @Inject constructor(val api: CatalogApi ) {
fun doSearch(q: String, limit: Int = 1, offset: Int = 0): LiveData<Resource<SearchResponse>>{
val data = MutableLiveData<Resource<SearchResponse>>();
api.search(q, limit, offset).enqueue(object : Callback<SearchResponse> {
override fun onResponse(call: Call<SearchResponse>?, response: Response<SearchResponse>?) {
data.value = Resource.success(response?.body());
}
override fun onFailure(call: Call<SearchResponse>?, t: Throwable?) {
val exception = AppException(t)
data.value = Resource.error( exception)
}
});
return data;
}
}
class Resource<T> private constructor(val status: Resource.Status, val data: T?, val exception: AppException?) {
enum class Status {
SUCCESS, ERROR, LOADING
}
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(SUCCESS, data, null)
}
fun <T> error(exception: AppException?): Resource<T> {
return Resource(ERROR, null, exception)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(LOADING, data, null)
}
}
}
class ProductDetailActivity : LifecycleActivity() {

//...
override fun
onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
view = ProductDetailView(layoutInflater, object: ProductDetailView.Listener{
override fun search(newValue: String) {
productDetailViewModel?.search(newValue)
}
});
setContentView(view?.rootView);
productDetailViewModel = ProductViewModel.create(this);

App.appComponent.inject(productDetailViewModel!!);
productDetailViewModel?.searchResult?.observe(this, Observer<Resource<SearchResponse>> { resource ->
if
(resource != null) {
when (resource.status) {
Resource.Status.SUCCESS -> {
val product = resource.data;
val products = product?.products;
if (products != null) {
if (!products.isEmpty()) { view?.populateProduct(products.first())
}
}
}
Resource.Status.ERROR->{
Toast.makeText(this, "Error: "+resource.throwable?.message, Toast.LENGTH_LONG)
}
}
}
})
}
}
class ProductViewModel: ViewModel(){
val searchInput: MutableLiveData<String> = MutableLiveData()
val searchResult = Transformations.switchMap(searchInput){
if
(it.length >= 1) {
repository.doSearch(it)
} else {
MutableLiveData()
}
}

private lateinit var repository
: CatalogRepository;

@Inject fun init(repository: CatalogRepository) {
this.repository = repository
}
fun search(term: String){
searchInput.value = (term)
}
companion object{
fun create(activity: FragmentActivity): ProductViewModel{
var productDetailViewModel = ViewModelProviders.of(activity).get(ProductViewModel::class.java)
return productDetailViewModel
}
}
}

Android developer at ABN AMRO Bank