Android view crossfading concurency issue - java

In my project I have a lot of asynctask, which all follow this pattern:
#Override
protected void onPreExecute() {
super.onPreExecute();
crossfade(progressBar, contentView);//hide content, show progress bar
}
#Override
protected Void doInBackground(String... arg0) {
//some work
}
protected void onPostExecute(Void unused) {
crossfade(contentView, progressBar);
}
Code for crossfade:
void crossfade(View contentView, View loadingView){
Runnable r = new Runnable(){
#Override
public void run() {
if(contentView != null){
contentView.setAlpha(0f);
contentView.setVisibility(View.VISIBLE);
contentView.animate()
.alpha(1f)
.setDuration(CROSSFADE_TIME)
.setListener(null);
}
if(loadingView != null){
loadingView.animate()
.alpha(0f)
.setDuration(CROSSFADE_TIME)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
if(loadingView != null){
loadingView.setVisibility(View.GONE);
}
}
});
}
}
};
runOnUiThread(r);
}
The problem happens when asynctask executes faster than animation time, causing second crossfade call before the first one is finished, resulting in both views being invisible.
I tried queueing runnables to execute them sequentially, but the problem is if user clicks a lot of buttons or many fragments are being loaded(they use crossfade method too), UI thread becomes overloaded and it may crash my app. The only solution I see so far is to add extra delay to all my asynctasks, using Thread.sleep(CROSSFADE_TIME), however it looks like a really dirty hack and I'm not sure if it's a good user experience.

In case someone needs it in future, adding loadingView.animate().cancel() and contentView.animate().cancel() before animation cancels previous animations and everything works ok.

