如果任何工作线程可以在android中更新UI?

时间:2017-02-16 06:41:08

标签: java android multithreading

我想我们无法在任何工作线程中更新任何UI view元素(TextViewEditText等),但在下面的示例中,我能够更新来自工作线程的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);
        }
    }

5 个答案:

答案 0 :(得分:7)

所以 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)

之前也使用了类似的内容

ProgressBar source code检查方法setProgress()

希望这可以解除你的怀疑。

答案 1 :(得分:5)

这里的原因是线程只能在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);,看看它不会引发任何异常,您的应用也不会崩溃。

答案 2 :(得分:1)

这种行为确实发生了。正如在android的开发者门户网站上清楚地提到的那样。

有一个显式引用的段落,表明

非主线程上的许多任务的最终目标是更新UI对象。但是,如果其中一个线程访问视图层次结构中的对象,则可能导致应用程序不稳定:如果工作线程在任何其他线程引用该对象的同时更改该对象的属性,则结果是未定义的。

所以这可能发生并且未定义。

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

答案 3 :(得分:-1)

您无法从后台线程更新ui线程中的任何内容,为此您需要使用Handler或使用AsyncTask.onProgressUpdate,有关详细信息,请详细查看AsyncTask

<强>更新

建议不要在不涉及Handler或使用Activity.runOnUiThread(Runnable)的情况下从工作线程更新UI。在您从TextView更新MyAndriodThread时,您的视图处于转换状态,如果您尝试更新UI,则视图可能会获得更新或导致崩溃,{base URL 3}}:

  

如果工作线程同时更改该对象的属性   任何其他线程引用该对象的时间,结果是   未定义

答案 4 :(得分:-3)

您应该尝试使用 BroadcastReceiver 来更新用户界面。 例如, 一旦您的工作线程完成了它的任务,它应该使用以下方式发送广播:

Intent intent = new Intent();  
intent.setAction("com.example.harshil.charotarexplore.android.action.broadcast");
sendBroadcast(intent);

在你班上:

private static final String BROADCAST = "com.example.harshil.charotarexplore.android.action.broadcast";
private BroadcastReceiver locationReceiver;

@Override
    protected void onCreate(Bundle savedInstanceState) {
...
locationReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction().matches(BROADCAST)) {
                    onLocationAvailable();
                }
            }
        };

onStart

registerReceiver(locationReceiver, new IntentFilter(BROADCAST));