在扩展 AsyncTask 的类中使用 DoInBackground 方法更改非 MainActivity 的 Activity 中 TextView 的文本

Posted

技术标签:

【中文标题】在扩展 AsyncTask 的类中使用 DoInBackground 方法更改非 MainActivity 的 Activity 中 TextView 的文本【英文标题】:Use DoInBackground Method in a class extending AsyncTask to change Text of TextView in Activity that is not MainActivity 【发布时间】:2018-10-01 23:42:21 【问题描述】:

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

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

我有两个活动,我在第二个活动“StreamPageMain”的 onCreate 中执行类“ListenForMessage”(在此之前,我从 MainActivity 中执行它,但这也不起作用)。我试图让该方法知道 TextView 属于哪个活动,正如您在我的代码中看到的那样,我在“ListenForMessage”类中创建了另一个方法来设置文本,因此它不是尝试更改的 DoInBackground 方法文本,但这些都不起作用。

总结: 我已经在代码中标记了那个地方,那是错误的。我需要做的是以某种方式获取名为“消息”的字符串并将该字符串放入 ID 为“textView1”的 TextView 中,这是“StreamPageMain”活动的一部分,而不是 MainActivity。

当按下按钮时,我在 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);

第二个活动“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 类的。如果我注释掉标记的行,则类本身、连接和一切都可以正常工作:

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 还很陌生,我的知识并没有超出我在这里发布的内容。请原谅格式错误和非最佳实践,我很高兴学习和改进!

【问题讨论】:

setText 操作无法在后台线程上执行。在 Async 类或 runOnUiThread 中使用 ProgressUpdate。 @PeterSagichnit 你成功了吗? 【参考方案1】:

如您所见,您无法更改 doInBackground 中的 UI,因为其中的代码在另一个线程中执行,您只能在 UI 线程中更改 UI。 (不过,您可以将更改从另一个线程发布到 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&lt;Void, String, Void&gt;

【讨论】:

我在方法中复制了您的行,并将 onProgressUpdate 位放在 DoInBackground 下方,但它似乎不起作用。 Android Studio 将字符串的名称(消息)标记为红色,并希望我创建一个名为 publishProgress 的方法,而 onProgressUpdate 方法被标记为未使用......我想他们不知何故找不到彼此? 非常感谢您的快速回答!这似乎很合乎逻辑! 对不起,我忘了提,您的 Async Task 签名告诉 Android 您没有发布任何进度 AsyncTask,只需将其更改为 AsyncTask我认为它应该工作:) 就是这样!太棒了! 太棒了 :) 很高兴我能帮助你,别忘了标记为答案,这样它也可以帮助其他人:)

以上是关于在扩展 AsyncTask 的类中使用 DoInBackground 方法更改非 MainActivity 的 Activity 中 TextView 的文本的主要内容,如果未能解决你的问题,请参考以下文章

如何在 doInBackground 的 AsyncTask 中显示 toast

从 Activity 调用时,另一个 AsyncTask 类中的 ProgressDialog 未显示

我如何从AsyncTask中的数据库类中获取对象?

如何在 doInBackgroung() 时制作 android AsyncTask 加载微调器

如何在修改之前在 AsyncTask 中使用变量?

如何在已经扩展 Thread 的类中扩展 JFrame