Why are you running the crossfade animation in a separate thread? Try to do the animation without
Runnable r = new Runnable(){ and runOnUiThread(r);

Related

Android Thread explanation?

Sorry everyone this has been asked a few times but I just do not understand any of the answers because most are about timed UI updates. So I have a backgroundTasks thread that is called when my app first starts(Does network connections so..I think that's how you do it?)
public class MainActivity extends ActionBarActivity {
String data[][];
int arrayPosition = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
runBackgroundTask();
}
Here is my thread..
public void runBackgroundTask(){
new Thread(new Runnable() {
public void run() {
data = pullSchedule(data);
updateUI(data,arrayPosition);
}
}).start();
}
All it does is call a method pullSchedule which updates a 2D array with a webcrawler. So my problem comes when I call the updateUI method which is also used by two buttons to cycle through the array data which work perfectly fine. It's just when the thread first runs if I try to update the UI I get an error.
public void upDateUI(String data[][], int arrayPosition){
TextView gameTime =(TextView)findViewById(R.id.txtDate);
//more but deleted to save space :)
}
I have researched why I cannot update the UI from the background thread but I don't understand how to fix this? I thought about putting that entire method into the runOnUiThread(new Runnable()but then I believe my data and arrayPosition have to be declared final because of an inner class..First Android app just lost. Thanks for any help.
you can use asyntask for this from which you can update ui
public myAsyn extends AsyncTask<Void,Void,Void>
{
#Override
protected String doInBackground(Void... params) {
data = pullSchedule(data);
return null;
}
#Override
protected void onPostExecute(String result) {
updateUI(data,arrayPosition);
}
}
doInBackground runs on seperate thread whereas onPostExecute runs on Ui thread therefore you can update UI from there.
You are on the right track. The reason your app is crashing is because you are attempting to update a UI element off of the UI thread. To avoid this, you can either do as RichS suggested, and use an AsyncTask which will execute onPostExecute() on the UI thread, or surround your updateUI() call in your background thread with runOnUiThread(new Runnable() {....

ProgressBar duration and hasEnded methods

I'm working with a progress bar and an animation.
progressBar = (ProgressBar)findViewById(R.id.progressBar1);
Animation animation = new RotateAnimation(30, 180);
progressBar.setAnimation(animation);
further down in my method I
set the duration for this animation
progressBar.getAnimation().setDuration(3000);
so for 3 seconds, the animation will run.
if I want to hide the ProgressBar after the animation ends I have to do the following
if(progressBar.getAnimation().getDuration() == 3000){
progressBar.setVisibility(ProgressBar.GONE);
}
What I tried doing was
if(progressBar.getAnimation.hasEnded()){
progressBar.setVisibility(ProgressBar.GONE);
}
this does not work
Could somebody explain why the hasEnded method doesn't seem to return true where I think it should.
you can call set an Animation Listener on the animation object. The Listener calls tree methods, one of which is onAnimationEnd().
animation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation anim) {
};
#Override
public void onAnimationRepeat(Animation anim) {
};
#Override
public void onAnimationEnd(Animation anim) {
// here your logic
};
});
I think problem is in below line...
if(progressBar.getAnimation.hasEnded()) {
it should be...
if(progressBar.getAnimation().hasEnded()) {
Edit::
That's because you're starting the animation and immediately checking if the animation has ended which is doubtful unless it simply doesn't do anything. What you want to do is implement an AnimationListener callback and set it to that animation.

Android - hidden text --> show text for 2 seconds --> hide text again

I am developing a board game that user plays with android. Since android is quite fast, I want to fake that android is performing some tough calculations and thus needs time for its next move.
What I want to do:-
User turn - he moves.
Android turn - android shows text "I am thinking" for 2 seconds
Android hides that text and and only after that moves his turn.
I tried doing:-
onAndroidTurn(){
textView1.setVisibility(View.VISIBLE);
Thread.sleep(2000);
textView2.setVisibility(View.GONE);
}
But what happens is that thread sleeps but text is not shown (okay I know why).
Then searching on stackoverflow, I learnt a way:-
onAndroidTurn(){
textView1.setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable(){
void run() {
textView1.setVisibility(View.GONE);
}
}, 2000);
}
Now what this does is that it runs that text in another thread and android's turn is updated on screen and after moving it's turn android showing "Thinking" is total stupidity.
What can I do for this?
Try the following:
Show "I'm thinking"
Calculate your move but don't actually do the move, just store it for a while
Schedule timer
When timer runs out remove the text and do the move
Something like this:
onAndroidTurn(){
textView1.setVisibility(View.VISIBLE);
saveMove(calculateNextMove());
new Handler().postDelayed(new Runnable(){
void run() {
textView1.setVisibility(View.GONE);
doNextMove(restoreMove());
}
}, 2000);
}
Maybe you could just use an AsyncTask (like this pseudo-code):
private class ShowTextTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute(Void... result) {
textView.setText("Initital");
}
protected Long doInBackground(Void... urls) {
Thread.sleep(2000);
}
protected void onPostExecute(Void... result) {
textView.setText("After 2 seconds");;
}
}
Okay i did it like this:-
public void androidThinking(){
textView1.setVisibility(View.VISIBLE);
androidThinking = true; //explained below
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
droidThink.setVisibility(View.GONE);
//CODE for android's turn
androidThinking = false; //explained below
}
}, 2000);
androidThinking when set to true prevents the user from moving his turn by forcing the listener for user's button to return prematurely
btn.setOnClickListener(new View.onClickListener(){
#Override
public void onClick(View v) {
if(androidThinking)
return;
Thanx everyone for your reply.

Progress Dialog blocking

Progress Dialog not showing up was solved by removing the blocking call.
My purpose is to download large amount of data from internet and keep the end user informed about download status, however I have to wait to the data to complete downloading for proceeding to the next step, and because of that I have to block the code from executing.
Blocking causes the progress dialog not to show up or freeze. I need tip for implementing those tasks the best way. Because my tasks are simple it seems for me to be overkill to implement task complete listener and I was wondering if there was any other way to solve my problem?
You can use this:
#Override
protected void onPreExecute() {
dialog= ProgressDialog.show(YourActivity.this, "", "MessageYouWantToDisplay");
}
#Override
protected void onPostExecute(T result) {
dialog.dismiss();
}
You call the class that extends AsycTask by typing:
new NameOFClass().execute();
Try this first as a test and put a Thread.sleep() in you doInBackground method to understand how it works. And after that use it in your project with your true methods, data etc.
Try this:::
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(YourActivity.this);
dialog.setMessage("Progress start");
dialog.show();
Log.d(TAG, "Showing dialog");
}
#Override
protected void onPostExecute(T result) {
dialog.dismiss();
Log.d(TAG, "Dismissing dialog");
}

Android thread confusion

I have written a function to create a splash screen with a 5 second timeout for my app.
The code works fine, but when the timeout reaches zero and I want to redirect to my main activity, the app crashes with the following error:
Only the original thread that created a view hierarchy can touch its views.
So I looked around a bit and someone suggested nesting this inside my function. It seems like a good Idea, but now methods like sleep / stop won't work.
My code is below, I can provide more / explain more in details if it isn't clear enough just let me know. Thanks for the help.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showSplashScreen();
}
protected boolean _active = true;
protected int _splashTime = 5000; // Splash screen is 5 seconds
public void showSplashScreen() {
setContentView(R.layout.splash_layout);
// Thread splashThread = new Thread() {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
int waited = 0;
while (_active && (waited < _splashTime)) {
Thread.sleep(100);
if (_active) {
waited += 100;
}
}
} catch (InterruptedException e) {
// do nothing
} finally {
showApplication();
}
}
});
}
Probably not what you want to hear, but you should never put a splash screen on your mobile app. With the exception of games, when people use a mobile app they want to get in, do what ever it is they need to do, and get out. If you make that process take longer, people are just going to get frustrated with you app. You should probably reconsider just not using a splash screen.
This will perform sleep on the UI thread. That's never a good idea.
Why not something like this?
new Handler().postDelayed(new Runnable() {
public void run() {
// start application ...
}
}, _splashTime);
But this answer has a good point. Displaying a splash screen for 5 seconds can be very annoying.
I believe you want AsyncTask for this. The method called on completion of the task will be called on your UI thread, making modifying UI elements much easier.
Use a Handler to post an event to the UI thread that will remove the splash.
Code should be something like...
splash.show()
new Handler().postDelayed(
new Runnable() {
void run() {
splash.remove();
},
delayTime);
I suggest you to make new activity for your spalsh screen, show it in a regular way (with startActivityForResult) and place in it such code (in it, not in your main activity):
new Handler().postDelayed( new Runnable()
{
public void run()
{ finish(); }
}, 5000 );
Also you can handle in this new activity click events for giving opportunity to user to close it faster, tapping on it.

Categories