开发反应时间测算游戏时遇Timer-0致命异常求助
解决Android Timer线程操作UI导致的CalledFromWrongThreadException问题
嘿,这个问题我太熟了!你遇到的ViewRootImpl$CalledFromWrongThreadException是Android开发里的经典坑——你在Timer的后台线程里直接操作UI组件了!
Android的UI组件有个严格的规则:只有创建视图层级的原始线程(也就是主线程/UI线程)才能修改视图。而Timer默认会在单独的后台线程执行任务,这就直接违反了这个规则,系统自然会抛出异常阻止你。
下面给你几个靠谱的解决方案,按推荐程度排序:
方案1:用Handler替代Timer(最推荐,符合Android线程模型)
Handler天生和UI线程绑定,用它来处理定时任务是最稳妥的方式:
// 在Activity里定义Handler和Runnable private Handler uiHandler = new Handler(Looper.getMainLooper()); private Runnable timerTask = new Runnable() { private long elapsedTime = 0; @Override public void run() { // 这里直接就是UI线程,放心操作UI! elapsedTime += 100; yourButton.setText("当前耗时:" + elapsedTime + "ms"); // 重复执行任务,间隔100ms uiHandler.postDelayed(this, 100); } }; // 启动定时任务(比如在按钮点击时) public void startGame(View view) { uiHandler.post(timerTask); } // 记得在页面销毁时停止任务,避免内存泄漏 @Override protected void onDestroy() { super.onDestroy(); uiHandler.removeCallbacks(timerTask); }
方案2:用runOnUiThread在Timer任务里切换线程
如果你不想换掉Timer,可以把UI操作代码包裹在Activity的runOnUiThread方法里,强制切换到UI线程执行:
new Timer().scheduleAtFixedRate(new TimerTask() { private long elapsedTime = 0; @Override public void run() { // Timer的任务在后台线程,不能直接碰UI,所以切换到主线程 runOnUiThread(new Runnable() { @Override public void run() { elapsedTime += 100; yourButton.setText("当前耗时:" + elapsedTime + "ms"); } }); } }, 0, 100);
方案3:Kotlin项目用协程(最简洁)
如果你的项目是Kotlin开发的,用协程的Main调度器可以更优雅地解决问题:
import kotlinx.coroutines.* // 在Activity/Fragment里启动协程 fun startGame() { lifecycleScope.launch(Dispatchers.Main) { var elapsedTime = 0L while (isActive) { delay(100) elapsedTime += 100 yourButton.text = "当前耗时:$elapsedTime ms" } } }
额外提醒
不管用哪种方案,都要记得在页面销毁时停止定时任务,不然会导致内存泄漏哦!
内容的提问来源于stack exchange,提问作者FADE




