简体   繁体   English

Android:Thread Runnable和Handler.post问题-Run方法中((Handler.postXX预期无法按时运行))

[英]Android: Thread Runnable and Handler.post issues - (Handler.postXX does not run on time when expected) within the Run method

I have a TCP Socket used as a TCP client which is used to read incoming data from a server constantly -- until the client or the server drops the connection. 我有一个TCP套接字用作TCP客户端,用于不断从服务器读取传入的数据-直到客户端或服务器断开连接。 I don't know of any other way but use a while (true) loop in a different Runnable thread and in it (in the while loop) check for incoming data. 我不知道有什么其他方法,但是可以在不同的Runnable线程中使用while(true)循环,并在其中(在while循环中)检查传入数据。 The received data needs to be printed in an EditText . 接收到的数据需要打印在EditText中

The problem I am having is updating the text from Handler.post(...) . 我遇到的问题是从Handler.post(...)更新文本。

I know that: 我知道:

  • In order to create TCP connections with Android 3.0 and above, I am required to place the TCP code either in a new Thread(...) or an AsyncTask because Strict mode has been enabled by default and I do not want to disable it. 为了使用Android 3.0及更高版本创建TCP连接,我需要将TCP代码放置在新的Thread(...)AsyncTask中,因为默认情况下已启用严格模式,而我不想禁用它。

  • I know that in order to update the UI from a new Thread in Android, I need to use Handler.post() if I use a Thread or the onProgressUpdate via publichProgress if I use AsyncTask and I know how to use all these but I am experiencing weird frustrating issues with both of them. 我知道为了从Android中的新线程更新UI,如果我使用Thread ,则需要使用Handler.post();如果使用AsyncTask ,则需要通过publichProgress来使用onProgressUpdate ,并且我知道如何使用所有这些,但是我他们两个都遇到了奇怪而令人沮丧的问题。

So, all I want to do is: 所以,我要做的就是:

  • Listen to the server permanently 永久监听服务器
  • As soon as the server sends me a message, example: 'w' or 'a' or n', I immidiately display it on the EditText. 一旦服务器向我发送消息,例如:“ w”或“ a”或n,我就会立即将其显示在EditText上。 You can think of it as a telnet session but I need "more" precision than telnet as I want to process every single byte, even non-printable ones so I do not want to use readLine in anyway. 你可以把它作为一个telnet会话,但我需要“更加”精确比远程登录,因为我要处理的每一个字节,甚至不可打印的人,所以我不想在这样用的readLine。 I must read a byte at a time OR get a buffer of bytes and then process them separately by iterating through the buffer. 必须一次读取一个字节,或者获取一个字节缓冲区,然后通过遍历该缓冲区分别处理它们。 I went with a byte at a time. 我一次只带了一个字节。

Here is the code I have and please pay attention to my comment above handle.response to see the problem I am having. 这是我的代码,请注意handle.response上方的注释,以查看我遇到的问题。 I hope you can clear this out. 希望您能解决这个问题。

The code is very briefly coded and I have removed a lot of the error checking sections for this sample. 该代码的编码非常简短,我删除了此示例的许多错误检查部分。

I have a new class called: ThreadClientInput: 我有一个名为:ThreadClientInput的新类:

public class ThreadClientInput  implements Runnable {

InputStream inputStream;
MainActivity mainActivity;
Handler handler = new Handler();
int NextByte = 0;

public ThreadClientInput(MainActivity ma)
{
    mainActivity = ma;
}

@Override
public void run() 
{ 
    ////////////////////////////////////////////////////////////////
    // Run the sensitive code that requires us to create this thread
    try {
        mainActivity.tcp_Client = new Socket("192.168.1.90", 23);
    }
    catch (Exception e){Log.e("EXEPTION:", e.getMessage().toString());return;}
    ////////////////////////////////////////////////////////////////


    // Only try to get the inputStream if we have a successful connection
    if (mainActivity.tcp_Client != null) 
    {
        try 
        {
            inputStream = mainActivity.tcp_Client.getInputStream();
        }
        catch (Exception e){Log.e("EXEPTION:", e.getMessage().toString()); return;}
    }


    /////////////////////////////////////////////
    /////////////////////////////////////////////
    // Update the text on the "Connect" button
    handler.post(new Runnable() 
    {
        @Override
        public void run() 
        { mainActivity.btn_Connect.setText("Connected");}
    });
    /////////////////////////////////////////////
    /////////////////////////////////////////////

    try 
    {   

        // I need to constantly read the data until we manually disconnect or
        // the server drops the connection
        while (true) 
        {
            // Get the next byte
            // I do not want to use "readline()" from a BufferedReader etc because I need to know exactly what's coming in
            // I need to process every single byte
            NextByte = inputStream.read();

            if (NextByte > -1)
            {
                Log.e("in (While loop):", Character.toString((char)NextByte));

                *** Here is the problem ****
                // Update the EditText and this is where the problem starts
                // if the server sends "1234", the Log.e() above will display everything correctly: "1234"
                // however the handler.post below will run at the end of the last byte so the
                // the EditText as well as the second Log.e below within the handle.post will display: "1444" or "4444" or "2444"
                // So the handler.post does *not* run immediately even if I try handle.postAtFrontOfQueue()
                // If the server sends "12345678", again, Log.e() above will display everything correctly: "12345678" 
                // however the handler.post below will run at the end of the last byte again
                // and I will get "88888888" (most of the time) or "18888888" 

                //   

                handler.post(new Runnable() 
                {
                    @Override
                    public void run() 
                    {
                        mainActivity.et_Response.setText(mainActivity.et_Response.getText() + Character.toString((char)NextByte));
                        Log.e("In handler.post:", Character.toString((char)NextByte));
                    }
                });
            }
        }           
    }
    catch (Exception e){Log.e("EXEPTION:", e.getMessage().toString());}
}}

