Android - 无法在服务中的计时器内访问TextView

时间:2011-09-30 09:18:23

标签: java android user-interface

我面临以下问题:

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());
        }
      };
}

我认为问题是,我在服务中查找View的ID,也在嵌套的Class(timertask)中查找。服务正在运行,我只是在“hungerValue”上得到一个nullpointer。任何想法我怎么能解决这个问题? 非常感谢! 丹尼尔

编辑:我现在尝试从活动中更新Timer中的视图,这是想法:

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());

}

当我尝试从我的活动中更改视图时,我明白了:

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.

4 个答案:

答案 0 :(得分:2)

您正尝试从服务内部处理布局的组件部分,但您只能使用相关活动内部的布局。该服务在执行其代码时不知道活动是否在顶部 - 活动实例可能甚至还不存在。

允许服务使显示更新的最佳方法是从服务广播一个Intent:

Intent intent = new Intent();
intent.setAction("my.intent.action.name");
sendBroadcast(intent);

然后,在Activity中,创建一个BroadcastReceiver实例来处理意图。

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
        }
    }
};

您还需要在onResume()方法中注册广播接收器:

IntentFilter filter = new IntentFilter("my.intent.action.name");  
registerReceiver(receiver, filter);

在onPause()方法中取消注册:

unregisterReceiver(receiver);

您不能为BroadcastReceiver实例使用静态内部类,因为它无法执行以下操作:

MyActivity.this.getContext()

MyActivity.this.findViewById(R.id.my_ui_component)

希望有所帮助!

答案 1 :(得分:1)

考虑使用AsyncTask而不是Service。由于您无法在后台更新UI http://developer.android.com/reference/android/os/AsyncTask.html

答案 2 :(得分:1)

我认为这是android的常见问题。 findViewById是相对于布局的,如果我记得很清楚,必须在setContentView之后调用。这可以帮到你:

http://www.anddev.org/solved_why_does_findviewbyid_return_null-t557.html

答案 3 :(得分:1)

对我而言,您似乎正在尝试查找并更新可能无法显示/膨胀的TextView(例如,它的持有活动已被销毁/停止/暂停)。

我猜你想要使用某项服务,因为它可以继续运行,即使你的应用程序被推到后台或被杀死。我认为,在您的情况下,您的设计可能会更改。

而不是让服务正在运行 - 只要您的应用暂停(在可见活动的onPause() - 方法中),就会保存时间戳。然后,当用户恢复应用程序(将调用onResume - 方法)时,您将计算先前保存的时间戳与当前时间之间的时间,并根据此值减少hp值。然后从保留活动更新TextView。

您还可以考虑使用AsyncTask - 如果hpvalue仅在应用程序处于活动状态时需要更新。