在 class 扩展 AsyncTask 中使用 DoInBackground 方法来更改 Activity 中不是 MainActivity 中的 TextView 的文本

Use DoInBackground Method in a class extending AsyncTask to change Text of TextView in Activity that is not MainActivity

我是一名大学生,正在编写一个 android 应用程序来显示消息,android 设备通过 TCP 从服务器接收消息。

classListenForMessage(扩展 AsyncTask)在 DoInBackground 方法中打开 TCP 连接并从服务器接收消息。我可以在日志中打印出来自服务器的消息,但是如果我尝试编辑 TextView 的文本,我的应用程序就会崩溃。看来我不能从 DoInBackground 方法编辑 TextView 的文本,尤其不能,因为 class 没有扩展创建 TextView 的 activity。

我有两个活动,我正在第二个 activity "StreamPageMain" 的 onCreate 中执行 class "ListenForMessage"(在此之前,我从 Main 中执行它Activity,但这也不起作用)。我试图让方法知道 TextView 属于哪个 activity,正如您在我的代码中看到的那样,我在 class "ListenForMessage" 中创建了另一个方法来设置文本,所以尝试更改文本的不是 DoInBackground 方法,而是 none 方法起作用了。

总结: 我已经在代码中标记了错误的地方。我需要做的是以某种方式获取名为 "message" 的字符串,并将该字符串放入 ID 为 "textView1" 的 TextView 中,它是 "StreamPageMain" activity 的一部分,而不是 Main Activity.

当按下按钮时,我在 MainActivity 中使用此方法开始:

 public void stream(View V) {

    EditText IPText = (EditText) findViewById(R.id.editTextBox2);  //Extracting the IP from an EditTextView
    EditText portText = (EditText) findViewById(R.id.editTextBox3); //Extracting the port from an EditTextView

    String IP = IPText.getEditableText().toString();            //correcting the format to String
    Integer port = Integer.parseInt(portText.getEditableText().toString()); //correcting the format to Integer

    Intent i = new Intent(MainActivity.this, StreamPageMain.class);
    i.putExtra("IP", IP);
    i.putExtra("port", port);
    startActivity(i);
}

第二个Activity"StreamPageMain"启动。这样可行。

import android.widget.VideoView;   

public class StreamPageMain extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.stream_page);

    Log.d("StreamingApp", "OnCreate is running.");

    //In this middle part I am implementing a videostream into the second 
    activity, but that does not affect anything        

    String IP = getIntent().getStringExtra("IP");
    Integer port = getIntent().getExtras().getInt("port");
    ListenForMessage listenForMessage = new ListenForMessage(StreamPageMain.this, IP, port);
    listenForMessage.execute();
}
}

您可以看到,我是如何将 Activity StreamPageMain 传递给 ListenForMessage class 的。如果我注释掉标记的行,class 本身、连接和一切正常:

package comn.example.ezio.streamingapp;

import android.app.Activity;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class ListenForMessage extends AsyncTask<Void, Void, Void> {

public Activity executingActivity;
public String IP;
public Integer port;

public ListenForMessage(Activity activity, String IPString, Integer portNumber) {
    this.executingActivity = activity;
    IP = IPString;
    port = portNumber;    
}

@Override
protected Void doInBackground(Void... voids) {

    try (Socket socket = new Socket(IP, port)) {

        Boolean connectionCancelled = false;

        while (connectionCancelled == false) {

            InputStreamReader isr = new InputStreamReader(socket.getInputStream());
            BufferedReader br = new BufferedReader(isr);
            String message = br.readLine();
            Log.d("StreamingApp", "Server says: " + message);

            setTextOfTextView(message); //!!!! This is it. At this line it 
                                        //crashes, no matter how I put it. 
                                        //Doing it directly here or in an 
                                        //extra method like now.
        }

    } catch (
            UnknownHostException e)

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

    {
        e.printStackTrace();
    }
    return null;
}

public void setTextOfTextView(String textOfTextView) {
    TextView textView1 = executingActivity.findViewById(R.id.textView1);
    textView1.setText("Server says: " + textOfTextView);
}
}

我毕竟是 Java 的新手,我的知识并没有超出我在这里发布的内容。请原谅格式错误和非最佳实践,我很乐意学习和改进!

如您所见,您无法更改 doInBackground 中的 UI,因为其中的代码在另一个线程中执行,您只能更改 UI在 UI 线程中。 (不过,您可以 post 从另一个线程更改到 UI 线程)。

要更新 AsyncTasks 中的 UI,您可以使用 onProgressUpdate 更新 UI,因为此处的代码在 UI 线程上运行。您在 AsyncTask:

中覆盖此方法
protected void onProgressUpdate(String... progress) {
         setTextOfTextView(progress[0]);
     }

要将更新从 doInBackground 发送到 onProgressUpdate,您可以调用方法 publishProgress()

@Override
protected Void doInBackground(Void... voids) {

    try (Socket socket = new Socket(IP, port)) {

        Boolean connectionCancelled = false;

        while (connectionCancelled == false) {

            InputStreamReader isr = new InputStreamReader(socket.getInputStream());
            BufferedReader br = new BufferedReader(isr);
            String message = br.readLine();
            Log.d("StreamingApp", "Server says: " + message);

            publishProgress(message); //!!!! This is it. At this line it 
                                        //crashes, no matter how I put it. 
                                        //Doing it directly here or in an 
                                        //extra method like now.
        }

    } catch (
            UnknownHostException e)

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

    {
        e.printStackTrace();
    }
    return null;
}

编辑:

此外,为了发布任何结果,您应该将 AsyncTask 签名更改为 AsyncTask<Void, String, Void>