Android - Cant access TextView within a Timer in a Service
Im facing the following problem:
public class StatCounterService extends Service {
private TimerTask updateTask_ref = new TimerTask() {
//Get the element by Id as common practice in Android
TextView hungerValue_ref = (TextView)layout.findViewById(R.id.hungerValue);
@Override
public void run() {
//Every X Seconds, the Tamagotchi loses Hunger Points
int hunger = (int) (getHunger()-1);
//Updating Model (DataBase)
updateHunger(hunger);
//Now I want to update the TextView, but this hungerValue is Null
hungerValue_ref.setText(new Long(getHunger()).toString());
}
};
}
I think the problem is, Im looking for the ID of the View inside a Service, also inside a nested Class (the timertask). The Service is running, i just get a nullpointer on the "hungerValue". Any Ide开发者_运维问答as how I could solve this? thanks a lot! Daniel
EDIT: I now try to update the view in the Timer from the activity, here is the idea:
public class MainWindowActivity extends Activity {
Timer timer_ref;
TextView hungerValue_ref;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Change the View I want to change initally (getting Value from Database)
updateHungerView();
//Start Timer
timer_ref = new Timer("StatCounterTimer");
timer_ref.schedule(updateTask_ref, 1000L, 10000L);
}
//The Timer from within I want to change the View every X seconds
private TimerTask updateTask_ref = new TimerTask() {
@Override
public void run() {
//Line Throws Exception, see Method below
updateHungerView();
}
};
/**
* Important Part
*/
public void updateHungerView() {
hungerValue_ref = (TextView) findViewById(R.id.hungerValue);
hungerValue_ref.setText(new Long(getHunger()).toString());
}
when I try to change the view from my activity, I get this:
09-30 11:54:48.967: ERROR/AndroidRuntime(30476): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
You're trying to work with a component part of a layout from inside a service but you can only work with the layout from inside the associated Activity. The service has no knowledge of if the activity is on top when it executes its code - the Activity instance might not even exist yet.
The best approach for allowing a service to cause the display to update is to broadcast an Intent from the service:
Intent intent = new Intent();
intent.setAction("my.intent.action.name");
sendBroadcast(intent);
Then, in your Activity, you create a BroadcastReceiver instance to process the intent.
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("my.intent.action.name")) {
// do something with the UI here
}
}
};
You'll also need to register your broadcast receiver in your onResume() method:
IntentFilter filter = new IntentFilter("my.intent.action.name");
registerReceiver(receiver, filter);
And unregister it in your onPause() method:
unregisterReceiver(receiver);
You can't use a static inner class for your BroadcastReceiver instance because it won't be able to do things like:
MyActivity.this.getContext()
or
MyActivity.this.findViewById(R.id.my_ui_component)
Hope that helps!
To me it appears that you're trying to find and update a TextView which might not be visible/inflated (e.g. it's holding Activity has been destroyed/stopped/paused).
I'm guessing you want to use a service, because it can keep running even if your App gets pushed to the background or gets killed. I think however in your situation a change in the your design might be preferable.
Instead of having a Service running - save a timestamp as soon as your app gets paused (in the onPause()
-method of the visible activity). Then when the user resumes the App (the onResume
-method will be called) you calculate the time between the previously saved timestamp and current time and decrease the hpvalue based on this. Then update the TextView from the holding activity.
You could also consider using an AsyncTask - if the hpvalue only needs updating when the App is active.
Consider using an AsyncTask rather than Service. Since you cannot update UI in Background http://developer.android.com/reference/android/os/AsyncTask.html
I think this is a common problem with android. The findViewById is relative to a layout and must be called after setContentView if I remember well. This could help you :
http://www.anddev.org/solved_why_does_findviewbyid_return_null-t557.html
精彩评论