简体   繁体   中英

Android Async tasks reuse

I am developing an app where in a number of the activities I need to retrieve data via Http. Once I have the data I process it in the onPostExecute() callback method.

This works fine if I define the async task as an inline class, but as I want to do the same processing in a number of activities I have defined it as an external class.

So the question is, using an external class how do I signal an "event" back to the calling class as a means of passing the data back. I know how to do this in C# but I am new to Java and can not see how to achieve this.

While it is true that a Listener technically correct, I would claim that it is either too complicated or not complicated enough.

Here's an easier solution:

class Foo {
   class Bar extends AsyncTask<Void, Void, Result> {

      public void onPostExecute(Result res) {
         doSomething(res);
      }
   }

   public void doSomething(Result res) {
      /// process result
   }

   public void startTask() {
       new Bar().execute();
   }
}

Is equivalent to:

class Bar extends AsyncTask<Void, Void, Result> {
     private final Foo foo;
     public Bar(Foo foo) { this.foo = foo; }

     public void onPostExecute(Result res) {
       foo.doSomething(res);
     }
}


class Foo {
   public void doSomething(Result res) {
      /// process result
   }

   public void startTask() {
       new Bar(this).execute();
   }
}

... which is what you asked: when you pull the inner class out, you lose the implicit pointer. Just make it explicit and pass it in.

The bad news is that there are all sorts of memory leak and lifecycle issues that arise from this solution. I would very much recommend that, before your program gets even bigger, you looking into using an IntentService instead of an AsyncTask and use a Handler, or Activity.runOnUiThread to get the results back onto the UI thread.

One approach:

Define a parent abstract class called ParentActivity (extend Activity of course). Have that contain an abstract method called onAsyncCallback();

Have each class that uses the task to extend that class and implement the method.

In your AsyncTask constructor, have it accept in a ParentActivity .

Eg

ParentActivity activity;
public MyTask (ParentActivity myActivity)
{
 this.activity = myActivity;
}

When done in onPostExecute() , simply do

activity.onAsyncCallback(data);

This would also work with an interface, it does the same thing, except intead you have the Constructor accept in the Listener instance.

If you want to do it in a clean way try following approach

First create an enum which contains all your async call names

public enum TaskType {
    USER_LOGIN(1), GET_PRODUCTS(2), GET_EMPLOYEE(3);

    int value;

    private TaskType(int value) {
        this.value = value;
    }
}

Then create an interface

public interface AsyncTaskListener {
    public void onTaskCompleted(String result, TaskType taskType);
}

Now implement this interface in the activity which you are going to call the GetAsyncTask eg:-

 public class LoginActivity extends Activity implements AsyncTaskListener {

 protected void onCreate(Bundle savedInstanceState) {
     String url = ".....";
     new GetAsyncTask(LoginActivity.this, LoginActivity.this, TaskType.USER_LOGIN).execute(url);
 }
    ...
    public void onTaskCompleted(String result, TaskType taskType) {
       if(taskType == TaskType.USER_LOGIN){
       //your login result handling here
       }
}

Lastly, this is your AsyncTask

public class GetAsyncTask extends AsyncTask<String, Void, String> {
    String outputStr;
    ProgressDialog dialog;
    Context context;
    AsyncTaskListener taskListener;
    TaskType taskType;

    public GetAsyncTask(Context context, AsyncTaskListener taskListener, TaskType taskType){
        this.context = context;
        this.taskListener = taskListener;
        this.taskType = taskType;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        dialog = ProgressDialog.show(context, "Loading", "Please wait...", true);
    }
    @Override
    protected String doInBackground(String... params) {
        String urlString = params[0];

        try {

            URL url = new URL(urlString);
            HttpURLConnection conn
                    = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            if (conn.getResponseCode() != 200) {
                throw new IOException(conn.getResponseMessage());
            }

            // Buffer the result into a string
            BufferedReader rd = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = rd.readLine()) != null) {
                sb.append(line);
            }
            rd.close();
            conn.disconnect();
            String jsonStr = sb.toString();
            outputStr = jsonStr;
        } catch (SocketTimeoutException e) {
            outputStr = "timeout";
        }catch(Exception e){
            e.printStackTrace();
            outputStr = "error";
        }

        return outputStr;
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        taskListener.onTaskCompleted(result, taskType);
        dialog.dismiss();
    }

}

Use Listener pattern. Take a look here TaskListener , Task that uses this listener and fragment that calls task

Try making your own http requesting class that extends AsyncTask and than put your code in "doInBackground" call. Thyt way you only have to instantiate your class with url and params and read response. That worked for me.

Here is example:

public class HttpRequest extends AsyncTask<String, Void, String> {
    public String url;
    public List<NameValuePair> nameValuePairs;
    public String httpResponse =null;

    public HttpRequest(String _url, List<NameValuePair> _nameValuePairs) {
        url = _url;
        nameValuePairs=_nameValuePairs;
    }

    @Override
    protected String doInBackground(String... params) {
        httpResponse = getWebPageWithFormData(url, nameValuePairs);
        return "";
    }

    public static String getWebPageWithFormData( String url, List<NameValuePair> nameValuePairs ){
        String html = "";
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost(url);
        if (nameValuePairs!=null){
            try {httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));}
            catch (Exception e){}
        }
        HttpResponse response = null;
        try {
            response = httpclient.execute(httppost);
        }
        catch (ClientProtocolException e) { return ""; }
        catch (IOException e) { return ""; }
        int responseCode = response.getStatusLine().getStatusCode();
        switch(responseCode) {
            case 200:
                HttpEntity entity = response.getEntity();
                if(entity != null) {
                    try{ html = EntityUtils.toString(entity);}
                    catch (Exception e) {}
                }
                break;
        }
        return html;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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