我正在尝试构建一个具有一些线程用法的应用。工作时,我在尝试从线程访问TextView时发现了不可预测的行为。 我知道android禁止我们直接从另一个线程访问视图。我也知道如何使用AsyncTask,Handler,Activity.runOnUiThread(Runnable),View.post(Runnable)从另一个线程访问主线程的视图,View.postDelayed(Runnable,long)等
这是我的代码段 -
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = (TextView) findViewById(R.id.tv);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
/*try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
/*for(int i= 0 ; i<10000000 ;i++){
Log.i("logger"," i ="+i);
tv.setText("i = "+i);
}*/
tv.setText("This is the new text");
}
});
t.start();
}
当我运行此代码时,不会发生崩溃,但是一旦我取消注释Thread.sleep()或循环部分,就会发生崩溃。 任何人都可以解释这种行为,为什么会发生这种情况以及为什么在给定代码时不会发生崩溃。请不要发布解释如何执行此操作的答案。
提前致谢。
当我取消注释Thread.sleep()代码部分 -
时,这是崩溃日志05-26 21:11:47.244: E/AndroidRuntime(14310): FATAL EXCEPTION: Thread-13346
05-26 21:11:47.244: E/AndroidRuntime(14310): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:5225)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1062)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:292)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.view.View.requestLayout(View.java:15316)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.widget.TextView.checkForRelayout(TextView.java:6659)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.widget.TextView.setText(TextView.java:3670)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.widget.TextView.setText(TextView.java:3528)
05-26 21:11:47.244: E/AndroidRuntime(14310): at android.widget.TextView.setText(TextView.java:3503)
05-26 21:11:47.244: E/AndroidRuntime(14310): at com.example.sampleproject.MainActivity$1.run(MainActivity.java:33)
05-26 21:11:47.244: E/AndroidRuntime(14310): at java.lang.Thread.run(Thread.java:864)
05-26 21:11:47.284: D/memalloc(14310): ion: Mapped buffer base:0x5432a000 size:2088960 offset:0 fd:66
答案 0 :(得分:4)
由于竞争条件。
当视图试图在错误的线程中使自身无效时,崩溃就会发生。 setText()
不会使视图无效,除非视图至少被测量过一次且TextView
具有非空mLayout
。
使用setContentView()
创建新的视图层次结构时,测量视图的消息将发布在UI线程的队列中,但是在测量传递创建之前,您的线程可能会运行文本视图的布局。
有关详细信息,请查看source。
答案 1 :(得分:0)
您无法从其他线程更新UI,请改用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = (TextView) findViewById(R.id.tv);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv.setText("This is the new text");
}
});
}
});
t.start();
}
如果你想在这里了解更多文章:
http://android-developers.blogspot.co.uk/2009/05/painless-threading.html
答案 2 :(得分:0)
尝试执行任何与此UI交互的代码
runOnUiThread(new Runnable() {
@Override
public void run() {
// Put your UI interactions here
}
});
答案 3 :(得分:0)
以下代码对我来说工作正常:
public class MainActivity extends Activity {
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle data = msg.getData();
String result=data.getString("key");
textview.setText(result);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TextView tv = (TextView) findViewById(R.id.tv);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Bundle bundle=new Bundle();
bundle.putString("key","you data");
Message message=new Message();
message.setData(bundle);
handler.sendMessage(message);
}
});
t.start();
}
}