1

i've got something blowing my mind all day long.

The question is, I have an AsyncTask that returns me an User Object, with all its attributes. I know that I have to return from the doInBackground method and receive in the OnPostExecute to work with my data. The fact is that i want to extract that user out of the AsyncTask method because i have to work with it in my main thread. My AsyncTask class is placed in the MainActivity.class.

i've heard about using interfaces to get my value back but i can't understand the way to do it.

 public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
    RequestHandler rh = new RequestHandler(); //this is the class i use to do de server conection
    User user;
    User ret_user;


    public FetchUserDataAsyncTask(User user){
        this.user  = user;
    }


    @Override
    protected void onPostExecute(User user) {
        super.onPostExecute(user);

   //I WANT THIS USER IN MY MAIN THREAD, TO WORK WITH ITS ATTRIBUTES

    }



    @Override
    protected User doInBackground(Void... params) {
        try {
            HashMap<String, String> dataToSend = new HashMap<>();
            dataToSend.put("username", user.username);
            dataToSend.put("password", user.password);
            ret_user = rh.sendGetRequest("myadresstophp.php", dataToSend);

        } catch (Exception e) {
            e.printStackTrace();
        }

 return ret_user;
 }

and the call (when user press the log in button), a few lines above.

 new FetchUserDataAsyncTask(userk).execute();

I was hoping to do something like that: (i know its not the way to do it)

User user = new FetchUserDataAsyncTask(userk).execute();

Thank you all, have a nice day!

Álvaro Koke
  • 113
  • 1
  • 10
  • This answer on that question is probably the best/easiest way for you to do it: http://stackoverflow.com/a/20012923/4409409 – Daniel Nugent Feb 17 '16 at 17:20
  • Also, if your AsyncTask is a subclass of the Activity, technically you could just make any member variables you need in the Activity (such as one for username), and then access that member variable in `onPostExecute()` of the AsyncTask for comparison to the username that comes from the server. – Daniel Nugent Feb 17 '16 at 17:27
  • thanks @DanielNugent I got the thing.. but now the question is: if i call a method from the OnPostExecute passing the user as parameter, how can i access to that User outside the new method. Can you give me an example please? – Álvaro Koke Feb 17 '16 at 17:33
  • Just posted an example in an answer... it should do the trick. – Daniel Nugent Feb 17 '16 at 17:47

5 Answers5

2

At first declare an Interface in your project somewhere having the required functions, then implement that interface in the (AsyncTask)calling class ,then declare one Interface object in the AsyncTask. Create the constructor of AsyncTask as follows:

public FetchUserDataAsyncTask(User user,InterfaceClass object){
    this.user  = user;
    this.interfaceObject=object;
}

And then do the following in onPostExecute:

@Override
protected void onPostExecute(User user) {
    super.onPostExecute(user);
    interfaceObject.function(user);  //call the function of the calling class

}
Matt
  • 74,352
  • 26
  • 153
  • 180
Code Mania
  • 31
  • 2
  • Ok and how can i return that user to the line i call the asyncTask? – Álvaro Koke Feb 17 '16 at 17:42
  • See when you implement the interface in the calling class, you must have to use the functions declared in the interface. In that function you can take user as argument. – Code Mania Feb 17 '16 at 17:47
1

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

For example:

Your interface:

public interface OnTaskCompleted{ 
    void onTaskCompleted(); 
}

Your Activity:

public class YourActivity implements OnTaskCompleted{ 
    // your Activity 
}

And your AsyncTask:

public class 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(); 
    } 
}
Shashank Udupa
  • 2,173
  • 1
  • 19
  • 26
0

onPostExecute runs on the UI thread by default (main thread). Do what ever you need to be do on the main thread in onPostExecute

I suggest you read up more from the link provided below for a better understanding on AsyncTask http://developer.android.com/reference/android/os/AsyncTask.html

Shashank Udupa
  • 2,173
  • 1
  • 19
  • 26
  • Yes I know how it works. The fact is that I need to compare the user returned in the AsincTask with the one the user writes to log in, so I need to pass the user of the onPostExecute out of the method to the main thread. I don't know if i'm explaining myself. – Álvaro Koke Feb 17 '16 at 17:12
0

I'm uncertain what you mean by "main thread". If you mean "UI Thread", which is the primary thread used to execute in your app, then Shashank's reply is correct.

However, I suspect from the context of the question that you actually mean that you want the results returned to the code that initiated the AsyncTask. Let's call that the "invoking object" In that case, what I would do is define a callback in your invoking object whose only purpose is to receive the result of this AsyncTank. You could call it onUserDataFetched(User user).

So, to do this using Interfaces, you could define an Interface that contains a single method:

public interface FetchUserDataListener {
    public void onUserDataFetched(User user);
}

Then make sure that your InvokingObject implements that interface, and pass your InvokingObject (which implements FetchUserData, and thus can be considered that object type) to your AsyncTask when you instantiate it.

So, you invoking object code would look like this:

public class InvokingObject implements FetchUserData {
    public void someMethodToInvokeFetchUserData() {
        //....
        new FetchUserDataAsyncTask(userk, this).execute();
        //....
    }

    @Override
    public void onUserDataFetched(User user) {
         //Compare the result to the data in your invoking object
    }
}

Then pass that callback to your AsyncTask when you construct it: And your AsyncTask Code would look like this:

public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User>         {
  FetchUserDataListener mFetchUserDataListener;

  public FetchUserDataAsyncTask(User user, FetchUserDataListener listner){
      this.user  = user;
      mFetchUserDataListener = listener
  }

  //...
  @Override
  protected void onPostExecute(User user) {
      super.onPostExecute(user);
      listener.onUserDataFetched(user)
  }
  //...

 }
rothloup
  • 1,220
  • 11
  • 29
  • This is of course a good way to do it if your AsyncTask is not a subclass of the Activity. The OP has a more simple case though, they have the AsyncTask as a subclass of their Activity, so there's really no reason to use an interface listener, as they can make any Activity method call directly from onPostExecute() of the AsyncTask. – Daniel Nugent Feb 17 '16 at 17:33
  • you are correct, Daniel. Since it happens to be in the same class, you can call `onUserDataFetched()` directly and eliminate the need for interfaces. I thought explaining this, however, would be more instructive. – rothloup Feb 17 '16 at 17:36
  • Thanks for the answer guys, the problem is that i can't implement (maybe I can I don't know) the interface because i'm implementing OnclickListener. So is there any other way to do it? – Álvaro Koke Feb 17 '16 at 17:40
  • As the above comments pointed out, you don't need a listener since your AsyncTask is inside your Activity. However, objects can implement multiple interfaces, so I don't see why implements onClickListener prevents you from doing this. I'll edit my answer to add code for the case where you don't use a listener. - bah, Dan beat me to it. Either method should work for you. – rothloup Feb 17 '16 at 17:42
0

Here you go, this is in general how it would work. This is based on this answer and modified to be specific to your existing code. Basically assign your member variable based on what the user entered, and compare that value to the one you get from the server:

public class MyClass extends Activity {

  //member variable for the username the user enters:
  User userEnteredUser;

  public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
    RequestHandler rh = new RequestHandler(); //this is the class i use to do de server conection
    User user;
    User ret_user;


    public FetchUserDataAsyncTask(User user){
        this.user  = user;
    }


    @Override
    protected void onPostExecute(User user) {
        super.onPostExecute(user);

   //I WANT THIS USER IN MY MAIN THREAD, TO WORK WITH ITS ATTRIBUTES
       processValue(user);  //added

    }



    @Override
    protected User doInBackground(Void... params) {
        try {
            HashMap<String, String> dataToSend = new HashMap<>();
            dataToSend.put("username", user.username);
            dataToSend.put("password", user.password);
            ret_user = rh.sendGetRequest("myadresstophp.php", dataToSend);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return ret_user;
    }
   }

  private void getValue()
  {
      EditText et = (EditText) findViewById(R.id.username);
      userEnteredUser.username = et.getText().toString(); //something like this.... replace with your syntax
      new FetchUserDataAsyncTask(userEnteredUser).execute();
  }

  private void processValue(User userFromServer) 
  {
    if (userEnteredUser.equals(userFromServer)) {
      //Users match! Yay!
    }
  }
}
Community
  • 1
  • 1
Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • Thank you very much, it helped me! – Álvaro Koke Feb 17 '16 at 18:08
  • Daniel, last thing. If i want a boolean(por example) from one of those methods you give me, to return to the part where i made the call getValue() to know if the users match (true) or not (false). What would you do? – Álvaro Koke Feb 17 '16 at 18:23
  • @ÁlvaroKoke If I understand correctly, that is not possible. What's going on here is that you are spawning a background thread to run in parallel to the main thread in order to do the network request. The main thread code continues on after starting the AsyncTask, so by the time you have your result from the server, the code in `getValue()` has long since completed, and there's no way to go back. Instead, you need to write your code such that you don't process the values until you have all the info. – Daniel Nugent Feb 17 '16 at 20:23