简体   繁体   English

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

[英]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). 我认为问题是,我正在服务内以及嵌套类(timertask)内寻找View的ID。 The Service is running, i just get a nullpointer on the "hungerValue". 该服务正在运行,我只是在“ hungerValue”上获得了一个空指针。 Any Ideas 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. 您试图从服务内部使用布局的组件,但是只能从关联的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 intent = new Intent();
intent.setAction("my.intent.action.name");
sendBroadcast(intent);

Then, in your Activity, you create a BroadcastReceiver instance to process the intent. 然后,在“活动”中,创建一个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
        }
    }
};

You'll also need to register your broadcast receiver in your onResume() method: 您还需要在onResume()方法中注册广播接收器:

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

And unregister it in your onPause() method: 并在您的onPause()方法中注销它:

unregisterReceiver(receiver);

You can't use a static inner class for your BroadcastReceiver instance because it won't be able to do things like: 您不能为BroadcastReceiver实例使用静态内部类,因为它无法执行以下操作:

MyActivity.this.getContext()

or 要么

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

Hope that helps! 希望有帮助!

Consider using an AsyncTask rather than Service. 考虑使用AsyncTask而不是Service。 Since you cannot update UI in Background http://developer.android.com/reference/android/os/AsyncTask.html 由于您无法在后台http://developer.android.com/reference/android/os/AsyncTask.html中更新UI

I think this is a common problem with android. 我认为这是android的常见问题。 The findViewById is relative to a layout and must be called after setContentView if I remember well. findViewById是相对于布局的,如果我记得很好,则必须在setContentView之后调用。 This could help you : 这可以帮助您:

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

To me it appears that you're trying to find and update a TextView which might not be visible/inflated (eg it's holding Activity has been destroyed/stopped/paused). 对我来说,您似乎正在尝试查找和更新可能不可见/夸大的TextView(例如,它持有的Activity已被破坏/停止/暂停)。

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). 而不是运行服务-请在应用暂停后立即保存时间戳(在可见活动的onPause()方法中)。 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. 然后,当用户恢复应用程序时(将调用onResume方法),您将计算出先前保存的时间戳和当前时间之间的时间,并据此减少hpvalue。 Then update the TextView from the holding activity. 然后从保持活动中更新TextView。

You could also consider using an AsyncTask - if the hpvalue only needs updating when the App is active. 您还可以考虑使用AsyncTask-如果仅在应用程序处于活动状态时才需要更新hpvalue。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM