Android - 将数据从 AsyncTask 传递到 Activity
Posted
技术标签:
【中文标题】Android - 将数据从 AsyncTask 传递到 Activity【英文标题】:Android - passing data from AsyncTask to Activity 【发布时间】:2020-12-12 14:13:07 【问题描述】:我正在尝试将一个字符串从 AsyncTask 传递回我的 Activity。浏览了其他类似的问题(例如here),我决定使用接口监听器。
界面:
package com.example.myapplication;
public interface GetListener
void passJSONGet(String s);
AsyncTask 类:
package com.example.myapplication;
import android.content.Context;
import android.os.AsyncTask;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
public class DataGetter extends AsyncTask<String, Void, String>
Context context;
private GetListener GetListener;
public String jsonString;
DataGetter(Context ctx, GetListener listener)
this.context = ctx;
this.GetListener = listener;
@Override
protected String doInBackground(String... params)
String ip = params[0];
String scriptname = params[1];
String db = params[2];
String urladress = "http://" + ip + "/"+ scriptname +".php";
try
URL url = new URL(urladress);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
OutputStream os = connection.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
String data = URLEncoder.encode("database", "UTF-8") + "=" + URLEncoder.encode(db, "UTF-8");
writer.write(data);
writer.flush();
writer.close();
BufferedReader reader = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
sb.append(line);
break;
return sb.toString();
catch (Exception e)
return e.getMessage();
@Override
protected void onPostExecute(String result)
GetListener.passJSONGet(result);
我的活动:
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONArray;
import org.json.JSONException;
public class WeatherDisplay extends AppCompatActivity implements GetListener
private JSONArray jsonArray;
private String jsonString;
@Override
protected void onCreate(Bundle savedInstanceState)
// other code
DataGetter dataGetter = new DataGetter(this, WeatherDisplay.this);
dataGetter.execute(ip, "readLatestOutside", "weather");
Toast.makeText(this, this.jsonString, Toast.LENGTH_LONG).show();
this.jsonArray = new JSONArray(this.jsonString);
// furter code
@Override
public void passJSONGet(String jsonstring)
this.jsonString = jsonstring;
JSON 是从服务器正确获取的,通常可以在 onPostExecute
中看到,但是稍后在 WeatherDisplay
中不可见(Toast 显示为空,变量仍然是空指针)。
我该如何解决这个问题?我缺乏经验,可能错过了一些琐碎的事情。
【问题讨论】:
您的onCreate
代码创建AsyncTask
,将其设置为在后台运行,然后立即跳转到下一行并显示Toast
- 您还没有准备好!稍后任务完成,运行onPostExecute
,这基本上最终将您的变量设置在Activity
中,仅此而已。您应该在 passJSONGet
中创建 toast,因为那是您实际接收要显示的信息的地方
以防万一您不知道,代码通常会逐行执行,如果您要获取一些数据,则必须等待结果返回才能继续(它会阻塞线程)。这就是所谓的同步执行,这一切都是一起发生的。相反的是异步执行,它通常被称为async,基本上是“东西可以在不同的时间运行和完成”,所以你可以继续做其他事情。 AsyncTask
s 是一种在后台做一些事情的方法 - 你将它们关闭,然后它们会返回结果并做任何事情
@cactustictacs 我附加了passJSONGet
以在WeatherDisplay
活动中设置一个布尔标志,在该标志上的“无所事事”循环使应用程序崩溃。我怎么能等待AsyncTask
返回字符串,以便我可以将其转换为JSON array
(根据代码,暂时忽略Toast
)?
好的,我添加了一个带有大量背景的答案,以帮助您了解一般情况,但您根本不应该等待结果 - 您的代码旨在对 @ 做出反应987654339@ 完成,通过运行onPostExecute
中的任何内容。这就是你对结果做任何你需要做的事情的地方。看看,希望对你有帮助!
【参考方案1】:
我认为稍微解释一下 Android 如何运行您的代码可能会有所帮助。
所以在后台,Android 正在无限循环中运行一些代码。该循环的一部分是检查消息队列,这基本上是任务交付的地方 - 它需要执行的代码。理想情况下,它会以足够快的速度完成每个循环所需的所有工作,使其每秒可以管理 60 个循环。如果它有太多事情要做,那就是它开始放慢速度并感到笨拙的地方。
一些可能会被执行的任务是创建一个Activity
,所以它可能希望系统运行它的onCreate
代码——每个Activity
发生一次。您可能已经注意到它只发生一次,这就是您放置无限循环的地方,对吗?在AsyncTask
提供结果之前将执行困在其中?
问题是您正在阻止主循环工作 - 在完成该任务之前它无法继续执行任何操作,并且您故意阻止它。这通常是非常糟糕的,这就是为什么我们鼓励你不要在那个主线程(运行主循环器的那个)上执行缓慢的操作 - 它创建的工作需要太长时间,并且会使整个 UI 的响应性降低(UI 处理也是需要运行的任务)
所以总的来说这很糟糕,但AsyncTask
s 的实际工作方式是它们在另一个线程上运行(所以它们不会阻塞主线程,就像让另一个独立的人在处理东西一样) - 但它们提供了结果在主线程上。他们向主线程的消息队列发布一条消息,告诉它运行onPostExecute
代码。
但系统在处理队列中较早的任务之前无法获取该消息。如果它正忙于运行一个无限循环等待AsyncTask
的结果,它永远不会得到那个消息,变量永远不会被更新,循环也永远不会看到它正在等待的变化。这就像有人举着一条线,直到他们看到更远的人试图交付的东西时才移动。
这是一堆背景知识,但关键是你不应该阻塞这样的线程,除非它是你创建的特殊线程,所以你知道它没问题并且不会干扰其他任何东西。将 Android 更多地视为事件驱动的系统 - 您编写的代码应该在发生某些事情时执行,例如创建 Activity 时 (ònCreate
) 或按下按钮时 (onClick
) 或 AsyncTask
完成 (onPostExecute
)。所有这些方法都以“on”开头是有原因的! “当事情发生时,这样做......”
因此,当您的任务完成时,它会运行onPostExecute
,这就是您处理接收结果的所有代码应该去的地方。它实际上并不需要在 onPostExecute
方法中 - 就像你已经将你的放在 passJSONGet
方法中以保持事物井井有条,但它是从 onPostExecute
调用的,这是触发正在运行的代码的事件.
因此,无论您需要对结果做什么,都可以从onPostExecute
调用它。当这种情况发生时,它会做你告诉它做的事情。更新变量,祝酒,用一些新数据填充布局中的视图,等等!
【讨论】:
【参考方案2】:将代码改为:
@Override
protected void onCreate(Bundle savedInstanceState)
// other code
DataGetter dataGetter = new DataGetter(this, WeatherDisplay.this);
dataGetter.execute(ip, "readLatestOutside", "weather");
// furter code
@Override
public void passJSONGet(String jsonstring)
this.jsonString = jsonstring;
this.jsonArray = new JSONArray(this.jsonString);
Toast.makeText(this, this.jsonString, Toast.LENGTH_LONG).show();
你的流程是错误的。所有 UI 工作都应该在 onPostExecute 之后完成
【讨论】:
以上是关于Android - 将数据从 AsyncTask 传递到 Activity的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 中,如何从 AsyncTask 更改 TextView? [复制]
Android - AsyncTask 之后的 Intent 数据