This blog will illustrate about how social links are are cached in the Open Event Android Project. We needed social link viewing in the app so that the User is able to reach out to the organizer in a much better way. After that we needed caching to store the social links in our database. Let’s head to the code.
1. Change the social link model
The first step is to change the social link model. We are caching social links using the Room Database. So, we need to add the annotation @Entity to the top of the model to represent the model as a table of type SocialLink in the database. We then add the annotation @PrimaryKey to the id field as every table in a room database requires a primary key for reference.
Social link model code :-
@Type(“social-link”) @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) @Entity data class SocialLink( @Id(IntegerIdHandler::class) @PrimaryKey val id: Int, val link: String, val name: String ) |
2. Create the SocialLinksDao file
This file lists all the possible methods by which the entity in a database can be reached. These methods help in fetching information from the database, as well as updating information in the database. We implement three methods, insertSocialLinks(), deleteAll() and getAllSocialLinks(). insertSocialLinks() helps us in putting social links in the database. deleteAll() helps in deleting all social links from the database. getAllSocialLinks() helps in fetching all social links of a specific eventId
@Dao interface SocialLinksDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertSocialLinks(socialLinks: List<SocialLink>) @Query(“DELETE FROM SocialLink”) fun deleteAll() @Query(“SELECT * from SocialLink WHERE event = :eventId”) fun getAllSocialLinks(eventId: Long): Flowable<List<SocialLink>> } |
We receive Social Links in the form of a Flowable while fetching from database. As Flowable cannot handle errors correctly, doOnFinally() will not work and thus doOnNext() is used in the SocialLinksViewModel file.
fun loadSocialLinks(id: Long) { compositeDisposable.add(socialLinksService.getSocialLinks(id) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe({ progress.value = true }).doOnNext({ progress.value = false }).subscribe({ socialLinks.value = it }, { Timber.e(it, “Error fetching Social Links”) error.value = “Error fetching Social Links” })) } |
3. Change the SocialLinksService file for storing social links in Database
We change the SocialLinksService file. We change it in such a way that social links are fetched from the server, only when they don’t exist in the database ( for a particular eventId )
fun getSocialLinks(id: Long): Flowable<List<SocialLink>> { //fetch social links val socialFlowable = socialLinksDao.getAllSocialLinks(id) return socialFlowable.switchMap { if (it.isNotEmpty()) socialFlowable else socialLinkApi.getSocialLinks(id) .map { socialLinksDao.insertSocialLinks(it) } .flatMap { socialFlowable } } } |
We will go through the code in steps. First of all, we declare a variable, socialFlowable, which is assigned a value of a Flowable<List<SocialLinks>> fetched from the database. The getAllSocialLinks(id) takes eventId as parameter. Secondly, we assign a switchMap which is used to return the value of socialFlowable if it is not empty. So, if there have been some social links fetched from the database, we just return them without even making a network call.
If socialFlowable is actually empty, the else part of the conditional takes care of the network call. getSocialLinks(id) return a Flowable<List<SocialLinks>> object. .map is used to insert all the fetched social links. .flatMap is used to return socialFlowable to the function after the process.
4. Modify the network call in SocialLinkApi file
SocialLinks have relationship with event. To store them in the database, we distinguish each of the SocialLinks using eventId. The eventId will not be fetched in the regular network call, so we modify it.
interface SocialLinkApi { @GET(“events/{id}/social-links?include=event&fields[event]=id&page[size]=0”) fun getSocialLinks(@Path(“id”) id: Long): Flowable<List<SocialLink>> } |
We thus include event relationship. The JSON response will now contain a an event model inside relationships. The event model will have its id and type (event).
4. Create EventId model
This model will contain the eventId. The model will be of type event.
@Type(“event”) data class EventId( @Id(LongIdHandler::class) val id: Long ) |
4. Modify the SocialLink model to include relationship with event
We modify the model to include event relationship. As we need to store just the id of the event, we create another class named EventId and use a TypeConverter to convert the EventId model to a primitive type Long while compilation. We also include a foreign key which references the id of the event with the event field in the SociaLink model.
@Type(“social-link”) @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) @Entity(foreignKeys = [(ForeignKey(entity = Event::class, parentColumns = [“id”], childColumns = [“event”], onDelete = CASCADE))]) data class SocialLink( @Id(IntegerIdHandler::class) @PrimaryKey val id: Int, val link: String, val name: String, @ColumnInfo(index = true) @Relationship(“event”) var event: EventId? = null ) |
4. Create the TypeConverter
We create the TypeConverter for converting EventId to just the id (Long).
class EventIdConverter { @TypeConverter fun fromEventId(eventId: EventId): Long{ return eventId.id } @TypeConverter fun toEventId(id: Long): EventId{ return EventId(id) } } |
4. Modify the Modules.kt file and the OpenEventDatabase
In the OpenEventDatabase file, we include the SocialLink entity to the Database. The annotation @TypeConverters is used to specify the type of TypeConverter to be used. Here we use EventIdConverter. In the abstract class, we include a function socialLinksDao() of type SocialLinksDao. It will fetch all the available functions from the file.
@Database(entities = [Event::class, User::class, SocialLink::class, Ticket::class], version = 1) @TypeConverters(EventIdConverter::class) abstract class OpenEventDatabase : RoomDatabase() { abstract fun eventDao(): EventDao abstract fun userDao(): UserDao abstract fun ticketsDao(): TicketsDao abstract fun socialLinksDao(): SocialLinksDao } |
In the Modules.kt file we add EventId class to the JSONAPIConverterfactory.
Retrofit.Builder() .client(get()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JSONAPIConverterFactory(objectMapper, Event::class.java, User::class.java, SignUp::class.java, Ticket::class.java, SocialLink::class.java, EventId::class.java)) .addConverterFactory(JacksonConverterFactory.create(objectMapper)) .baseUrl(baseUrl) .build() |
In the same file, we add another factory object. This is about the fetching of the function of socialLinksDao in the OpenEventDatabase file.
factory { val database: OpenEventDatabase = get() database.socialLinksDao() } |
Thus, after all the modifications, we are able to achieve the feat of caching of social links in the app successfully!
References
Tags: GSoC18, FOSSASIA, Open Event, Android, SocialLinks Caching
You must log in to post a comment.