在非 UI 线程中构建视图并在 UI 线程中使用

3
我正在非UI线程中创建一组webview对象,并在网页加载完成后将它们添加到队列中。 UI线程最终会查询webview,如果维护的队列中有元素,则会从队列中选择webview。这个非UI线程将一直运行并在需要时填充队列。不幸的是,当UI线程呈现webview时,虽然它得到了正确的呈现,但非UI线程突然以“CalledFromWrongThreadException”的错误失败了。我不确定这种行为是否符合预期。我已创建一个示例程序来演示相同的问题。有人能帮助我识别问题吗?
public class MultiThreadTest extends Activity {

    private volatile WebView mWv;
    private LinearLayout mLL;
    private volatile Handler nonUiThreadHandler = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mLL = (LinearLayout) findViewById(R.id.linearLayout);
        Button creator = (Button) findViewById(R.id.creator);
        creator.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                createThread();
            }
        });

        Button consumer = (Button) findViewById(R.id.consumer);
        consumer.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                mLL.addView(mWv, new LinearLayout.LayoutParams(200, 200));
                mWv.requestFocus();
            }
        });
    }

    public void createThread() {

        Thread t = new Thread (new Runnable() {         
            @Override
            public void run() {
                Looper.prepare();
                nonUiThreadHandler = new Handler();
                Looper.loop();          
            }
        }, "Creator thread");

        t.start();

        while (!t.isAlive() || nonUiThreadHandler==null) {}

        nonUiThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                constructView();
            }
        });
    }

    public void constructView() {
        mWv = new WebView(MultiThreadTest.this);
        mWv.setWebChromeClient(new WebChromeClient());
        mWv.loadUrl("http://www.yahoo.com");
        mWv.setFocusable(true);
    }
} 
1个回答

2

1
你也可以使用runOnUiThread(),它与处理程序相同但没有处理程序。 - bigstones
是的,视图必须在它们将显示的线程上构建。 - hackbod
@anothem 谢谢指出。是的,我意识到了那个限制。这就是我从UI线程执行以下行的原因。mLL.addView(mWv,new LinearLayout.LayoutParams(200, 200)); mWv.requestFocus();(接下来在下一个评论中...)(1/2) - Ashok G
如果你注意到了,意图是在非UI线程中创建Web视图(mWv),但最终在UI线程中使用它。这样做的原因是,我尝试加载的页面相当重,我不想让这个操作阻塞其他UI操作。因此,意图是等待mWv上的onPageLoadFinished操作,并在主线程中使用它。谢谢。 - Ashok G
@hackbod 感谢您的回复。基于此问题(http://stackoverflow.com/questions/4974048/is-it-safe-to-create-and-modify-views-in-a-background-thread-before-adding-them-t)所接收到的回答以及进行了一些简单实验,我能够在非 UI 线程上创建和使用视图,并在 UI 线程上消费它们。(1/2) - Ashok G
@hackbod 但是似乎存在一些特定问题,当非 UI 线程创建视图并保持活动状态时,UI 线程尝试显示相同的视图。我的意思是,对于创建的视图进行的无效操作似乎以某种奇怪的原因落在了非 UI 线程的队列中(可能与创建线程时设置的处理程序有关?)。(2/2) - Ashok G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接