"Only the original thread that created a view hierarchy can touch its views." 使用 TimerTask 时出错
"Only the original thread that created a view hierarchy can touch its views." error when using TimerTask
我创建了一个由一个 activity 组成的应用程序,其中包括一个 TextView
。这是我的 activity 的 XML :
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
tools:context="zeratops.myApp.MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="#000000"
android:layout_alignParentTop="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dp"
android:text=""
android:textColor="#FF0000"
android:id="@+id/text_1"
android:paddingTop="200dp"
android:layout_gravity="center" />
</FrameLayout>
</RelativeLayout>
我的java代码是每秒设置这个textView
的文本。这是我用来执行此任务的代码:
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView text = (TextView) findViewById(R.id.text_1);
Timer timer = new Timer();
TimerTask timertask = new TimerTask() {
@Override
public void run() {
text.setText("test");
}
};
timer.schedule(timertask, 0, 1000);
}
}
问题
当我启动加载我的应用程序并在我的 LG G2 上启动它时,我遇到了以下问题:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
我确定了这些行:
timer.schedule(timertask, 0, 1000);
导致错误,因为我删除它时没有异常。我错过了一些检查吗?
您需要执行
text.setText("test");
在 UI 线程中。您可以使用 Handlers 来做到这一点。
final Handler myHandler = new Handler();
myHandler.post(myRunnable);
final Runnable myRunnable = new Runnable() {
public void run() {
text.setText("test");
}
};
您的 run()
方法将在后台线程上调用,因此它无法操作 UI。
一个更轻量级的解决方案是:
public class MainActivity extends AppCompatActivity {
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text_1);
onEverySecond.run();
}
private Runnable onEverySecond=new Runnable() {
@Override
public void run() {
text.setText("test");
text.postDelayed(this, 1000);
}
}
}
在指定的延迟时间后,postDelayed()
在主应用程序线程上调用您的 run()
方法。在这里,我们使用它让 Runnable
在 1000 毫秒后再次安排自己到 运行。
要停止 postDelayed()
"loop",请在您的 MainActivity
上调用 removeCallbacks(onEverySecond)
。
与在 TimerTask
中使用 runOnUiThread()
相比,它的优势在于成本更低,因为您没有创建单独的线程(Timer
/TimerTask
线程)。
我创建了一个由一个 activity 组成的应用程序,其中包括一个 TextView
。这是我的 activity 的 XML :
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
tools:context="zeratops.myApp.MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="#000000"
android:layout_alignParentTop="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dp"
android:text=""
android:textColor="#FF0000"
android:id="@+id/text_1"
android:paddingTop="200dp"
android:layout_gravity="center" />
</FrameLayout>
</RelativeLayout>
我的java代码是每秒设置这个textView
的文本。这是我用来执行此任务的代码:
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView text = (TextView) findViewById(R.id.text_1);
Timer timer = new Timer();
TimerTask timertask = new TimerTask() {
@Override
public void run() {
text.setText("test");
}
};
timer.schedule(timertask, 0, 1000);
}
}
问题
当我启动加载我的应用程序并在我的 LG G2 上启动它时,我遇到了以下问题:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
我确定了这些行:
timer.schedule(timertask, 0, 1000);
导致错误,因为我删除它时没有异常。我错过了一些检查吗?
您需要执行
text.setText("test");
在 UI 线程中。您可以使用 Handlers 来做到这一点。
final Handler myHandler = new Handler();
myHandler.post(myRunnable);
final Runnable myRunnable = new Runnable() {
public void run() {
text.setText("test");
}
};
您的 run()
方法将在后台线程上调用,因此它无法操作 UI。
一个更轻量级的解决方案是:
public class MainActivity extends AppCompatActivity {
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text_1);
onEverySecond.run();
}
private Runnable onEverySecond=new Runnable() {
@Override
public void run() {
text.setText("test");
text.postDelayed(this, 1000);
}
}
}
在指定的延迟时间后,postDelayed()
在主应用程序线程上调用您的 run()
方法。在这里,我们使用它让 Runnable
在 1000 毫秒后再次安排自己到 运行。
要停止 postDelayed()
"loop",请在您的 MainActivity
上调用 removeCallbacks(onEverySecond)
。
与在 TimerTask
中使用 runOnUiThread()
相比,它的优势在于成本更低,因为您没有创建单独的线程(Timer
/TimerTask
线程)。