应用程序在第二次计时器运行时崩溃

时间:2012-05-09 21:37:06

标签: android timer crash apk

我正在使用Android应用,我想每30秒更新一次背景信息。

我使用了计时器,它可以工作,但只有一次!应用程序第二次崩溃。

public Timer mTimer = null;    

public void loadColor()
    {
        setContentView(R.layout.color);

        cur_scr = (LinearLayout) findViewById(R.id.colorScreen);
    }

    public void onClick(View v) throws InterruptedException
    {
        int id = v.getId();

        switch (id)
        {
                case R.id.nextColor:
                    loadColor();

                    mTimer = new Timer();

                    mTimer.scheduleAtFixedRate(new TimerTask()
                    {
                      public void run()
                      {
                         Random gen = new Random();
                         cur_scr.setBackgroundColor(Color.argb(255, gen.nextInt(256), gen.nextInt(256), gen.nextInt(256)));
                      }
                    }, 0, 2000);

                    break;
        }
    }

logcat的:

05-10 15:46:12.325: W/dalvikvm(346): threadid=9: thread exiting with uncaught exception (group=0x40015560)
05-10 15:46:12.344: E/AndroidRuntime(346): FATAL EXCEPTION: Timer-0
05-10 15:46:12.344: E/AndroidRuntime(346): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
05-10 15:46:12.344: E/AndroidRuntime(346):  at android.view.ViewRoot.checkThread(ViewRoot.java:2932)
05-10 15:46:12.344: E/AndroidRuntime(346):  at android.view.ViewRoot.invalidateChild(ViewRoot.java:642)
05-10 15:46:12.344: E/AndroidRuntime(346):  at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:668)
05-10 15:46:12.344: E/AndroidRuntime(346):  at android.view.ViewGroup.invalidateChild(ViewGroup.java:2511)
05-10 15:46:12.344: E/AndroidRuntime(346):  at android.view.View.invalidate(View.java:5279)
05-10 15:46:12.344: E/AndroidRuntime(346):  at android.view.View.setBackgroundDrawable(View.java:7626)
05-10 15:46:12.344: E/AndroidRuntime(346):  at android.view.View.setBackgroundColor(View.java:7516)
05-10 15:46:12.344: E/AndroidRuntime(346):  at com.haxad0x.tools.Core$1.run(Core.java:162)
05-10 15:46:12.344: E/AndroidRuntime(346):  at java.util.Timer$TimerImpl.run(Timer.java:284)
05-10 15:46:14.194: D/AndroidRuntime(346): Shutting down VM
05-10 15:46:14.194: W/dalvikvm(346): threadid=1: thread exiting with uncaught exception (group=0x40015560)
05-10 15:46:14.194: I/Process(346): Sending signal. PID: 346 SIG: 9

请帮帮我!谢谢你们!


        case R.id.nextColor:
            loadColor();

            mTimer = new Timer();

            mTimer.scheduleAtFixedRate(new TimerTask()
            {
              public void run()
              {
                  runOnUiThread(new Runnable()
                  {
                      public void run() {
                         Random gen = new Random();
                         cur_scr.setBackgroundColor(Color.argb(255, gen.nextInt(256), gen.nextInt(256), gen.nextInt(256)));
                      }
                  });
              }
            }, 0, 2000);

            break;

代码以这种方式工作。我只有一个问题:这是一个很长的代码吗?我的意思是;它是否因为所有的空隙而影响性能?我可以缩短它吗?

1 个答案:

答案 0 :(得分:1)

很可能你的计时器在您的活动被销毁后仍然存在,并且您的视图不再存在(导致findViewById失败),并且您可以看到不同的视图。

您需要做的是停止onDestroy中的活动计时器。

将您的计时器变量创建为类级变量,也许是mTimer .. 所以你有

public class YourActivity extends Activity {
  private Timer mTimer = null;

  public void onCreate(Bundle state) {
    //setContentView
    mTimer = new Timer();
    mTimer.scheduleAtFixedRate(new TimerTask()
    {
      public void run()
      {
         Random gen = new Random();
         cur_scr = (LinearLayout) findViewById(R.id.colorScreen);
         cur_scr.setBackgroundColor(Color.argb(255, gen.nextInt(256), gen.nextInt(256), gen.nextInt(256)));
      }
    }, 0, 30000);
  } 

  public void onDestroy() {
    mTimer.cancel();
  }
}

- 更新 - 虽然我仍然同意您应该进行上述更改,但看起来TimerTask是在新线程(而不是主UI线程)中运行的。您需要在UI线程上运行与UI一起使用的任何代码。像这样使用Activity.runOnUiThread

mTimer.scheduleAtFixedRate(new TimerTask()
{
      public void run()
      {
         Random gen = new Random();
         runOnUiThread(new Runnable() {
            @Override
            public void run() {
               //actually you should probably put a try catch around the code below so it doesn't crash your app if somehow the view isn't found any longer.. It should work as long as you remove the timer task on onDestroy but to be safe i would put a try catch in.
               cur_scr = (LinearLayout) findViewById(R.id.colorScreen);
               cur_scr.setBackgroundColor(Color.argb(255, gen.nextInt(256), gen.nextInt(256), gen.nextInt(256)));
            }
         });

      }
}

使用Handler可能更容易做到这一点。

我就是这样做的:

public class YourActivity extends Activity {
  private static final int BG_CHANGE_INTERVAL = 30 * 1000;
  private Handler mHandler = null;
  private Runnable mUpdateBgRunnable = new Runnable() {
    Random gen = new Random();
    @Override
    public void run() {
      cur_scr = (LinearLayout) findViewById(R.id.colorScreen);
      cur_scr.setBackgroundColor(Color.argb(255, gen.nextInt(256), gen.nextInt(256), gen.nextInt(256)));
      updateBg();
    }

  }
  public void onCreate(Bundle state) {
    //setContentView
    mHandler = new Handler();
    updateBg();
  } 

  private void updateBg() {
     mHandler.removeCallbacks(mUpdateBgRunnable);
     mHandler.postDelayed(mUpdateBgRunnable, BG_CHANGE_INTERVAL);
  }

  public void onDestroy() {
    mTimer.cancel();
  }
}

有关处理程序与计时器的详细信息,请参阅此article