I am developing an Android application where I need to check a web service for updates everytime the user wishes to. This involves quite a bit in the background, and I used SwipeRefreshLayout to indicate the updating act.
I assumed the refreshing animation happened async, and independent of any work im doing to fetch the updates, but when I perform a pull, there are visibles stutters in the animation thoughout the process, ruining the whole process visually. Any ideas on why this is happening and thoughts on how to fix it? I havent included code in case that the info i have provided is enough, if not, i will supply the necessary code sample.
The code looks something like the following:
onRefresh()
{
startService(syncServiceIntent);
}
The service then checks the service and updates a local database with any possible updates using an API call that returns an Observable of my data. After finishing, I broadcast it and upon receiving this broadcast, I accordingly repopulate my RecyclerView using another Observable of my data, and finally on
onCompleted()
{
//do some stuff
mSwipeRefreshLayout.setRefreshing(false);
//do some other stuff
}
Why is the animation stuttering?
Related
My app consists of
1) An Activity
2) A service (forground)
3) A contentProvider... Writes to it inside the service, reads from it (cursor and observer) in the activity
The service maintains a Bluetooth connection to a device that is reporting data about once every second. The service then writes a tag/value pair to the content provider database.
The activity consists of some textviews, some buttons and some imageviews. It is also using the SpeedView dials package. However if I dont use the speedview the problem still happens.
The activity subscribes to the contentprovider for cursor loadercallbacks to init the screen and then uses a contentObserver to listen for changes.
Inside the contentObserver onChange(self,uri) {} .. it will see a change, request the value of the URI and then proceed to try to update the corresponding View.
OK so here is the problem. Because the callback is as if I think I understand "not" in the main looper, it cannot directly call some of the UI apis directly... For example trying to change the text of a TextView.
In the past this was not a biggie what I simply would do is create a handler and simply do something along these lines...
TextView mytextview...
Handler mHandler = null;
onCreate() { ... mytextview = findviewbyid(...)...
mHandler = new Handler();// No looper means main thread I think
}
onChange(self,uri) {
....
mHandler.post(new Runnable() {
#Override
run() {
mytextview.setText("the new text from the content provider");
}
});
.....
}
So what is the problem? Well at first nothing.. This works.. Well at least for several minutes. Not totally predictable sometimes quick sometimes after about 15 minutes .. But eventually the handler.post() wont work anymore. I placed a Log before the handler.post and a log inside the run() at the start and finish... I always see the log finish and the GUI update but then for some reason I wont see any logs except the one prior to the handler.post (meaning the content provider callback is still working!)
I am a bit at wits end as to why this will run for several minutes and simply stop. Its like some sort of internal looper queue is filling up.
My other attempts to use handeler.post(runnable)) to change a text view or other views didn't have these issues (at least maybe I didn't see them if the activity wasn't showing that long or for some other reason??!!
Well this isnt a true answer to the solution.. However what I discovered is that my updates from the Observer calling the handler.post was about at a rate of 50ms.
thats a bit fast though it still doesnt explain why no crashing or errors.
My workaround that seems to be working is instead of using an Observer update to call the handler.post that updates the UI is to do a crude POLLING of the observer class and poll at 100ms. Using a handler.postDelayed() and inside there doing the updates to the UI by checking the observer class for changes (polling) of each variable to see if any of them changed and if so then update the UI.
I have an activity that gets started by a background service in order to show scrolling message(s) that the service receives from a server. It's possible to have just one scrolling message or a set of multiple to scroll, one at a time.
My question is what is the ideal way to manage cycling the scrolling of multiple messages, and multiple sets of multiple messages? I've tried my cycler loop within worker threads, AsyncTask, etc.
The Details:
My scroll activity receives the message(s) to show, as an array of strings available for it to access from the service's scope. Say, for example, the service starts the activity in order to scroll several messages, one after another:
My service populates MyService.messages[] with the messages to show and then starts MyScrollActivity.
MyScrollActivity gets size of MyService.messages[]. If >1, start MyCyclerThread (or AsyncTask, etc. - haven't figured out best way!)
MyCyclerThread loops through MyService.messages[] and sets MyScrollActivity TextView with messages[i] and animates across screen once, one message-iteration at a time (one message scrolls across, then the next, etc. - at end of loop, it starts over at i=0 and repeats the messages again.
Step 3 repeats until activity is taken down by the service or another activity is shown. The cycler-thread (or whatever - the loop) needs to die at this time.
I want that particular worker thread (that's doing the looping) to die and never come back whenever its activity stops. But for some reason, whenever the activity is restarted (say a new batch of messages comes in to display), the old cycler/worker thread resurrects, along with a new one for the new messages.
I've tried adding Thread.currentThread().interrupt() to the activity's onPause and onDestroy methods, and I see in my logging that the thread does get interrupted. But like I said, it resurrects (even with the same ID it had before) and starts its loop again. This means for however many 'X' times I start my activity, I also get 'X-1' number of threads running.
I toyed with Activity single-instance XML, various "isThreadABCrunning" flags, etc. I've even tried using an AsyncTask for my cycler loop. I'm running out of ideas.
Help, I'm beginner/intermediate in Android... I know just enough to make trouble, apparently. Is there something I'm missing about threading or activities? Is it perhaps something as simple as handling my activity differently?
I'd appreciate a 30,000 foot overview, and then I can provide code samples, as needed (I've gone through too many iterations to pin down one to post here for now), so sorry if I'm treading close to violating a post's best-practice.
Update: animation code
slide = new TranslateAnimation(dm.widthPixels, -params.width, 0, 0);
slide.setDuration(animDuration);
slide.setRepeatCount(repititions);
slide.setRepeatMode(Animation.RESTART);
slide.setInterpolator(new LinearInterpolator());
slide.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
msgIsScrolling = true;
}
#Override
public void onAnimationEnd(Animation animation) {
msgIsScrolling = false;
}
#Override
public void onAnimationRepeat(Animation animation) {
//nothing to do
}
});
textView_message.startAnimation(slide);
My gut reaction is that "multithreaded" solutions are the wrong way to approach this problem. My first thought is:
Change your design so that the activity just asks the service for the "next" message to show (the activity shouldn't know about how many messages or what message index it's on, it should just show whatever message the service gives it).
Use an Animator.AnimatorListener's onAnimationEnd() method to re-trigger your animation.
Essentially, you'd have some method inside the activity that takes a message and starts it animating. You'd call this method both when your activity is first shown and whenever a message is done scrolling.
Since this all lives inside the activity itself, there shouldn't need to be any cleanup when the activity dies.
It's a simple scenario:
You start some background, let's say, network operation in a separate thread.
Set your ProgressBar visible.
Go away from your app before network operation completed.
While your app is in background the network operation completes but you never receive a callback (or fired event) because you should unsubscribe your callbacks/event subscriptions to prevent undesired exceptions (you can only modify UI views from a main thread).
You resume the app and see ProgressBar on the screen despite your background operation has already been finished.
I'm curious what is the best way to handle this scenario.
A possible option could be also using an event bus (for example https://github.com/greenrobot/EventBus) and holding a sticky event when the network operation is completed and checking this in onResume of your activity.
My answer is a Service or an IntentService. While your app is in background I assume you hide the progress bar. When the app is in foreground You can bind to the Service's instance, having maybe a method that returns the current progress. If the progress is grater or equal to the max you show the progress again, otherwise you undertake another action
The general pattern for those this type of scenario (can also include data processing, view updates, etc, etc) is:
/*
`controller` is the object with reference to the task
currently being executed.
It can be anything:
a network operation,
a file copy,
an image processing,
an asset loading, etc, etc...
*/
public void onStart() {
super.onStart();
myView.setSomeProperty(controller.getCurrentValue());
controller.subscribe(this);
}
public void onStop() {
controller.unsubscribe(this);
super.onStop();
}
#Override
public void onControllerSubscriptionUpdate(int newValue){
myView.setSomeProperty(newValue);
}
that way, every time the Activity or Fragment is coming to the foreground, your view gets updated with the latest parameter.
In my app, the activity starts a thread which download images from server. There are more than 30 images. While downloading, the user can switch to another activity. So I want that the thread should stop doing whatever it is doing. I have used mThread.interrupt() method in onDestroy(), but it did not worked. How to do that?
I can suggest simpler&safer approach to handle this
Use common value across your app;
SharedPreferences or inside Application context
Key=IsStopDownload
Value= True / False
part.1)
in Application context (MyApplication)
private boolean isAppActive = false;
public void setAppState(Context context, boolean state) {
isAppActive = state;
// note: you can also store this in SharedPreferences
}
public boolean getAppState(Context context) {
return isAppActive;
// note: you can also load this from SharedPreferences
}
part.2)
Activity onResume and onPause are the guaranteed places to identify state of your activity.
onResume -> activity is active
onPause -> activity is not active
part.3)
let your thread check the activity state and if not active, it can stop itself, thats safer then making external interrupt. threads can act weird when interrupted from outside, it is safer to break the loop from inside.
e.g.
((MyApplication)context.getApplicationContext()).getAppState(context);
if thats false, thread stops itself
hope this helps...
---
Social Coding #AspiroTV
When user will switch the activity , this one will not destroyed but paused so trying your code is onPause() might work .
First of all, you're probably much better using an AsyncTask than a Thread, so, personally, I wouldn't use a Thread at all. In my humble opinion and small experience, in that situation where you're using them, they grow and grow until you have spaguetti code.
And second, as Dr. Nik said, this task is typically better served using a Service. It's is, in my opinion, the best and safer thing you can do.
I would point out several reasons why you should use one:
The service does not need to stop because the activity goes away.
Services are very easy and quick to implement. And the notification code for completion is also easy.
You are downloading images, and it's always better to do the job at once if you can, to save bandwidth and connection time for the user and cache images locally. This used to be worse because today cell phones are full fledged computers. But it's always important to code for efficiency. Therefore, IMHO, you should keep the need for a connection as small/quick as possible.
Finally, even using AsyncTasks inside an activity demands a tricky (simple, but still tricky) code to catch and detach the task when the Activity is going away, and a check for nulls when it's coming back. Since you're downloading images, and that can take a time and it's very possible that the user may demand an orientation change (turn the device to landscape), you will need that. Search stackoverflow for "orientation change asynctask" for examples.
There are probably other reasons, too, but those are on the top of my head right now. And of course, it's my opinion.
I read a lot about handling rotation in android applications, but I still have so many questions and need to much to understand.
Let me explain my problem or implementation, that I'm using now in my application.
If an activity will be opened, a get request will be sent to server. This request will be executed in a Thread (new Thread(...)) and if request was completed, activity's ui will be refreshed.
But what should I do, if the user rotate his device?
By default, the activity will be destroyed and request will be started again and start a new thread, but the thread of destroyed activity may be still running.
I guess, it's a quite wrong way, I have now.
But what is the best approach, to handle this?
Probably is the best way to forbid rotation, but what If I don't want that?!
May be it's the second part of my question:
I saw a video form Google IO. Mr. Dobjanschi suggested to use services and to store retrieved data in content provider. So, probably I can use a service for executing my requests. But should data be replaced every time the get request was completed?!
Well dont know exactly how its done, You can try saving the instance and retrieving the same when config changes with following methods:
I have read about them but haven't really implemented them yet. I hope it can give you some start.
#Override
public Object onRetainNonConfigurationInstance() {
return(myServerThread);
}
private void restoreServerFunctions() {
if (getLastNonConfigurationInstance()!=null) {
myServerThread=(Thread)getLastNonConfigurationInstance();
}
}
You can specify that the activity handles the rotation itself. This is done through adding:
android:configChanges="keyboardHidden|orientation"
in the tag of the activity inside your android manifest. You don't have to actually handle the rotation but this will tell android to not destroy your activity. The base activity class will handle all the rotating of the user interface for you and your thread will be executed correct.
A small side note: if you are doing only a small server task use AsyncTask to execute the call to the server in the background instead of creating a thread. This will minimze some of the programming effort you need to communicate the results from the thread to the activity and update your UI.
One easy way, though I've never tried it. Instead of refreshing the current UI, when the thread finishes, start a new Activity with the just downloaded content. So first, you start an Activity with a blank page (or just the page's frame), then you rotate the blank page as much as you like, then the downloading Thread spawns a new Activity, replacing the blank page Activity with the loaded content page using the current orientation.