简体   繁体   English

Android - 将数据从 AsyncTask 传递到 Activity

[英]Android - passing data from AsyncTask to Activity

I am trying to pass a string from AsyncTask back to my Activity.我正在尝试将 AsyncTask 中的字符串传递回我的活动。 Browsing through other similar questions (eg. here ), I decided to use an interface listener.浏览其他类似的问题(例如这里),我决定使用接口侦听器。

Interface:界面:

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);
    }
}

My 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;
    }

The JSON is properly get from server and is seen in onPostExecute normally, however, it isn't visible later on in WeatherDisplay (the Toast is displayed empty, variable is still a nullpointer). JSON 正确地从服务器获取并且在onPostExecute正常显示,但是,稍后在WeatherDisplay不可见(Toast 显示为空,变量仍然是空指针)。

How do I resolve this issue?我该如何解决这个问题? I am inexperienced and might've missed some trivial stuff.我没有经验,可能错过了一些琐碎的东西。

I think a little explanation about how Android runs your code might help.我认为有关 Android 如何运行您的代码的一些解释可能会有所帮助。

So in the background, Android is running some code in an endless loop.所以在后台,Android 正在无限循环中运行一些代码。 Part of that loop is to check a message queue, which is basically where tasks get delivered - code it needs to execute.该循环的一部分是检查消息队列,它基本上是任务被交付的地方——它需要执行的代码。 Ideally it will get all the work required in each loop finished quickly enough that it can manage 60 loops per second.理想情况下,它可以足够快地完成每个循环中所需的所有工作,每秒可以管理 60 个循环。 If it has too much to do, that's where it starts to slow down and feel janky.如果它有太多事情要做,那就是它开始放慢速度并感到笨拙的地方。

Some of the tasks that might get executed are things like an Activity being created, so it might want the system to run its onCreate code - which happens once for each Activity .一些可能被执行的任务是像正在创建的Activity类的事情,因此它可能希望系统运行其onCreate代码——这对每个Activity发生一次。 You've probably noticed it only happens once, and that's where you've put your endless loop, right?您可能已经注意到它只发生一次,这就是您放置无限循环的地方,对吗? To kind of trap the execution in there, until the AsyncTask delivers its result?要在那里捕获执行,直到AsyncTask提供其结果?

The problem is you're stopping the main loop from working - it can't move on and do anything until it's finished that task, and you're blocking it on purpose.问题是您正在阻止主循环工作 - 在完成该任务之前它无法继续并做任何事情,而您是故意阻止它。 That's very bad in general, and it's why you're encouraged not to do slow operations on that main thread (the one that runs the main looper) - it creates work that takes too long, and makes the whole UI less responsive (UI handling is also tasks that need to run)这通常非常糟糕,这就是为什么鼓励您不要在该主线程(运行主循环程序的线程)上执行缓慢操作的原因-它创建的工作需要太长时间,并使整个 UI 响应速度变慢(UI 处理)也是需要运行的任务)

So that's bad in general, but the way AsyncTask s actually work is they run on another thread (so they're not blocking the main one, it's like having another independent person working on stuff) - but they deliver the result on the main thread.所以总的来说这很糟糕,但AsyncTask的实际工作方式是它们在另一个线程上运行(所以它们不会阻塞主线程,就像让另一个独立的人处理事情一样) - 但它们在主线程上提供结果. They post a message to the main thread's message queue, telling it to run the onPostExecute code.他们向主线程的消息队列发布一条消息,告诉它运行onPostExecute代码。

But the system can't get to that message until it's handled the earlier tasks in the queue.但是系统在处理队列中较早的任务之前无法获取该消息。 And if it's busy running an endless loop waiting for the result of the AsyncTask , it will never get to that message, the variable will never be updated, and the loop will never see the change it's waiting for.如果它忙于运行一个无限循环等待AsyncTask的结果,它永远不会得到那个消息,变量永远不会被更新,循环永远不会看到它正在等待的变化。 It's like someone holding up a line, refusing to move until they see something that someone further down the line is trying to deliver就像有人举着一条线,拒绝移动,直到他们看到线下的某个人正在尝试提供的东西


That's a bunch of background, but the point is you shouldn't ever block a thread like that, unless it's a special thread you created so you know it's ok and it's not interfering with anything else.这是一堆背景,但关键是你永远不应该阻塞这样的线程,除非它是你创建的一个特殊线程,所以你知道它没问题,并且不会干扰其他任何东西。 Think of Android more as an event-driven system - you write code that should be executed when something happens, like when an Activity gets created ( ònCreate ) or when a button is pressed( onClick ) or when an AsyncTask completes ( onPostExecute ).将 Android 更像是一个事件驱动的系统 - 您编写的代码应该在发生某些事情时执行,例如创建 Activity ( ònCreate ) 或按下按钮时 ( onClick ) 或AsyncTask完成时 ( onPostExecute )。 There's a reason all these methods start with "on"!所有这些方法都以“on”开头是有原因的! "When a thing happens, do this..." “有事就做这个……”

So when your task completes, it runs onPostExecute and that's where all your code to handle receiving the result should go.因此,当您的任务完成时,它会运行onPostExecute ,这就是您处理接收结果的所有代码应该去的地方。 It doesn't literally need to be inside the onPostExecute method - like you've put yours inside a passJSONGet method to keep things organised, but that gets called from onPostExecute , that's the event that triggers the code being run.它实际上并不需要在onPostExecute方法中——就像你已经将你的方法放在passJSONGet方法中以保持组织有序,但它是从onPostExecute ,这是触发正在运行的代码的事件。

So whatever you need to do with the result, call it from onPostExecute .因此,无论您需要对结果做什么,都可以从onPostExecute调用它。 When that happens, it will do the stuff you've told it to do.当这种情况发生时,它会做你让它做的事情。 Update a variable, make a toast, populate views in your layout with some new data, whatever!更新变量、敬酒、用一些新数据填充布局中的视图,等等!

Change the code to :将代码更改为:

  @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();
    
}

Your flow is wrong.你的流量不对。 All UI work should be done after onPostExecute所有 UI 工作都应该在 onPostExecute 之后完成

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

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