Android:从后台线程传递消息到UI线程的正确方法是什么?

6
我一直在阅读关于线程、处理器、循环等方面的资料,但是我非常困惑。在我的应用程序中,我想让第一个活动启动后台工作程序。该后台工作程序将不断从TCP套接字请求数据,并(希望)在数据到达时将新信息发布到UI线程。如果用户转换到新活动,则后台需要继续执行其任务,但仅向UI线程发送不同的消息,以便可以相应地更新新布局。
以下是我目前已经实现的内容...这是我的主要活动文件。
public class MyTCPTest extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // set the layout
        setContentView(R.layout.main);
        // create a handler to handle messages from bg thread
        Handler handler = new Handler();

        BgWorkerThread bgw = new BgWorkerThread();
        bgw.start();
 }

在另一个文件中,我这样定义了我的后台工作线程...
public class BgWorkerThread extends Thread {    
@Override
public void run(){

    while(true)
    {
        try {
        // simulate a delay (send request for data, receive and interpret response) 
        sleep(1000);

                    // How do I send data here to the UI thread?

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
} 

如果UI切换到另一个Activity,这个线程会继续运行吗?此外,这个线程如何知道要将消息发送给哪个Activity?显然,我希望它总是将数据发送到当前活动的Activity。那会自动发生吗?
最后,当UI切换到不同的activity时,需要通知bgworker,以便它可以开始请求与新Activity布局相关的数据。通知工作程序更改的最佳方法是什么?我不能只是在BgWorkerThread类中创建一个公共方法,以便在Activities加载时调用吗?
3个回答

3
我在下面的SO问题中提供了相同代码和更详细的步骤。总之,为了通知您的UI并给它不同的上下文,我建议采取以下措施:
  • 拥有一个将requestId映射到Handler的Map(假设您有request id的上下文)。您可以在Activity中注册适当的Handler,因此您可以使处理程序对每个Activity表现出不同的行为(例如,在从服务器接收到响应时更新各种UI元素)。
  • 更改线程模型以使用AsyncTask,因为它具有onProgressUpdate方法,使得从后台线程通知UI线程变得更加容易编码。
这是您的BackgroundThread的存根/伪代码:
public class ResponseHandler extends AsyncTask<Void, String, Integer> {
    boolean isConnectionClosed = false;
    Map<Integer, Handler> requestIdToMapHandler;

    public ResponseHandler() {
        this.requestIdToMapHandler = new HashMap<Integer, Handler>();
    }

    @Override
    protected Integer doInBackground(Void... params) {
        int errorCode = 0;

        try {
            // while not connection is not close
            while(!isConnectionClosed){
                // blocking call from the device/server
                String responseData = getResponse();

                // once you get the data, you publish the progress
                // this would be executed in the UI Thread
                publishProgress(responseData);
            }
        } catch(Exception e) {
            // error handling code that assigns appropriate error code
        }

        return errorCode;

    }

    @Override
    protected void onPostExecute(Integer errorCode) {
        // handle error on UI Thread
    }

    @Override
    protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);
        String responseData = values[0];

        // the response contains the requestId that we need to extract
        int requestId = extractId(responseData);

        // next use the requestId to get the appropriate handler
        Handler uiHandler = getUIHandler(requestId);

        // send the message with data, note that this is just the illustration
        // your data not necessary be jut String
        Message message = uiHandler.obtainMessage();
        message.obj = responseData;
        uiHandler.sendMessage(message);
    }

    /***
     * Stub code for illustration only
     * Get the handler from the Map of requestId map to a Handler that you register from the UI
     * @param requestId Request id that is mapped to a particular handler
     * @return
     */
    private Handler getUIHandler(int requestId) {
        return null;
    }

    /***
     * Stub code for illustration only, parse the response and get the request Id
     * @param responseId
     * @return
     */
    private int extractId(String responseId) {
        return 0;
    }

    /***
     * Stub code for illustration only
     * Call the server to get the TCP data. This is a blocking socket call that wait
     * for the server response
     * @return
     */
    private String getResponse() {
        return null;
    }
}

1

我觉得你正在寻找一个UI回调机制。我在这里回答了一个类似的问题:最佳方式在应用运行时定期执行操作——Handler?。正如你所看到的,我通过创建一个Handler实例并调用sendEmptyMessage()方法从后台线程向UI发送消息。你可以发送其他类型的消息,但这只是一个简单的例子。

希望能有所帮助。


Travis,感谢你提供的链接,但我仍然感到困惑。区别在于你所有的代码似乎都在同一个类中。我的后台工作程序是它自己的类,扩展了Thread。这是一个与Activity类不同的类,那么我如何访问该类/活动的处理程序? - PICyourBrain

1
您可以使用 <\p>。
runOnUIThread(new Runnable {

public void run() {
//Update your UI here like update text view or imageview etc

}

});

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