user842225
user842225

Reputation: 5989

Refreshing data showing on the screen from backend in my case

My Android project is using MVVM pattern written in Kotlin.

In my app, I have one screen that shows a list of data of a stock market. The stock market is specified by user input (user input the name of stock market & press "enter") & the list of data is got by sending a request to the backend. In practice, the user enters the market name e.g. "Market-A" or "Market-B".

Now the business requirement is that if the user enters "Market-A" and entered the data screen, the data should be refreshed automatically every 5 seconds, whereas if the user enters "Market-B" the refresh interval is 10 seconds. So, I need to periodically send a request to the backend with different time interval according to which stock market user is interested in.

The request of getting data for each stock market hits the same endpoint but different parameter in endpoint URL for the MARKET-NAME:

that's GET https://foo.io/{MARKET-NAME}/data, just replace the placeholder {MARKET-NAME} with what user inputs.

My question is what is the best approach to implement the automatic data refreshing feature?

I am using MVVM with LiveData. For the networking request, I am using Retrofit library.

I know there is WorkManager but I don't think it is the right component for my case since I don't need this data refreshing when the app is in background. Any suggestions? (If you suggest RxKotlin I would like to hear more & I am also curious what would be the solution if not using RxKotlin)

Upvotes: 3

Views: 1171

Answers (3)

daneejela
daneejela

Reputation: 14213

I advise you to take advantage of MVVM observable nature.

The standard, MVVM way, IMO would be following:

  • Have Room table MyData (or whatever you are fetching)
  • Have MyDataDao fun getData() returning LiveData<List<MyData>> ()
  • in your viewModel class have a LiveData<List<MyData>> object that is filled with getData() function that view can observe

--> now you have observing part fixed - livedata/room magic will fire data change every time when data updates.

For updating/fetching part you can use really anything -> a CountDownTimer which has an interval set to the desired value. Within timer's onTick you can start a coroutine executed on Dispatchers.IO or any background thread that sends a request to the server and saved data to local Room DB.

Again, LiveData/Room magic will update data change every time you update this table, which means that your object holding data in viewModel will change, and view observer will be fired (there you can refresh your recycler view with new data).

Let me know if you need more implementational details.

Upvotes: 1

Wale
Wale

Reputation: 1806

Hi user842225, This will an interesting discussion/opinion rather than a QA.

First:

You said : "I don't need this data refreshing when app is in background." So, you really don't have to use any background process at all! else you just forfeited the purpose of background services!

Second:

You said something about RxKotlin... Which makes me wondering why? let say you're gonna use RxKotlin: So, you will be making your network calls using Rx which purpose is to perform an Asynchronous operations and return results to the main thread when possible....well, I think Retrofit already does the Asynchronous thing itself, look at => Call<T> in Retrofit, used somewhere in your code like this=> call.enqueue.

Enough of the this, my suggestion is as below:

What I want to do: let say I want to change something on a page periodically as long as the page is running and visible, I don't really care what happens when the page is closed.

First thing that comes to my mind: is a timer kind of idea to schedule my operations and an Handler kind to help keep track of threads so as to be able to cancel/shutdown operation when necessary.

Like I said Enough of the talking! pls examine the code below for clarity, not much code but much comments to better explain the process:

object JavaApplication2 {
        private var beepHandler: ScheduledFuture<*>? = null
        private var scheduler: ScheduledThreadPoolExecutor? = null
        private var interval: Long = 0

        @JvmStatic
        fun main(arguments: Array<String>) {
            val scan = Scanner(System.`in`)
            println("How often so you want me to do refresh this request? e.g 10 or 5")
            interval = scan.nextLong()
            startExecutor(interval)
        }

        /*One of the most important method in our program, this helps start fresh Executor and
        timed it according to what we want*/
        private fun startExecutor(interval: Long) {
            scheduler = Executors.newScheduledThreadPool(1) as ScheduledThreadPoolExecutor

            /*Below method just in case you wanna be more bad-ass, you can simply use this same scheduler to
            run the new task which is of a different time interval, this I can't really explain now
            but you can look at how be to make use of this to save recreating of Executor all the time
            you need to change the operation. Well, not really compulsory, so I will comment it out*/
    //      scheduler.setRemoveOnCancelPolicy(true);

            //Now we instantiate and schedule
            beepHandler = scheduler!!.scheduleAtFixedRate(MarketClass(), 1, interval, TimeUnit.SECONDS)

            /* Now, below is the trick, we simply set the repetition operation to one day!
            basically no one stays on a particular screen of an app for 24hrs (Weird if anyone does!).
            Good thing here is that using ScheduledThreadPoolExecutor promise us that our*/scheduler!!.schedule({
                beepHandler!!.cancel(true)
                scheduler!!.shutdown()
            }, 1, TimeUnit.DAYS)
        }

        fun endExecutor() {
            beepHandler!!.cancel(true)
            scheduler!!.shutdownNow()
        }

        /* Here should be our network request subclass that simply implements Runnable because that's what @ScheduledThreadPoolExecutor needs
            I really wish I can explain this @ScheduledThreadPoolExecutor further!*/
        internal class MarketClass : Runnable {
            private var x = 0 //I'm using this just show that we can cancel the operation half way or anytime we like.
            override fun run() {
                getMarket() //Here is the calling of the method that does get markets by name ....

                //========TESTING SOMETHING======
                println("Even if the world is > 2020: I won't let go of Science!!! ")
                /* here when we get to 10 we want to cancel the operation, this is imagine
                to be a user switching to another screen where same operation will be done
                but with different time interval, so let say onclick we just do the cancel
                and shutdown the scheduler, note: not necessarily done here! can be done anywhere
                since our (beepHandler) and (scheduler) are reachable.*/
                x++
                if (x == 10) {
                    println("Wanna visit another market ???\nMaybe the Jedi markets ??? ")
                    endExecutor()
                }
                //=======END TESTING SOMETHING=====
            }
        }

        private fun getMarket(){
            println("Bing! Bing!! Bing!!! Welcome to Illuminations Market... <- (0) . (0) -> we have souls to sell")
            //ApiCall().getMarketByName(param, object: Interface{
            // bla bla bla ...
            // })
        }
    }

With the code above, in relations to what you want to do, I will simply create a scheduler like above and do either cancel/shutdown or scheduler.scheduleAtFixedRate when needed.

Note: those funny codes above might be confusing, but what you need in reality are below method:

1. startExecutor(interval: Long)
2. endExecutor()
3. MarketClass : Runnable{}
4. getMarket()

Happy Coding.

Upvotes: 3

Sina
Sina

Reputation: 2883

You want periodic work in background(for server calls), so why not using WorkManager? Refresh interval for each market should be saved and be available on server. Get market data (interval included), setup workmanager with proper interval in onCreate, enqueueUniquePeriodicWork in onResume and cancelAllWorks in onPause of the page. If you don't want to use RxJava, you can simply persist data on db and observe from view or just observe retrofit callbacks to update your view. In the latter you can pass a MutableLiveData object from viewmodel and setvalue from onSuccess/onFailure and observe from view:

  1. In view: viewModel.getMutableLiveData.observe
  2. In viewModel: remoteRepository.getMarketData(mutableLiveData)
  3. In remote Repository: onResponse(){mutableLiveData.setValue(response.body)} and set something like empty object of YourResponse in failure.

Upvotes: 0

Related Questions