Return value from AsyncTask without get() method

2019-02-11 01:07发布

问题:

I'm trying to return value from my asynctask in DoInBackground, but calling get() method freezes my UI. How can I re-write my code to a callback method? :

public class GetUrlDataTask extends AsyncTask<String, Integer, String> {
String response;
HttpUtils util;
@Override
protected String doInBackground(String... params) {
    try {
        util = new HttpUtils(params[0]);
        response = util.getContent();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return response;
}

@Override
protected void onPostExecute(String result) {
    super.onPostExecute(result);
}

In my activity I get result as response = new GetUrlDataTask().execute("site").get;

回答1:

[UPDATE] Now i suggest to use annotations. With annotations you can inject views, make function as asyncronous process on the compilation step with just a few letters. Check out butterknife library on github.

ButterKnife

You can create interface, pass it to AsyncTask (in constructor), and then call method in onPostExecute

For example:

Your interface:

public interface OnTaskCompleted{
    void onTaskCompleted();
}

Your Activity:

public YourActivity implements OnTaskCompleted{
    //your Activity
}

And your AsyncTask:

public YourTask extends AsyncTask<Object,Object,Object>{ //change Object to required type
    private OnTaskCompleted listener;

    public YourTask(OnTaskCompleted listener){
        this.listener=listener;
    }

    //required methods

    protected void onPostExecute(Object o){
        //your stuff
        listener.onTaskCompleted();
    }
}


回答2:

You shouldn't use .get() if the Async task is going to take any decent amount of time (which it usually is).

Instead, you can either use a message/handler/service/etc, or you can simply use the onPostExecute(Result) method.

EDIT: New Code. Based on your description, it seems like you need to use an interface.

If you need to have Asynctask in another class, then an interface is probably your best option.

TestTask.java (your separate Asynctask):

import android.os.AsyncTask;

// Remember to change object type <> to what you need
public class TestTask extends AsyncTask<Object,Object,Object> {

    public interface OnTaskCompleted{
        void onTaskCompleted();
    }

    private OnTaskCompleted listener;

    public TestTask(OnTaskCompleted listener){
        this.listener = listener;
    }

    protected void onPostExecute(Object o){
        // Call the interface method
        if (listener != null)
            listener.onTaskCompleted();
    }

    @Override
    protected Object doInBackground(Object... params) {
        // The sleep() is just to simulate activity and delay
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

MainActivity.java (or any other activity):

public class MainActivity extends Activity {

private boolean status = false;

private OnTaskCompleted listener = new OnTaskCompleted() {
    public void onTaskCompleted() {
        status = true;
        Toast.makeText(MainActivity.this, "Status: " + status, Toast.LENGTH_SHORT).show();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toast.makeText(MainActivity.this, "Status: " + status, Toast.LENGTH_SHORT).show();
    new TestTask(listener).execute("Testing");
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

}


回答3:

I'm not a big fan of having AsycTask tasks in separate classes, especially if you need to use the response. It makes interacting with the response and local variables overly difficult considering how easy it is when implemented as an inner class.

I'm guessing you put it in its own class so you can reuse it. I would consider keeping the AsycTask as an inner class and calling outside reusable objects/methods in doInBackground(). This will keep the code DRY and allow your activity to do what it needs with the response.

public class MyActivity extends Activity {
    TextView textview;

    //...

    private class GetUrlTask extends AsyncTask<String, Integer, String> {

        protected String doInBackground(String... params) {
            return new GetHttpResponse().get(params[0]);
        }

        protected void onPostExecute(String response) {
            //Do UI updates...
            textview.setText(response);
        }
    }
}


public class GetHttpResponse {

    public String get(String url) {
        try {
            util = new HttpUtils(url);
            response = util.getContent();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    return response;
    }
}


回答4:

You could do something like this:

public class MyActivity extends Activity
{
  public void someMethod()
  {
    // Here you could put up a ProgressDialog

    GetUrlDataTask myTask = new GetUrlDataTask();
    myTask.execute();
  }

  public class GetUrlDataTask extends AsyncTask<String, Integer, String>
  {
    @Override
    protected String doInBackground(String... params)
    {
      String response = null;
      HttpUtils util;

      try
      {
        util = new HttpUtils(params[0]);
        response = util.getContent();
      }
      catch (Exception e)
      {
        e.printStackTrace();
        response = e.getMessage();
      }
      return response;
    }

    @Override
    protected void onPostExecute(String result)
    {
      // Here you can dismiss the ProgressDialog and display the result
    }
  }
}