【问题标题】:doInBackground not updating variabledoInBackground 不更新变量
【发布时间】:2017-11-30 14:06:48
【问题描述】:

我正在开发一个基本的 android 应用程序,它使用 HttpURLConnection 制作 POST。我想从我的 Web API 返回响应消息。

我的 MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView mTextView = findViewById(R.id.textView);
        AsyncExample asyncExample = new AsyncExample();
        asyncExample.execute();
        mTextView.setText(asyncExample.getResponseMsg());
    }
}

我的 AsyncExample.java

class AsyncExample extends AsyncTask<Void, Void, Void> {
    private HttpURLConnection con;
    private String responseMsg;

    protected void onPreExecute() {
        responseMsg = "empty message";
    }

    @Override
    protected Void doInBackground(Void... params) {
        String urlParameters = "param1=data1";

        byte[] postData = urlParameters.getBytes(Charset.forName("UTF-8"));
        int postDataLength = postData.length;
        String request = "http://192.168.1.30:6262";
        URL url = null;
        try {
            url = new URL(request);
            con = (HttpURLConnection) url.openConnection();
            con.setDoOutput(true);
            con.setInstanceFollowRedirects(false);
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            con.setRequestProperty("charset", "utf-8");
            con.setRequestProperty("Content-Length", Integer.toString(postDataLength));
            responseMsg = con.getResponseMessage();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getResponseMsg() {
        return responseMsg;
    }
}

运行应用程序后,我在TextView 中获得了empty message。为什么我的doInBackground 没有更新?就算con.getResponseMessage()null也要更新?

【问题讨论】:

  • 您应该从 doinbackground 方法返回响应并在 onPostexecute 中存储该字符串响应,现在您将能够访问该字符串响应
  • 又一个...IT IS MULITHREADING

标签: java android android-asynctask httpurlconnection


【解决方案1】:

问题是您的AsyncTask 是异步执行的,而您尝试立即检索该值。您需要稍微不同地实现这一点。要么利用 AsyncTask 的 API,因为它在 UI 线程上为你发布回调。您可以直接在您的AsyncTask 中更新您的TextView

class MyAwesomeAsyncTask extends AsyncTask<Void, Void, String> {

    @Override
    protected void onPreExecute() {
        myTextView.setText("progress started");
    }

    @Override
    protected String doInBackground(final Void... voids) {
        String s = amazingCallToBackend();
        return s;
    }

    @Override
    protected void onPostExecute(final String s) {
        myTextView.setText(s);
    }
}

或者,如果您只是想要该值,您可以将回调传递给您的异步任务,该任务会将值传递给您,类似这样

interface Callback {
    void onValueReceived(String value);
    void onFailure();
}

class MyAwesomeAsyncTask extends AsyncTask<Void, Void, String> {

    private Callback callback;

    MyAwesomeAsyncTask(final Callback callback) {
        this.callback = callback;
    }

    @Override
    protected String doInBackground(final Void... voids) {
        String s = amazingCallToBackend();
        return s;
    }

    @Override
    protected void onPostExecute(final String s) {
        callback.onValueReceived(s);
    }
}

这是你如何创建它的

Callback callback = new Callback() {
        @Override
        public void onValueReceived(final String value) {

        }

        @Override
        public void onFailure() {

        }
    };

new MyAwesomeAsyncTask(callback).execute();

但是,请小心,因为如果由于某种原因您的活动/片段在 AsyncTask 完成之前已经消失/完成,这可能会导致内存泄漏。

快速的 Google 搜索将告诉您所有关于 AsyncTask 泄漏内存的信息 :)

【讨论】:

  • 我要试试第一种方法。我在哪里定义myTextView
  • 您的 textView 是您的活动中的一个字段,而您的 AsyncTask 是同一活动中的一个内部类。或者您可以像我在第二个示例中传递回调一样传递您的 TextView。
  • 如何使用Callback 接口创建新的MyAwesomeAsyncTask
  • 编辑了我的答案,现在看看
  • 谢谢先生!现在我必须向我的 API 发出正确的请求!
【解决方案2】:

您的 doInBackground 方法需要时间来执行。您正在立即致电mTextView.setText(asyncExample.getResponseMsg());,但asynctask 尚未完成。你需要等到你的 doInBackground 完成,然后调用 setText 你可以在 onPostExecute 方法中做到这一点。

【讨论】:

  • 好的,我知道当我尝试访问我的字符串时,我的 HTTP 请求没有完成。如果我想在 OnPostExecute 中返回一个值而不是更新 TextView 怎么办?
  • 你不能从onPostExecute返回值。我能想到的一种方法是使用CountDownLatch ,但这有点矫枉过正。处理 AsyncTask 是一团糟。 RxJava 解决了这个问题。
【解决方案3】:

AsyncTask 有 3 个默认方法 1. 预执行 2.在后台做 3. 后期执行

执行后:

从 doinbackground 得到的响应是在 post execute 中。 在这里我们可以处理结果。使用runnable方法更新文本视图ui

【讨论】:

    【解决方案4】:

