Android“只有创build视图层次结构的原始线程才能触及其视图。”

我已经在Android中构build了一个简单的音乐播放器。 每首歌曲的视图都包含一个SeekBar,实现如下:

public class Song extends Activity implements OnClickListener,Runnable { private SeekBar progress; private MediaPlayer mp; // ... private ServiceConnection onService = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder rawBinder) { appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer progress.setVisibility(SeekBar.VISIBLE); progress.setProgress(0); mp = appService.getMP(); appService.playSong(title); progress.setMax(mp.getDuration()); new Thread(Song.this).start(); } public void onServiceDisconnected(ComponentName classname) { appService = null; } }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.song); // ... progress = (SeekBar) findViewById(R.id.progress); // ... } public void run() { int pos = 0; int total = mp.getDuration(); while (mp != null && pos<total) { try { Thread.sleep(1000); pos = appService.getSongPosition(); } catch (InterruptedException e) { return; } catch (Exception e) { return; } progress.setProgress(pos); } } 

这工作正常。 现在我想要一个计时器来计算歌曲进度的秒/分钟。 所以我把一个TextView放在布局中,用onCreate() findViewById()来获得它,然后在progress.setProgress(pos)之后把它放在run() progress.setProgress(pos)

 String time = String.format("%d:%d", TimeUnit.MILLISECONDS.toMinutes(pos), TimeUnit.MILLISECONDS.toSeconds(pos), TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes( pos)) ); currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time); 

但最后一行给我例外:

 android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 

然而,我在这里做的基本上是一样的事情,因为我在做SeekBar – 在onCreate创build视图,然后在run()触摸它 – 并没有给我这个抱怨。 那么这里是否有明显的错误? 我对Android和Java都是新手,所以如果它是愚蠢的,我不会感到惊讶。

