简体   繁体   English

是否有任何工作线程可以更新android中的UI?

[英]If any worker thread can update UI in android?

I suppose that we can't update any UI view elements ( TextView , EditText etc) in any worker thread but in the below example, I am able to update views from a worker thread, not all the times but only sometimes. 我想我们无法在任何工作线程中更新任何UI view元素( TextViewEditText等),但是在下面的示例中,我能够从工作线程中更新views ,而并非总是如此,而是有时可以。

See below example where I'm updating UI elements in worker thread - 请参阅下面的示例,其中我在工作线程中更新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();
    }
}

Here is my worker thread - 这是我的工作线程-

class MyAndriodThread implements Runnable
{
    @Override
    public void run()
    {
        AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
    }
}

Believe me, I won't get any exception saying - 相信我,我不会有任何例外地说-

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

And I can see the update UI on emulator. 我可以在模拟器上看到更新UI。

But if I do any heavy task (sleeping) in worker thread then I got the expected above exception, why it is happening like this? 但是,如果我在工作线程中执行了任何繁重的任务(睡眠),那么我得到了上述预期的异常,为什么会这样发生? I am a new guy on android multithreading please get me out from this confusion. 我是android多线程新手,请让我摆脱这种混乱。

If i will change my worker thread to this i will get above exception - 如果我将我的工作线程更改为此,我将获得上述例外-

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.");
    }
}

And if my UI or main thread is waiting (join()) then i get exact output no exception at all, see this - 如果我的UI或主线程正在等待(join()),那么我得到的输出完全没有异常,请参见此-

MyAndriodThread myTask = new MyAndriodThread();
        Thread t1 = new Thread(myTask, "Anhad");
        t1.start();
        try
        {
            t1.join();
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }

Update 更新资料
First I thought it's rendering scheme then i changed my program by using ProgressBar..then i got really unexpected output... Still no exception mean i can update my progress bar in worker thread. 首先,我认为这是一种渲染方案,然后我使用ProgressBar更改了程序。然后,我得到了意想不到的输出...仍然没有例外,这意味着我可以在工作线程中更新进度条。 See below- 见下文-

 @Override
    public void run()
    {
        for(int i = 1; i<=10; i++)
        {
            try
            {
                Thread.sleep(2000);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            activity.progressBar.setProgress(i);
        }
    }

So Bajrang Hudda I am guessing you are mostly satisfied by the ShadyGoneInsane answer. 因此,我猜Bajrang HuddaShadyGoneInsane的回答很满意。 I truly agree with him, that once a view is rendered and attached to the Window you cannot access it from worker thread directly. 我真的同意他的观点,即一旦渲染视图并将其附加到Window后,您将无法直接从辅助线程访问它。

To answer your updated question regarding progressBar, ProgressBar internally uses a Runnable to update its progress which it then post(r) using its handler. 为了回答有关progressBar的更新问题,ProgressBar在内部使用Runnable更新其进度,然后使用其处理程序过帐post(r)

To explain in detail, when you try to update a ProgressBar from worker thread progressBar.setProgress(i) the progressBar first checks if the call is made on MainThread, if so update directly; 详细解释一下,当您尝试从工作线程progressBar.setProgress(i)更新ProgressBar时,progressBar首先检查是否在MainThread上进行了调用,如果是,则直接更新;否则,直接进行更新。 else it creates a new Runnable RefreshProgressRunnable to update the progress, which it then post(r) using its handler. 否则它会创建一个新的Runnable RefreshProgressRunnable更新进度,它然后post(r)使用其处理程序。

Eg new Handler().post(r) 例如new Handler().post(r)

and I am sure you have also used something like this before view.post(r) 而且我确定您在view.post(r)之前也使用过类似的方法

ProgressBar source code check for method setProgress() ProgressBar源代码检查方法setProgress()

Hope this clears your doubt. 希望这能消除您的疑问。

The reason here is the thread was able to make changes in the TextView object only till it was not visible on UI screen ie not rendered. 这是因为线程只能在TextView对象中进行更改,直到它在UI屏幕上不可见即未呈现为止。 It is only after the view rendering, that any thread except the Main thread may not make changes to any UI components. 只有在渲染视图之后,除主线程以外的任何线程都不得对任何UI组件进行更改。

So when you do this : 因此,当您这样做时:

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.");
    }
}

you are trying to change text after view rendering which causes the Exception 您试图在视图渲染后更改导致异常的文本

while in the below code snippet the thread was able to make changes in the TextView object because it was not visible on UI screen ie not rendered. 在下面的代码段中,线程能够在TextView对象中进行更改,因为它在UI屏幕上不可见,即未呈现。

class MyAndriodThread implements Runnable
{
    @Override
    public void run()
    {
        AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
    }
}

As pointed by @JohnnyAW - when you use join() you delay rendering until the worker is ready, so you don't get exception even if the worker thread sleeps 正如@JohnnyAW所指出的那样-当您使用join()时,延迟渲染直到工作器准备好为止,因此即使工作器线程处于睡眠状态,也不会出现异常

when you did this Thread.sleep(2000); 当您执行此操作时Thread.sleep(2000); the TextView was rendered in between and then you tried to touch your view from this workerThread which caused the crash TextView在两者之间呈现,然后您尝试从此workerThread触摸视图,从而导致崩溃

you can also try Thread.sleep(0); 您也可以尝试Thread.sleep(0); and see it won't raise any exception and your app will not crash . 并且看到它不会引发任何异常,并且您的应用程序不会崩溃。

This behaviour really happens. 此行为确实发生。 As it is clearly mentioned at android's developer portal. 正如在android的开发人员门户网站上明确提到的那样。

There is a para with Explicit references which states that 有一个带有显式引用的段落,其中指出:

Many tasks on non-main threads have the end goal of updating UI objects. 非主线程上的许多任务的最终目标是更新UI对象。 However, if one of these threads accesses an object in the view hierarchy, application instability can result: If a worker thread changes the properties of that object at the same time that any other thread is referencing the object, the results are undefined. 但是,如果这些线程之一访问视图层次结构中的对象,则可能导致应用程序不稳定:如果辅助线程在任何其他线程引用该对象的同时更改了该对象的属性,则结果是不确定的。

So this can happens and undefined. 因此,这可能会发生并且是不确定的。

https://developer.android.com/topic/performance/threads.html https://developer.android.com/topic/performance/threads.html

You cannot update anything in the ui thread from a background thread, to do that you need to use a Handler or use AsyncTask.onProgressUpdate , for more details have a detail look at AsyncTask . 您不能从后台线程更新ui线程中的任何内容,而需要使用HandlerAsyncTask.onProgressUpdate ,有关更多详细信息,请查看AsyncTask

Update 更新资料

It is not recommended to update your UI from worker thread without involving Handler or use Activity.runOnUiThread(Runnable) . 不建议从不涉及Handler工作线程中更新UI或使用Activity.runOnUiThread(Runnable) In your case when you are updating your TextView from MyAndriodThread , your view is in transition state, if you will try to update your UI your view may get the update or results in crashing, according to documentation : 在当你更新你的情况TextViewMyAndriodThread ,您的看法是转型国家,如果你尝试更新你的用户界面视图可能会得到更新或崩溃的结果,根据文档

If a worker thread changes the properties of that object at the same time that any other thread is referencing the object, the results are undefined 如果辅助线程在任何其他线程引用该对象的同时更改该对象的属性,则结果不确定

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM