【问题标题】:Android: proper way to pass a message from background thread to UI thread?Android:将消息从后台线程传递到 UI 线程的正确方法?
【发布时间】:2013-03-27 05:37:14
【问题描述】:

我已经阅读了很多关于线程、处理程序、looper 等的内容,但我很困惑。在我的应用程序中,我想让第一个 Activity 启动一个后台工作人员。这个后台工作人员将不断地从 TCP 套接字请求数据,并(希望)在数据到达时将新信息发布到 UI 线程。如果用户转换到一个新的 Activity,后台需要继续做这件事,但只向 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。这会自动发生吗?

最后,当 UI 切换到不同的 Activity 时,需要通知 bgworker 以便它可以开始请求与新 Activity 布局相关的数据。将变更通知员工的最佳方式是什么?难道我不能在 BgWorkerThread 类中创建一个可以在加载活动时调用的公共方法吗?

【问题讨论】:

    标签: android multithreading


    【解决方案1】:

    我在下面的SO Question 中说明了相同的代码和更详细的步骤。总而言之,为了通知您的 UI 并为其提供不同的上下文,我建议如下:

    • 有一个将 requestId 映射到 Handler 的映射(假设您有请求 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;
        }
    }
    

    【讨论】:

      【解决方案2】:

      我认为您正在寻找一种 UI 回调机制。我在这里回答了一个类似的问题:Best way to perform an action periodically [while an app is running] - Handler?。如您所见,我通过创建Handler 实例然后调用sendEmptyMessage() 从后台线程向UI 发送消息。您可以发送其他类型的消息,但这是一个简单的示例。

      希望对您有所帮助。

      【讨论】:

      • 特拉维斯感谢您的链接,但我仍然感到困惑。不同之处在于您的所有代码似乎都在同一个类中。我的后台工作者是扩展 Thread 的自己的类。这是一个独立于 Activity 类的类,那么我如何访问该类/活动的处理程序?
      【解决方案3】:

      你可以使用

      runOnUIThread(new Runnable {
      
      public void run() {
      //Update your UI here like update text view or imageview etc
      
      }
      
      });
      

      【讨论】:

        猜你喜欢
        • 2021-03-08
        • 1970-01-01
        • 2011-06-09
        • 2019-12-22
        • 1970-01-01
        • 1970-01-01
        • 2023-03-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多