您必须将更新ui的后台任务部分移到主线程上。 这里有一个简单的代码:

 runOnUiThread(new Runnable() { @Override public void run() { //stuff that updates ui } }); 

Activity.runOnUiThread文档

只要在后台运行的方法中嵌套这个,然后将实现任何更新的代码复制粘贴到块的中间。 只包括尽可能less的代码量,否则就开始挫败后台线程的目的。

我解决了这个问题,把runOnUiThread( new Runnable(){ .. inside run()

 thread = new Thread(){ @Override public void run() { try { synchronized (this) { wait(5000); runOnUiThread(new Runnable() { @Override public void run() { dbloadingInfo.setVisibility(View.VISIBLE); bar.setVisibility(View.INVISIBLE); loadingText.setVisibility(View.INVISIBLE); } }); } } catch (InterruptedException e) { e.printStackTrace(); } Intent mainActivity = new Intent(getApplicationContext(),MainActivity.class); startActivity(mainActivity); }; }; thread.start(); 

我的解决scheme是:

 private void setText(final TextView text,final String value){ runOnUiThread(new Runnable() { @Override public void run() { text.setText(value); } }); } 

在后台线程上调用这个方法

通常,涉及用户界面的任何操作都必须在主线程或UI线程中完成,也就是执行onCreate()和事件处理的那个线程。 一种确定的方法是使用runOnUiThread() ,另一种方法是使用Handlers。

ProgressBar.setProgress()有一个机制,它总是在主线程上执行,所以这就是为什么它工作。

看无痛线程 。

我一直在这种情况下,但我find了处理程序对象的解决scheme。

在我的情况下,我想用观察者模式更新ProgressDialog。 我的视图实现观察者并重写更新方法。

所以,我的主线程创build视图,另一个线程调用更新ProgressDialop和….的方法:

 Only the original thread that created a view hierarchy can touch its views. 

处理器对象可以解决这个问题。

下面,我的代码的不同部分:

 public class ViewExecution extends Activity implements Observer{ static final int PROGRESS_DIALOG = 0; ProgressDialog progressDialog; int currentNumber; public void onCreate(Bundle savedInstanceState) { currentNumber = 0; final Button launchPolicyButton = ((Button) this.findViewById(R.id.launchButton)); launchPolicyButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showDialog(PROGRESS_DIALOG); } }); } @Override protected Dialog onCreateDialog(int id) { switch(id) { case PROGRESS_DIALOG: progressDialog = new ProgressDialog(this); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setMessage("Loading"); progressDialog.setCancelable(true); return progressDialog; default: return null; } } @Override protected void onPrepareDialog(int id, Dialog dialog) { switch(id) { case PROGRESS_DIALOG: progressDialog.setProgress(0); } } // Define the Handler that receives messages from the thread and update the progress final Handler handler = new Handler() { public void handleMessage(Message msg) { int current = msg.arg1; progressDialog.setProgress(current); if (current >= 100){ removeDialog (PROGRESS_DIALOG); } } }; //The method called by the observer (the second thread) @Override public void update(Observable obs, Object arg1) { Message msg = handler.obtainMessage(); msg.arg1 = ++currentPluginNumber; handler.sendMessage(msg); } } 

这个解释可以在这个页面上find,你必须阅读“第二个线程的例子ProgressDialog”。

我看到你已经接受了@普罗斯的答案。 以防万一,你也可以使用处理程序! 首先,做int字段。

  private static final int SHOW_LOG = 1; private static final int HIDE_LOG = 0; 

接下来,将一个处理程序实例作为一个字段。

  //TODO __________[ Handler ]__________ @SuppressLint("HandlerLeak") protected Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // Put code here... // Set a switch statement to toggle it on or off. switch(msg.what) { case SHOW_LOG: { ads.setVisibility(View.VISIBLE); break; } case HIDE_LOG: { ads.setVisibility(View.GONE); break; } } } }; 

做一个方法。

 //TODO __________[ Callbacks ]__________ @Override public void showHandler(boolean show) { handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG); } 

最后,把这个onCreate()方法。

 showHandler(true); 

我有类似的问题,我的解决scheme是丑陋的,但它

 void showCode() { hideRegisterMessage(); // Hides view final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { showRegisterMessage(); //Shows view } }, 3000); // after 3 sec } 

这是明确地抛出一个错误。 它说任何线索创造了一个观点,只有这个观点可以触及它的观点。 这是因为创build的视图是在该线程的空间内。 视图创build(GUI)发生在UI(主)线程中。 所以,你总是使用UI线程来访问这些方法。

在这里输入图像描述

在上图中,进度variables在UI Thread的空间内。 所以,只有UI线程可以访问这个variables。 在这里,你通过新的线程()访问进度,这就是为什么你有错误。

使用这个代码,并且不需要runOnUiThread函数:

 private Handler handler; private Runnable handlerTask; void StartTimer(){ handler = new Handler(); handlerTask = new Runnable() { @Override public void run() { // do something textView.setText("some text"); handler.postDelayed(handlerTask, 1000); } }; handlerTask.run(); } 

我使用HandlerLooper.getMainLooper()它为我工作得很好

  Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // Any UI task, example textView.setText("your text"); } }; handler.sendEmptyMessage(1); 

这是提到的exception的堆栈跟踪

  at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843) at android.view.View.requestLayout(View.java:16474) at android.view.View.requestLayout(View.java:16474) at android.view.View.requestLayout(View.java:16474) at android.view.View.requestLayout(View.java:16474) at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352) at android.view.View.requestLayout(View.java:16474) at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352) at android.view.View.setFlags(View.java:8938) at android.view.View.setVisibility(View.java:6066) 

所以如果你去挖掘,那么你就会知道

 void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } } 

mThread在像下面这样的构造函数中进行初始化

 mThread = Thread.currentThread(); 

我的意思是说,当我们创build特定的视图时,我们在UI线程上创build了它,然后尝试在工作线程中进行修改。

我们可以通过下面的代码片段来validation它

 Thread.currentThread().getName() 

当我们膨胀布局,然后在哪里得到例外。

如果你不想使用runOnUiThread API,你可以AsynTask实现AsynTask来完成需要几秒钟的操作。但是在这种情况下,在doinBackground()处理完你的工作之后,你需要返回onPostExecute()的成品视图。 Android实现只允许主UI线程与视图交互。

谢谢

对我来说,问题是我从代码中明确地调用了onProgressUpdate() 。 这不应该做。 我调用了publishProgress()来解决这个错误。

在我的情况下,我在适配器中有EditText ,并且它已经在UI线程中,但是当这个活动加载时,这个错误会崩溃。

我的解决scheme是我需要从XML中的EditText中删除<requestFocus />

我知道我迟到了派对,但是这发生在我从AsynctaskdoInBackground调用UI更改,而不是使用onPostExecute

处理onPostExecute的用户界面解决了我的问题。