简体   繁体   中英

Get value of variable from inner class

I have a method that implement a class inside it:

public class RetrofitCallBackUtil {
    private ArrayList<Message> messages = new ArrayList<>();


    public ArrayList<Message> getLastTenMessageCallBack(int user_id, int sender_id, int offset_number, RESTDatabaseDAO service) {
        Call<ArrayList<Message>> call = service.getLastTenMessage(user_id, sender_id, offset_number);

        call.enqueue(new Callback<ArrayList<Message>>() {
            @Override
            public void onResponse(Call<ArrayList<Message>> call, Response<ArrayList<Message>> response) {
                if (response.isSuccess()) {
                    messages = response.body();
                    Log.i("Success", "Good ten " + response.body().size());
                } else {
                    Log.i("Success", "Not good  ten" + response.raw());
                }
            }

            @Override
            public void onFailure(Call<ArrayList<Message>> call, Throwable t) {
                Log.i("Fail", "Failure ten " + t.toString());
            }
        });
        //Still get NullPointException from here. (messages.size())
        Log.i("Success", "Good ten activity" + messages.size());
        return messages;
    }

}

I want to get value of messages variable but cannot. I also create a global variable List<Message> messages and return but still cannot get value from response.body() . Anybody have solution for this?

UPDATE:

I want something like this:

List<Message>  testMessages = retrofitCallBackUtil.getLastTenMessageCallBack(AppConfig.USER_ID, userId, 0, service);

Because I want to use the result of that method many time and many place so I want it return me a data.

First, your method public ArrayList<Message> getLastTenMessageCallBack(...) has synchronous signature, but implementation is asynchronous. So when method returns messages , they are still empty.

Correct signature with asynchronous impl would be

public void getLastTenMessageCallBack(...)

where result can be obtained only in callback.

If you want have a synchronous method you may use latch:

public ArrayList<Message> getLastTenMessageCallBack(int user_id, int sender_id, int offset_number, RESTDatabaseDAO service) {

    ArrayList<Message> messages = new ArrayList<>();
    CountDownLatch latch = new CountDownLatch(1);

    Call<ArrayList<Message>> call = service.getLastTenMessage(user_id, sender_id, offset_number);
    call.enqueue(new Callback<ArrayList<Message>>() {
        @Override
        public void onResponse(Call<ArrayList<Message>> call, Response<ArrayList<Message>> response) {
            if (response.isSuccess()) {

                messages = response.body();
                latch.countDown();
            } 
        }

        @Override
        public void onFailure(Call<ArrayList<Message>> call, Throwable t) {
            Log.i("Fail", "Failure ten " + t.toString());
            latch.countDown();
        }
    });
    //waiting result ...
    latch.await();
    return messages;
}

Note, there is no variables out of method scope.

upd
What if you want to have asynchronous method and want use it from many classes. Correct signature would be:

public void getLastTenMessageCallBack(..., Callback<ArrayList<Message>>)

and you will implement this callback in client code. Async method more preferable of course, so it avoid ui blocking

onResponse and onFailure are asynchronously called, so when you are trying to return messages; , it is not updated yet.

return it from within the callbacks.

Edit 1:

public void getLastTenMessageCallBack(int user_id, int sender_id, int offset_number, RESTDatabaseDAO service) {
     Call<ArrayList<Message>> call = service.getLastTenMessage(user_id, sender_id, offset_number);
     call.enqueue(new Callback<ArrayList<Message>>() {
          @Override
          public void onResponse(Call<ArrayList<Message>> call, Response<ArrayList<Message>> response) {
               if (response.isSuccess()) {
           // THIS IS BEING CALLED AFTER YOUR METHODS RETURNS, SO UPDATE YOUR VIEWS LIKE THIS.

            updateViews(response.body());
            Log.i("Success", "Good ten " + response.body().size());
        } else {
            Log.i("Success", "Not good  ten" + response.raw());
        }
    }

    @Override
    public void onFailure(Call<ArrayList<Message>> call, Throwable t) {
        Log.i("Fail", "Failure ten " + t.toString());
    }
   });


}
public void updateViews(List<Messages> messages){
    //use messages to update your views here.
}

Edit 2: now since you are calling it from many places, pass something like requestCode and check the the same onResponse to identify the origin of the call.

