Android Work Manager: Can I force prune the Completed Jobs? - android

I am uploading photos in my app and want to provide the User with the upload progress. The way I was going to do this is have a global tag, UPLOAD_MEDIA_TAG and use that whenever I am creating a OneTimeWorkRequest. That way I can just observe the WorkManager.getInstance().getStatusesByTag(UPLOAD_MEDIA_TAG) LiveData, and whenever a job completes I would show a percent completed. (I.E. Uploading 3/10 photos would show 30%). Then once all 10/10 photos have been uploaded, I would hide the progress bar. This works great as it easily supports leaving the app, coming back, and you will see the Progress Bar as soon as the app launches right where they left off.
The problem is that once all 10/10 jobs are finished and marked as completed, those jobs will stick around until the WorkManger prunes them. So if the user uploads 5 more photos, the LiveData is returning 15 statuses instead of 5, throwing off the percent calculation.
I noticed there is a method OneTimeWorkRequest.keepResultsForAtLeast(duration) but what I would want is something like, keepResultsForAtMost(duration) or a WorkManager.getInstance().forcePrune() method that would cleanup all the completed jobs from the database.
Any help would be great!

After submitting an issue with Google, they are saying there will be a function called, pruneWork in Alpha03.

As of alpha release, you don't have much control over job pruning since it is managed by WorkManager. You can however observe the lifecycle of WorkManager to get job status and take necessary action as follows:
WorkManager.getInstance().getStatusById(myWork.getId())
.observe(lifecycleOwner, workStatus -> {
// Do something with the status
if (workStatus != null && workStatus.getState().isFinished()){
// Stop observing data or do other action
}
});
Note:
Use [WorkManager 1.0.0-alpha03 which has several bugs fixed and new features introduced. One you might be interested is:
Added WorkManager.pruneWork() to remove completed jobs from the internal database

Related

how to save the reached level in a quiz app

I'm new to kotlin and android app developpement in general and I want to make a Quiz app with jetpack compose not XML, and this app has multiple levels and when the user starts the quiz if he decides to stop playing in a certain level and quit the app i want to save that level so next time when he come back to play he's gonna start from the same level he stopped.
I've tried to use preference datastore but it doesn't seem to work.
this is my Datastore class:
Datastore
this is how I store the current level:
save current lvl
this is how I get the lvl I stored in the datastore:
get stored lvl
if you have any idea what I'm doing wrong please and if posibile use jetpack compose.
thank you.
You should post code, not images. Although in this case, you can see from your second image that saveScreen is a function you're never calling anywhere, because it's grey (i.e. it's never used anywhere), and there's a yellow underline giving you a warning too.
You'll have to call saveScreen() or saveScreen.invoke() somewhere. Why not just make it a normal function though?
// call this when you want to save the screen
private fun saveScreen() {
// scope.launch { datastore.Save etc }
}
Unless doing things this way is a Compose thing (I've never used it). Either way, that function you're defining isn't called anywhere, so your screen isn't being stored!

android paging 3 line after adapter.submitData doesn't get executed

I am currently learning Paging 3 library. I have an app that has shimmer placeholder for showing while loading the data. When i get the data i want to hide shimmer layout and show recycler view.
lifecycleScope.launch {
quoteViewModel.getQuotes2().collectLatest {
binding.shimmerLayout.visibility = View.GONE // this line gets executed when view created and it causes shimmer layout to hide
binding.homeRecyclerView.visibility = View.VISIBLE
homeAdapter.submitData(it)
Log.d("mytag", "after submit data") // this line isn't executed
}
}
Problem here is the line after the submitData doesn't get executed.I searched and saw the reason was because submitData never returns so it doesn't get called. But i didn't find any example code. Can someone please show me an example code for solving this?
The logic i want to execute after submitData is to hide shimmerLayout and show recyclerView, but because that logic is before submit data, they are executed immediately, therefore shimmerLayout doesn't seem when loading data.
As you mentioned, it is true that the line after submitData() won't be executed since it doesn't return. What is it exactly that you are wanting to achieve using Paging 3 with it here?
I had a similar doubt where my recyclerview was not updating data fetched from Paging 3 because I was using collect operator. Dustin Lam, the author of Paging 3 library answered me here to replace the collect with collectLatest and that had fixed the issue for me.
In your case, you are already using collectLatest. One quick fix can be to put the submitData at the very end of collectLatest since that is what causes the issue.
As mention in the collectLatest definition,
Terminal flow operator that collects the given flow with a provided action. The crucial difference from collect is that when the original flow emits a new value then the action block for the previous value is cancelled.
Note that replacing collect with collectLatest fixed the issue for my case only because initially my StateFlow was emitting an Initial state, but when the data was ready, it emitted a Success state that wasn't collected by the collect operator since the submitData never returns, but collectLatest forces the previous action block to be cancelled and new value to be collected.
I'm unsure of a solution to your problem yet but either edit your question with the exact use case or in case you do not find any alternative to it, you can directly ask Dustin Lam about it like I did :)
I tried with: submitData(lifecycle, it) and it started to stop onto the lines after the Submit.
Here says: Caution: The submitData() method suspends and does not return until either the PagingSource is invalidated or the adapter's refresh method is called. This means that code after the submitData() call might execute much later than you intend.

Missions timeline not working after loading while takeoff

I am having an issue where i execute the startTakeOff() method, and the callback of this method is returned before (0.5 meters) the drone got to the height he suppose to (1.2 meters) - this part is documented (startTakeoff()) - which is a bit weird. After the callback is returned i am loading the actions to the timeline (getting a success callback), and then firing the startTimeline() method, i've also registered a listener that prints all timeline's event status, and what i see is that the timeline started, then the GoToAction (first in the timeline) is started but the drone doing nothing, doesn't move or do anything.
If i clean the timeline, reload the actions, and fire startTimeline() again (w/o calling to stopTimeline()) it works as it suppose to.
My question is - is there a 'clean' way to delay takeOff's callback? Or make the timeline run w/o reloading it? By 'clean' i mean that i can set a custom timeout that will load and start the timeline, but it is not the best practice...
No the OSDK fires the callback as it leaves the ground, your only option is to observe and wait for it to reach 1.2m.
OSDK only provide you with some sample. It is not 100% sure of working. You should think and get a solution to your problem.
In your case, the delay they put is without any guarantee. you need accurate feedback to make it sable
You can use a system such as SVO to achieve this. https://github.com/uzh-rpg/rpg_svo
SVO gives you feedback of ur current drone status. So instead of using delay
start take off
while uav not at 1.2 meter -> (take off running) do nothing
while uav airbone -> do the thing you want
Additionally you can put timeout in, in case overloads and drone cant take off successfully.
I saw people getting SVO / PTAM / Vinsmono working on android phone before. So i think you could do the same.

Updated RealmResults with time-based query

I'm starting to approach this wonderful world of Realm. I'm very happy of the results I'm getting and now I have one question to submit.
In my android app I've got a Fragment that displays data retrieved from Realm. The query condition is that the time this data refers to is in between the beginning and the end of today.
RealmResults<Appointment> results = realm
.where(MyObject.class)
.between("begin", rangeBegin, rangeEnd)
.between("end", rangeBegin, rangeEnd)
.findAllSorted("begin", Sort.ASCENDING);
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well.
I've also added listeners for changes in order to optimize UI updates.
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
From what I've seen it seems to do the same query done the very first time onStart() was executed.
Is there a way to have also live-updating query or should I re-run that query somewhere else outside onStart()?
Thank you in advance
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
The query is pretty constant after you've set it up, so you'd need to execute a new query with different parameters for rangeStart and rangeEnd, and replace your other results.
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well. I've also added listeners for changes in order to optimize UI updates.
Personally I'd advise to put the query in onCreateView() instead, and the Realm lifecycle management to onCreateView() and onDestroyView().
Also, you can avoid manually assigning RealmChangeListeners for displaying lists if you use RealmRecyclerViewAdapter (adapters 1.3.0 works with realm 1.2.0).
If you use RealmRecyclerViewAdapter, then just call adapter.updateData(newResults); and it'll update the view as needed.

Best practice to use AsyncTask in autocomplete?

I am using Async task to populate auto-complete suggestions from server.
Problem:
when user types and removes the text in edittext so many times.
lets say he typed: cofee > cof > coffee >coffee late .... etc for so many times.
for each text changed after 3 keyword(threshold) i am initializing an asynctask and ask for result.
so in current scenario i have so many threads running in background. so some of my latest async threads are waiting for there chance.
Whole this make my app very slow.
What can I do to tackle this problem?
If it is possible to load entire data from server at beginning...then you can avoid calling asynctask repeatedly and fetching the data from server. This will improve performance of you app. If data displayed in Listview is String, following link show how to filter it:
http://www.androidhive.info/2012/09/android-adding-search-functionality-to-listview/
And if custom object is used in ListView adapter, try:
Filtering ListView with custom (object) adapter
Hopefully this helps.
You should cancel the current task before issuing a new one. Use AsyncTask#cancel(true) for that and make sure that the execution of the task can be quickly stopped. This means correct handling of interruption and frequent checking whether the task was cancelled in the body of AsyncTask#doInBackground.
And you cannot execute again the AsyncTask you have cancelled. You have to create a new one. (Trying to execute it again leads to IllegalStateExceptions)
It worked for me by cancelling the task each time you change the text (if it is still running).
You need to define your request once outside the listener(private for the class), and then start your listener function by (if your request is not finished, then cancel it).
define your request out side the function
private YourSearchTaskClass YourTaskReq = new YourSearchTaskClass();
then start your addTextChangeListener/afterTextChanged by this
if (YourTaskReq.getStatus()!= AsyncTask.Status.FINISHED)
YourTaskAvReq.cancel(false);
YourTaskReq= new YourSearchTaskClass(keyword);

Categories

Resources