I work with Android 2.3.3.
I’ve a problem to refresh my activity’s view after a rotation of the screen.
My activity has a variable(counter), a TextView(displays counter value), a button(increase counter and show it) and TimerTask(increase counter and show it too).
It works correctly. My TextView shows a new value after each event from Button or TimerTask.
It works until I rotate my phone. The TimerTask’s event refreshes no longer my TextView. The button and rotation screen can still modify my view. My TimerTask still increase my variable but there aren’t no change in the screen. I checked, TimerTask still run and execute.
It isn’t my true project, it’s just the part where there’s my bug.
My only activity :
package com.test;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class TestRefreshActivity extends Activity {
static TimerTask mTimerTask=null;
static Timer t=new Timer();
final Handler handler = new Handler();
static int counter=0;
//-------------------------------------------------
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//create and run the timer
if(mTimerTask==null){
Log.d("Test","new TimerTask");
mTimerTask = new TimerTask() {
public void run() {
handler.post(new Runnable(){
public void run(){
counter++;
refreshCounter();
Log.d("TIMER", "TimerTask run");
}
});
}
};
t.schedule(mTimerTask, 500, 3000);
}
Log.d("Test","--onCreate--");
refreshCounter();
}
//-------------------------------------------------
@Override
public void onBackPressed() {
super.onBackPressed();
mTimerTask.cancel();
TestRefreshActivity.this.finish();
}
//-------------------------------------------------
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("Test","--onDestroy--");
}
//-------------------------------------------------
public void onBtnClick(View view){
counter++;
refreshCounter();
}
//-------------------------------------------------
//the only function which refreshes the TextView
private void refreshCounter(){
Runnable run = new Runnable(){
public void run(){
TextView textView = (TextView)findViewById(R.id.textView1);
textView.setText("counter="+counter);
textView.invalidate();
}
};
synchronized(run){
TestRefreshActivity.this.runOnUiThread(run);
}
}
}
And my only view :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onBtnClick"
android:text="Button" />
</LinearLayout>
I don’t understand why my TimerTask can’t change the view after the rotation. What I know, the rotation destroys my activity for recreate it, but only static variables survive.
Thanks for your help.
Regards,
android:configChanges="keyboardHidden|orientation"– is a poor solution which is discouraged by guidelines. It will only save you on screen rotation, but your app will still be unusable after your activity will get killed by android when it’s on background.Look at what you do. You provide a reference to a handler to a static timer. When you rotate your phone, activity gets recreated (means one is destroyed, and another one is created). Your activity gets destroyed, but static members still live and they still hold a reference to a handler for activity which is dead.
A new activity gets created then, with a new handler, but your timertask knows nothing about this.
So you have a static timer that updates invalid handler, and activity that does nothing.
I would drop the statics and would save current timer value in bundle in onSaveInstanceState.
Or (even an easier for you) create a setter for handler in your TimerTask, and when you do this check
if(mTimerTask==null){and see, that timer is already instantiated, set a new handler in it (mTimerTask.setHandler(yourActivityHandler)) so when new activity is created your timertask will post in a new handler.