public void getLastTenMessageCallBack(final int requestCode, int user_id, ..

and onResponse

updateViews(requestCode, response.body());

and update your views accordingly by comparing the request code.

public void updateViews(int requestCode, List<Messages> messages){
    if(requestCode==XYZ)
    //use messages to update your views here.
}

Edit 3: Define your own interface to receive callbacks:

public interface NetworkResponseCallback {
   void onSuccess(ArrayList<Message>> messages);
   void onFailure(); //if you want to pass something else, like cause of failure you can add it.
}

Now, change your method definition:

public void getLastTenMessageCallBack(int user_id, ... , final NetworkResponseCallback networkResponseCallback){

implement onResponse and onFailure :

if (response.isSuccess()) {
  if(networkResponseCallback!=null)
       networkResponseCallback.onSuccess(response.body());


public void onFailure(Call<ArrayList<Message>> call, Throwable t) {
    Log.i("Fail", "Failure ten " + t.toString());
    networkResponseCallback.onFailure();
}

Call the getLastTenMessageCallBack method like this:

getLastTenMessageCallBack(int user_id, int sender_id, int offset_number, RESTDatabaseDAO service, new NetworkResponseCallback() {
        @Override
        public void onSuccess(List<Messages> messages) {

        }

        @Override
        public void onFailure() {

        }
    });

You're declaring a locally scoped variable called "messages" in your anonymous inner class that will hide the outer class variable. Try removing that line and adding the messages member variable back into your class object.

You had the right idea with having a global variable, but you never assigned any value to this variable, all you did was declare a local variable in the function onResponse() , the SCOPE of this variable is just inside that function onResponse() so anywhere outside that function that variable won't exist.

So by declaring a variable messages which is scoped to the enclosing class, you can now directly assign the response body to the "class scoped" messages variable

This should do it,

private List<Message> messages;
public ArrayList<Message> getLastTenMessageCallBack(int user_id, int sender_id, int offset_number, RESTDatabaseDAO service) {
    Call<ArrayList<Message>> call = service.getLastTenMessage(user_id, sender_id, offset_number);
    call.enqueue(new Callback<ArrayList<Message>>() {
        @Override
        public void onResponse(Call<ArrayList<Message>> call, Response<ArrayList<Message>> response) {
            if (response.isSuccess()) {
                // This is wrong you are creating a new variable
                // messages scoped to this function.
                // List<Message>  messages = response.body();
                // Instead just assign the response body to the class
                // scoped variable messages
                messages = response.body();
                Log.i("Success", "Good ten " + response.body().size());
            } else {
                Log.i("Success", "Not good  ten" + response.raw());
            }
        }

        @Override
        public void onFailure(Call<ArrayList<Message>> call, Throwable t) {
            Log.i("Fail", "Failure ten " + t.toString());
        }
    });
    Log.i("Success", "Good ten activity" + messages.size());
    return messages;
}

EDIT

should we use some way to make the return of getLastTenMessageCallBack() later then onResponse?

The above function getLastTenMessagesCallBack will always return null, because the onResponse function is called after the success of your Retrofit call, so you cannot be returning anothing from the function getLastTenMessagesCallBack because the messages variable wont be set until the callback from your retrofit call is called, so the solution for this is,

First you create an interface to that has a function which takes in the result of your Retrofit call

public interface MyCustomCallback {
    void onSuccess(Object result);

    void onError(Object errorObject);
}

then you pass in an implementation of this interface to the getLastTenMessageCallBack

public void getLastTenMessageCallBack(int user_id, int sender_id, int offset_number, RESTDatabaseDAO service, final MyCustomCallback callback) {
    Call<ArrayList<Message>> call = service.getLastTenMessage(user_id, sender_id, offset_number);
    call.enqueue(new Callback<ArrayList<Message>>() {
        @Override
        public void onResponse(Call<ArrayList<Message>> call, Response<ArrayList<Message>> response) {
            if (response.isSuccess()) {
                // This is wrong you are creating a new variable
                // messages scoped to this function.
                // List<Message>  messages = response.body();
                // Instead just assign the response body to the class
                // scoped variable messages
                messages = response.body();
                callback.onSuccess(messages);
                Log.i("Success", "Good ten " + response.body().size());
            } else {
                Log.i("Success", "Not good  ten" + response.raw());
            }
        }

        @Override
        public void onFailure(Call<ArrayList<Message>> call, Throwable t) {
            Log.i("Fail", "Failure ten " + t.toString());
        }
    });
}

So remember while calling getLastTenMessageCallback you need to pass an Implementation of the interface, an example implementation of the interface I just created would look like this

class CallbackImpl implements MyCustomCallback {

    @Override
    public void onSuccess(Object result) {
        List<Message> messages = (List<Message>) result;
    }

    @Override
    public void onError(Object errorObject) {

    }
}

PS you can refine this further by using generics in the callback interface just like how retrofit does for instance

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