问题描述
我想我们无法在任何工作线程中更新任何UI view
元素( TextView
, EditText
等),但是在下面的示例中,我能够从工作线程中更新views
,而并非总是如此,而是有时可以。
请参阅下面的示例,其中我在工作线程中更新UI元素-
public class AndroidBasicThreadActivity extends AppCompatActivity
{
public static TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_android_basic_thread);
textView = (TextView) findViewById(R.id.textview);
MyAndriodThread myTask = new MyAndriodThread();
Thread t1 = new Thread(myTask, "Bajrang Hudda");
t1.start();
}
}
这是我的工作线程-
class MyAndriodThread implements Runnable
{
@Override
public void run()
{
AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
}
}
相信我,我不会有任何例外地说-
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
我可以在模拟器上看到更新UI。
但是,如果我在工作线程中执行了任何繁重的任务(睡眠),那么我得到了上述预期的异常,为什么会这样发生? 我是android多线程新手,请让我摆脱这种混乱。
如果我将我的工作线程更改为此,我将获得上述例外-
class MyAndriodThread implements Runnable
{
@Override
public void run()
{
try
{
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
System.out.println("Child thread completed.");
}
}
如果我的UI或主线程正在等待(join()),那么我得到的输出完全没有异常,请参见此-
MyAndriodThread myTask = new MyAndriodThread();
Thread t1 = new Thread(myTask, "Anhad");
t1.start();
try
{
t1.join();
} catch (InterruptedException e)
{
e.printStackTrace();
}
更新资料
首先,我认为这是一种渲染方案,然后我使用ProgressBar更改了程序。然后,我得到了意想不到的输出...仍然没有例外,这意味着我可以在工作线程中更新进度条。
见下文-
@Override
public void run()
{
for(int i = 1; i<=10; i++)
{
try
{
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
activity.progressBar.setProgress(i);
}
}
1楼
因此,我猜Bajrang Hudda对ShadyGoneInsane的回答很满意。 我真的同意他的观点,即一旦渲染视图并将其附加到Window后,您将无法直接从辅助线程访问它。
为了回答有关progressBar的更新问题,ProgressBar在内部使用Runnable更新其进度,然后使用其处理程序过帐post(r)
。
详细解释一下,当您尝试从工作线程progressBar.setProgress(i)
更新ProgressBar时,progressBar首先检查是否在MainThread上进行了调用,如果是,则直接更新;否则,直接进行更新。
否则它会创建一个新的Runnable RefreshProgressRunnable
更新进度,它然后post(r)
使用其处理程序。
例如new Handler().post(r)
而且我确定您在view.post(r)
之前也使用过类似的方法
检查方法setProgress()
希望这能消除您的疑问。
2楼
这是因为线程只能在TextView对象中进行更改,直到它在UI屏幕上不可见即未呈现为止。 只有在渲染视图之后,除主线程以外的任何线程都不得对任何UI组件进行更改。
因此,当您这样做时:
class MyAndriodThread implements Runnable
{
@Override
public void run()
{
try
{
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
System.out.println("Child thread completed.");
}
}
您试图在视图渲染后更改导致异常的文本
在下面的代码段中,线程能够在TextView对象中进行更改,因为它在UI屏幕上不可见,即未呈现。
class MyAndriodThread implements Runnable
{
@Override
public void run()
{
AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
}
}
正如@JohnnyAW所指出的那样-当您使用join()时,延迟渲染直到工作器准备好为止,因此即使工作器线程处于睡眠状态,也不会出现异常
当您执行此操作时Thread.sleep(2000);
TextView
在两者之间呈现,然后您尝试从此workerThread触摸视图,从而导致崩溃
您也可以尝试Thread.sleep(0);
并且看到它不会引发任何异常,并且您的应用程序不会崩溃。
3楼
此行为确实发生。 正如在android的开发人员门户网站上明确提到的那样。
有一个带有显式引用的段落,其中指出:
非主线程上的许多任务的最终目标是更新UI对象。 但是,如果这些线程之一访问视图层次结构中的对象,则可能导致应用程序不稳定:如果辅助线程在任何其他线程引用该对象的同时更改了该对象的属性,则结果是不确定的。
因此,这可能会发生并且是不确定的。
4楼
您不能从后台线程更新ui线程中的任何内容,而需要使用Handler
或AsyncTask.onProgressUpdate
,有关更多详细信息,请查看 。
更新资料
不建议从不涉及Handler
工作线程中更新UI或使用Activity.runOnUiThread(Runnable)
。
在当你更新你的情况TextView
从MyAndriodThread
,您的看法是转型国家,如果你尝试更新你的用户界面视图可能会得到更新或崩溃的结果,根据 :
如果辅助线程在任何其他线程引用该对象的同时更改该对象的属性,则结果不确定