从Thread更新UI

时间:2010-12-06 18:13:04

标签: android multithreading user-interface

我想从更新Progressbar的Thread更新我的UI。不幸的是,当从“runnable”更新进度条的drawable时,进度条消失了! 在onCreate()中改变其他方面的进度条的可绘制效果!

任何建议?

public void onCreate(Bundle savedInstanceState) {
    res = getResources();
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gameone);
    pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); //**Works**/
    handler.postDelayed(runnable, 1);       
}

private Runnable runnable = new Runnable() {
    public void run() {  
        runOnUiThread(new Runnable() { 
            public void run() 
            { 
                //* The Complete ProgressBar does not appear**/                         
                pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); 
            } 
        }); 
    }
}

7 个答案:

答案 0 :(得分:61)

你应该在AsyncTask(智能背景线程)和ProgressDialog

的帮助下完成此操作

AsyncTask可以正确,轻松地使用UI线程。该类允许执行后台操作并在UI线程上发布结果,而无需操纵线程和/或处理程序。

异步任务由在后台线程上运行的计算定义,其结果在UI线程上发布。异步任务由3种泛型类型定义,称为Params,Progress和Result,以及4个步骤,分别称为begin,doInBackground,processProgress和end。

4个步骤

执行异步任务时,任务将经历4个步骤:

onPreExecute(),在执行任务后立即在UI线程上调用。此步骤通常用于设置任务,例如通过在用户界面中显示进度条。

doInBackground(Params...),在onPreExecute()完成执行后立即在后台线程上调用。此步骤用于执行可能需要很长时间的后台计算。异步任务的参数将传递给此步骤。计算结果必须由此步骤返回,并将传递回最后一步。此步骤还可以使用publishProgress(Progress ...)发布一个或多个进度单元。这些值发布在UI线程的onProgressUpdate(Progress ...)步骤中。

onProgressUpdate(Progress...),在调用publishProgress(Progress ...)后在UI线程上调用。执行的时间是不确定的。此方法用于在后台计算仍在执行时显示用户界面中的任何形式的进度。例如,它可用于为进度条设置动画或在文本字段中显示日志。

onPostExecute(Result),在后台计算完成后在UI线程上调用。背景计算的结果作为参数传递给该步骤。 线程规则

此课程必须遵循一些线程规则才能正常使用:

必须在UI线程上创建任务实例。 必须在UI线程上调用execute(Params ...)。 不要手动调用onPreExecute(),onPostExecute(Result),doInBackground(Params ...),onProgressUpdate(Progress ...)。 该任务只能执行一次(如果尝试第二次执行,则会抛出异常。)

示例代码
适配器在此示例中的作用并不重要,更重要的是要了解您需要使用AsyncTask来显示进度的对话框。

private class PrepareAdapter1 extends AsyncTask<Void,Void,ContactsListCursorAdapter > {
    ProgressDialog dialog;
    @Override
    protected void onPreExecute() {
        dialog = new ProgressDialog(viewContacts.this);
        dialog.setMessage(getString(R.string.please_wait_while_loading));
        dialog.setIndeterminate(true);
        dialog.setCancelable(false);
        dialog.show();
    }
    /* (non-Javadoc)
     * @see android.os.AsyncTask#doInBackground(Params[])
     */
    @Override
    protected ContactsListCursorAdapter doInBackground(Void... params) {
        cur1 = objItem.getContacts();
        startManagingCursor(cur1);

        adapter1 = new ContactsListCursorAdapter (viewContacts.this,
                R.layout.contact_for_listitem, cur1, new String[] {}, new int[] {});

        return adapter1;
    }

    protected void onPostExecute(ContactsListCursorAdapter result) {
        list.setAdapter(result);
        dialog.dismiss();
    }
}

答案 1 :(得分:25)

我见过的最简单的解决方案 UI线程的执行是通过视图的post()方法实现的。 这是必需的,因为UI方法不是可重入的。该 方法是:

package android.view;

public class View;

public boolean post(Runnable action);

post()方法对应于SwingUtilities.invokeLater()。 不幸的是,我找不到与之相对应的简单内容 SwingUtilities.invokeAndWait(),但可以构建更晚的版本 基于前者的监视器和旗帜。

所以你节省的是创建一个处理程序。你只需要 找到你的观点,然后发布。你可以通过找到你的观点 findViewById()如果您倾向于使用id-ed资源。所结果的 代码很简单:

/* inside your non-UI thread */

view.post(new Runnable() {
        public void run() {
            /* the desired UI update */
        }
    });
}

注意:与SwingUtilities.invokeLater()方法相比 View.post()确实返回一个布尔值,表示是否 view具有关联的事件队列。自从我用过 invokeLater()resp。 post()无论如何只是为了火而忘记, 我没有检查结果值。基本上你应该 只有在调用了onAttachedToWindow()之后才调用post() 在视图上。

最好的问候

答案 2 :(得分:11)

使用AsyncTask类(而不是Runnable)。它有一个名为onProgressUpdate的方法,它可以影响UI(它在UI线程中调用)。

答案 3 :(得分:11)

如果您使用Handler(我看到您这样做,并希望您在UI线程上创建了它的实例),那么请不要在runOnUiThread()内使用runnable。从非UI线程执行smth时使用runOnUiThread(),但是Handler已经在UI线程上执行了runnable

尝试像这样做:

private Handler mHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gameone);
    res = getResources();
    // pB.setProgressDrawable(getResources().getDrawable(R.drawable.green)); **//Works**
    mHandler.postDelayed(runnable, 1);
}

private Runnable runnable = new Runnable() {
    public void run() {
        pB.setProgressDrawable(getResources().getDrawable(R.drawable.green));
        pB.invalidate(); // maybe this will even not needed - try to comment out
    }
};

答案 4 :(得分:9)

您需要在UI线程中创建Handler,然后使用它来发布或发送来自其他线程的消息以更新UI

答案 5 :(得分:1)

如果您不喜欢AsyncTask,可以使用observer pattern。在该示例中,使用ResponseHandler作为活动中的内部类,然后使用字符串消息设置进度条百分比...您需要确保在ResponseHandler中执行对UI的任何更改以避免冻结UI,然后您的工作线程(示例中的EventSource)可以执行所需的任务。

我会使用AsyncTask,但是观察者模式可以很好地用于自定义原因,而且更容易理解。此外,我不确定这种方式是否被广泛接受或将100%工作。我正在下载和Android插件现在测试它

答案 6 :(得分:0)

根据官方documentation的建议,您可以使用AsyncTask 来处理持续时间小于5毫秒的工作项 。如果您的任务需要更多时间,请留意其他替代方案。

HandlerThreadThreadAsyncTask的替代方案。如果您需要从HandlerThread更新UI,请在UI线程Looper上发布消息,UI线程Handler可以处理UI更新。

示例代码:

Android: Toast in a thread