Android - 将数据从 AsyncTask 传递到 Activity
Android - passing data from AsyncTask to Activity
我正在尝试将字符串从 AsyncTask 传回我的 Activity。浏览其他类似问题(例如here),我决定使用接口侦听器。
接口:
package com.example.myapplication;
public interface GetListener {
void passJSONGet(String s);
}
AsyncTask class:
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);
}
}
我的Activity:
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 显示为空,变量仍然是空指针)。
如何解决这个问题?我没有经验,可能漏掉了一些琐碎的东西。
我想稍微解释一下 Android 运行 您的代码可能会有帮助。
所以在后台,Android 正在 运行 无限循环中执行一些代码。该循环的一部分是检查消息队列,这基本上是任务交付的地方 - 它需要执行的代码。理想情况下,它会以足够快的速度完成每个循环中所需的所有工作,以至于每秒可以管理 60 个循环。如果它有太多事情要做,那就是它开始放慢速度并感觉笨拙的地方。
有些任务可能会被执行,比如正在创建 Activity
,因此它可能希望系统 运行 它的 onCreate
代码 - 每个任务都会发生一次Activity
。您可能已经注意到它只发生一次,那就是您放置无限循环的地方,对吧?是为了让执行陷入困境,直到 AsyncTask
交付结果?
问题是您正在阻止主循环工作 - 在完成该任务之前它无法继续执行任何操作,而您是故意阻止它。这通常是非常糟糕的,这就是为什么我们鼓励您不要在该主线程(运行 是主循环程序的主线程)上执行缓慢的操作 - 它创建的工作花费的时间太长,并使整个 UI反应迟钝(UI处理也是需要运行的任务)
所以总的来说这很糟糕,但是 AsyncTask
s 实际工作的方式是它们 运行 在另一个线程上(所以它们不会阻塞主线程,就像让另一个独立的人工作一样关于东西) - 但他们在主线程上传递结果。他们 post 一条消息到主线程的消息队列,告诉它 运行 onPostExecute
代码。
但是系统在处理队列中较早的任务之前无法获取该消息。如果它正忙于 运行 无限循环等待 AsyncTask
的结果,它永远不会收到该消息,变量永远不会更新,循环永远不会看到它正在等待的变化为了。这就像有人举着一条线,拒绝移动,直到他们看到线下有人试图交付的东西
这是一堆背景知识,但要点是你不应该像那样阻塞线程,除非它是你创建的特殊线程,所以你知道它没问题并且不会干扰其他任何东西。将 Android 更多地视为一个 event-driven 系统 - 您编写的代码应在某些事情发生时执行,例如创建 Activity 时(ònCreate
)或按钮被创建时按下(onClick
)或当 AsyncTask
完成时(onPostExecute
)。所有这些方法都以“on”开头是有原因的! “当事情发生时,这样做......”
因此,当您的任务完成时,它 运行s onPostExecute
这就是您处理接收结果的所有代码应该去的地方。它实际上不需要在 onPostExecute
方法内——就像您将自己的方法放在 passJSONGet
方法内以保持事情井井有条,但它是从 onPostExecute
调用的,这就是事件触发代码 运行.
因此,无论您需要对结果做什么,都可以从 onPostExecute
调用它。当发生这种情况时,它会执行您告诉它要做的事情。更新变量、敬酒、用一些新数据填充布局中的视图等等!
将代码更改为:
@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
之后完成
我正在尝试将字符串从 AsyncTask 传回我的 Activity。浏览其他类似问题(例如here),我决定使用接口侦听器。
接口:
package com.example.myapplication;
public interface GetListener {
void passJSONGet(String s);
}
AsyncTask class:
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);
}
}
我的Activity:
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 显示为空,变量仍然是空指针)。
如何解决这个问题?我没有经验,可能漏掉了一些琐碎的东西。
我想稍微解释一下 Android 运行 您的代码可能会有帮助。
所以在后台,Android 正在 运行 无限循环中执行一些代码。该循环的一部分是检查消息队列,这基本上是任务交付的地方 - 它需要执行的代码。理想情况下,它会以足够快的速度完成每个循环中所需的所有工作,以至于每秒可以管理 60 个循环。如果它有太多事情要做,那就是它开始放慢速度并感觉笨拙的地方。
有些任务可能会被执行,比如正在创建 Activity
,因此它可能希望系统 运行 它的 onCreate
代码 - 每个任务都会发生一次Activity
。您可能已经注意到它只发生一次,那就是您放置无限循环的地方,对吧?是为了让执行陷入困境,直到 AsyncTask
交付结果?
问题是您正在阻止主循环工作 - 在完成该任务之前它无法继续执行任何操作,而您是故意阻止它。这通常是非常糟糕的,这就是为什么我们鼓励您不要在该主线程(运行 是主循环程序的主线程)上执行缓慢的操作 - 它创建的工作花费的时间太长,并使整个 UI反应迟钝(UI处理也是需要运行的任务)
所以总的来说这很糟糕,但是 AsyncTask
s 实际工作的方式是它们 运行 在另一个线程上(所以它们不会阻塞主线程,就像让另一个独立的人工作一样关于东西) - 但他们在主线程上传递结果。他们 post 一条消息到主线程的消息队列,告诉它 运行 onPostExecute
代码。
但是系统在处理队列中较早的任务之前无法获取该消息。如果它正忙于 运行 无限循环等待 AsyncTask
的结果,它永远不会收到该消息,变量永远不会更新,循环永远不会看到它正在等待的变化为了。这就像有人举着一条线,拒绝移动,直到他们看到线下有人试图交付的东西
这是一堆背景知识,但要点是你不应该像那样阻塞线程,除非它是你创建的特殊线程,所以你知道它没问题并且不会干扰其他任何东西。将 Android 更多地视为一个 event-driven 系统 - 您编写的代码应在某些事情发生时执行,例如创建 Activity 时(ònCreate
)或按钮被创建时按下(onClick
)或当 AsyncTask
完成时(onPostExecute
)。所有这些方法都以“on”开头是有原因的! “当事情发生时,这样做......”
因此,当您的任务完成时,它 运行s onPostExecute
这就是您处理接收结果的所有代码应该去的地方。它实际上不需要在 onPostExecute
方法内——就像您将自己的方法放在 passJSONGet
方法内以保持事情井井有条,但它是从 onPostExecute
调用的,这就是事件触发代码 运行.
因此,无论您需要对结果做什么,都可以从 onPostExecute
调用它。当发生这种情况时,它会执行您告诉它要做的事情。更新变量、敬酒、用一些新数据填充布局中的视图等等!
将代码更改为:
@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
之后完成