    重写onPostExecute() 方法以返回文本。在 Main Activity 中创建一个方法以在完成异步任务的执行后更新 TextView。它是空白的,因为Main Thread 没有暂停其执行和设置文本视图,但异步任务尚未完成执行,因此字符串为空。所以在设置文本视图之前等待异步任务完成。

    String str_result= new RunInBackGround().execute().get();
    

    Refer to this for more information.

    【讨论】:

    • 所以默认情况下get方法会找到onPostExecute的返回值?我无法让onPostExecute 返回String,它说我无法覆盖该方法
    • 您当然不能从 PostExecute 返回字符串,您可以将 Async Task 类设为您拥有 MainActivity 的类的内部类,然后调用外部类的方法从 Inner Aysnc Task 类更新 textView。如果您需要帮助,可以参考此代码。 github.com/Pronoy999/NewsApp/blob/master/app/src/main/java/com/…
    【解决方案5】:

    您可以通过多种方式做到这一点。我会尝试向您建议一种对现有代码进行少量更改的方法。

    将 mTextView 声明为全局变量,覆盖 AsyncExample 中的 onPostExecute() 方法,并使用 doInBackground() 方法传递给它的值更新该 onPostExecute() 方法中的 mTextView [这里,注意 responseMsg 在由 onPostExecute() 方法捕获为字符串值(结果)的 doInBackground() 地面的结尾]。但是,我也认为重写 onPreExecute() 方法是个好主意。

    为此,您的 MainActivity.java 应如下所示:

    public class MainActivity extends AppCompatActivity {
    TextView mTextView;    //declare mTextView outside the onCreate() method as a Global String variable.
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.textView);
            AsyncExample asyncExample = new AsyncExample();
            asyncExample.execute();
        }
    }
    

    请将您的 asynctask 设为同一活动的内部类,并按如下方式进行编辑:

        class AsyncExample extends AsyncTask<Void, Void, Void> {
        private HttpURLConnection con;
        private String responseMsg;
    
        @Override           // Its a good practice to Override the onPreExecute() method.
        protected void onPreExecute() {
            responseMsg = "empty message";
        }
    
        @Override
        protected String doInBackground(String... params) {
            String urlParameters = "param1=data1";
    
            byte[] postData = urlParameters.getBytes(Charset.forName("UTF-8"));
            int postDataLength = postData.length;
            String request = "http://192.168.1.30:6262";
            URL url = null;
            try {
                url = new URL(request);
                con = (HttpURLConnection) url.openConnection();
                con.setDoOutput(true);
                con.setInstanceFollowRedirects(false);
                con.setRequestMethod("POST");
                con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                con.setRequestProperty("charset", "utf-8");
                con.setRequestProperty("Content-Length", Integer.toString(postDataLength));
                responseMsg = con.getResponseMessage();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return responseMsg;    //return the value of responseMsg
        }
    
            @Override      //override onPostExecute() method
            protected void onPostExecute(String result) {    //receive the value to be set to mTextView which is returned by the doInBackground() method.
               mTextView.setText(result);
        }
    
    }
    

    【讨论】:

      【解决方案6】:

      尝试这样做

      主活动

      public class MainActivity extends AppCompatActivity {
      TextView mTextView;
      
        @Override
        protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          mTextView = findViewById(R.id.textView);
          AsyncExample asyncExample = new AsyncExample(this,mTextView);
          asyncExample.execute();
        }
      }
      

      异步任务

          class AsyncExample extends AsyncTask<Void, Void, Void> {
          private HttpURLConnection con;
          private String responseMsg;
          private MainActivity mContext;
          TextView mTextView;
      
          public AsyncExample (MainActivity context, TextView textView) {
              mContext = context;
              mTextView = textView;
          } 
      
          protected void onPreExecute() {
              responseMsg = "empty message";
          }
      
          @RequiresApi(api = Build.VERSION_CODES.KITKAT)
          @Override
          protected Void doInBackground(Void... params) {
              String urlParameters = "param1=data1";
      
              byte[] postData = urlParameters.getBytes(StandardCharsets.UTF_8);
              int postDataLength = postData.length;
              String request = "http://192.168.1.30:6262";
              URL url = null;
              try {
                  url = new URL(request);
                  con = (HttpURLConnection) url.openConnection();
                  con.setDoOutput(true);
                  con.setInstanceFollowRedirects(false);
                  con.setRequestMethod("POST");
                  con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                  con.setRequestProperty("charset", "utf-8");
                  con.setRequestProperty("Content-Length", Integer.toString(postDataLength));
                  responseMsg = con.getResponseMessage();
                  mContext.runOnUiThread(new Runnable() {
                      @Override
                      public void run() {
                          mTextView.setText(responseMsg);
                      }
                  });
              } catch (MalformedURLException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return null;
          }
      
          public String getResponseMsg() {
              return responseMsg;
          }
      }
      

      【讨论】:

      • 立即试用代码。我刚刚将 Activity 的上下文传递给您的 AsyncTask,以便我们可以获取 runOnUiThread 方法。
      • runOnUiThreadmTextView 的错误仍然相同
      • 以与传递上下文相同的方式传递 TextView。
      • 立即尝试。希望你能得到你想要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-20
      • 1970-01-01
      • 2017-04-09
      相关资源
      最近更新 更多