1

我正在开发一个 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;
        }
    }

日志猫:

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;

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

4

1 回答 1

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

有关处理程序与计时器的更多信息,请参阅本文

于 2012-05-09T21:57:36.440 回答