I tried various variations including one with runOnUiThread and AsyncTask, with all I am getting same results. 我尝试了各种变体,包括使用runOnUiThread和AsyncTask的变体,所有这些我都得到了相同的结果。 I am out, I have nothing. 我出去了,我什么都没有。 At this point of time I am just reading some documentation about Handle.post method to see if I can make sense. 目前,我只是在阅读有关Handle.post方法的一些文档,以了解我是否有意义。

I hope you have a solution and I know that " while (true) " isn't a good practice but I can break the loop from outside the thread with setting a flag and I don't know of any other way how to do this. 我希望您有一个解决方案,并且我知道“ while(true) ”不是一个好习惯,但是我可以通过设置标志来中断线程外的循环,而我不知道其他方法。

I am not sure how you are able to access NextByte from public void run() without defining it as final !? 我不确定如何在不将其定义为final!的情况下如何从public void run()访问NextByte

  • If you want the handler to post message to the Activity UI thread, yo should create it within the activity class so it can access its Looper. 如果您希望处理程序将消息发布到Activity UI线程,则应活动类中创建该消息,以便它可以访问其Looper。

I can think of two solutions for your problem as follows: 我可以为您的问题想到以下两种解决方案:

1- To use a custom Runnable class where you pass to it the NextByte value as variable eg 1-要使用自定义的Runnable类,将NextByte值作为变量传递给它,例如

   handler.post(new MyRunnable(NextByte));

And MyRunnable can be something like: MyRunnable可能类似于:

  class MyRunnable implements Runnable{
   int byteData;  
   public MyRunnable(int byteData){
      this.byteData = byteData;
   }

     public void run(){ // update the EditText}
  }                          

2- To use handler.sendMessage(); 2-使用handler.sendMessage(); and add the NextByte as the message argument eg 并添加NextByte作为消息参数,例如

 Message msg = new Message();
 msg.arg1 = NextBye
 handler.sendMessage(msg);

And your handler should be defined as following: 并且您的处理程序应定义如下:

handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int nextByte = msg.arg1;
                            // update the EditText
        }
    };

Problem has been resolved. 问题已解决。

This is what I did this. 这就是我所做的。

In MainActivity.java which is the main file, in class MainActivity 在主文件MainActivity.java中的类MainActivity中

    public static class MyRunnable implements Runnable {
    int currentByte = 0;

    public MyRunnable(int b){
        currentByte = b;
    }

    @Override
    public void run() 
    {
        mainActivity.et_Response.setText(mainActivity.et_Response.getText() + Character.toString((char)currentByte));
    }
}

I have a statement mainActivity = this; 我有一条声明mainActivity = this; in onCreate and then in ThreadClientInput.run onCreate然后在ThreadClientInput.run中

        try {
        while (true)
        {
            NextByte = inputStream.read(); 

            // if the server drops the connection, break out the loop
            if (NextByte < 0) break;

            handler.post(new MainActivity.MyRunnable(NextByte));
        }
    }
    catch (Exception e) {
        Log.e("EXCEPTION", e.getMessage());
    }

After this, handler.post is getting called correctly and at the correct and expected time. 此后,将在正确的预期时间正确调用handler.post。 Full credit goes to iTech. 完全归功于iTech。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM