Trying to get Retrofit onResponse executed first

夙愿已清 提交于 2019-12-25 03:45:48

问题


My list in my activity called AllStores contains only null. I need this list, because I want to populate my ListView later on. The problem is caused because my callback is getting executed as last.

To clarify I have made a screenshot below:

Link: http://i.imgur.com/bIkWjld.png

The screenshot also tells what I actually want. To achieve that I had tried it with a AsyncTask. However it didn't worked out as you can see in the screenshot.

Here is the code:


EDIT 2.0 I have changed my getSubprise() method to become synchronous and using AsyncTask

AllStores.java:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Response<List<Store>> subprises = new StoreService().getSubprises();
    System.out.println("AllStores (Activity) - subprise " + subprises);
}

StoreService.java:

       public Response<List<Store>> getSubprises() {
        new LongOperation().execute("");
        System.out.println("getStores (StoreService.java) value "+ getStores());
        return getStores();
    }

    private class LongOperation extends AsyncTask<String, Void, Response<List<Store>>> {

        @Override
        protected Response<List<Store>> doInBackground(String... params) {
            System.out.println("doInBackground executed second");
            try {
                Call<List<Store>> call = subpriseAPI.listStores();
                stores=call.execute();
                Iterator it=stores.body().iterator();
//                while(it.hasNext())
//                    System.out.println(((Store)it.next()).getSTREET());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return stores;
        }

        @Override
        protected void onPreExecute() {
            //Can't put the call here because this is the main thread
            System.out.println("onPreExecute first");
        }

        @Override
        protected void onPostExecute(Response<List<Store>> result) {
            //Can't put the call here because this is the main thread
            setStores(stores);
        }
    }

    public Response<List<Store>> getStores() {
        return stores;
    }

    public void setStores(Response<List<Store>> stores) {
        this.stores = stores;
    }

However I'm now still getting the same result see the screenshot below:

link: http://i.imgur.com/GOGFXMR.png

The screenshot looks the same as the screenshot from above.


回答1:


To get the result from the background thread to the main thread I had to use AsyncTask.get(). By using that I could see a value in my stores variable instead of having a null value.

Below you can see my code for those who want to see it:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public Response<List<Store>> getSubprises() throws IOException, ExecutionException, InterruptedException {
    LongOperation longOperation = new LongOperation();
    longOperation.execute("");
    stores = longOperation.get();
    return stores;
}

private class LongOperation extends AsyncTask<String, Void, Response<List<Store>>> {
    @Override
    protected Response<List<Store>> doInBackground(String... params) {
        //System.out.println("doInBackground executed second");
        try {
            Call<List<Store>> call = subpriseAPI.listStores();
            stores=call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stores;
    }

    @Override
    protected void onPreExecute() {
        //Can't put the call here because this is the main thread
        //System.out.println("onPreExecute first");
    }

    @Override
    protected void onPostExecute(Response<List<Store>> result) {
        //Can't put the call here because this is the main thread
    }
}



回答2:


i think you'd be better off letting Retrofit invoke the calls internally on its internal HTTP executor. this would involve changing your API interface to something like this:

public interface SubpriseAPI {
    @GET("api/locations/get")
    List<Store> listStores();
}

... and including any relevant configuration for Retrofit to handle the deserialization into your Store type.

however, if you want to invoke the calls yourself, you might consider hosting your own executor instance (or however you want to run your worker threads), something like this (not this exact implementation, but you should get the idea):

class StoreService {
...
private final ExecutorService executorService = Executors.newCachedThreadPool();
...
public Response<List<Store>> getSubprises() {
  executorService.submit(new Callable<List<Store>>() {
            @Override
            public List<Store> call() throws Exception {
                final Call<List<Store>> call = subpriseAPI.listStores();

                try {
                    if(stores == null) {
                        stores = new ArrayList<>();
                    } else {
                        stores.clear();
                    }

                    stores.addAll(call.execute());

                    System.out.println("stores "+ stores);

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).get();
...

UPDATE

i didn't realize you were (apparently) using Retrofit 2. i've had a chance to experiment a bit and this is what i came up with.

here are the relevant dependencies i have in my app/build.gradle:

compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

...here is my API interface:

public interface Api {
    @Headers("Accept: application/json")
    @GET("/v2/567595ad0f0000ea23315d02")
    public Call<List<Widget>> widgets();

}

...here is a simple DTO i'll be unmarshalling some JSON into:

public class Widget {
    public String value;
}

...and finally, here is my test activity: public class Retrofit2TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Api mockyApi = new Retrofit.Builder()
            .baseUrl("http://www.mocky.io")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(Api.class);

        mockyApi.widgets().enqueue(new Callback<List<Widget>>() {
            @Override
            public void onResponse(Response<List<Widget>> response, Retrofit retrofit) {
                if(response.isSuccess()) {
                    Log.d(Retrofit2TestActivity.class.getName(), "## widgets.size() == " + response.body().size());
                } else {
                    Log.d(Retrofit2TestActivity.class.getName(), String.format("## response was NOT successful [%d]", response.code()));
                }
            }

            @Override
            public void onFailure(Throwable t) {
                Log.d(Retrofit2TestActivity.class.getName(), String.format("## response was NOT successful [%s]", t.getMessage()));
            }
        });
    }

}

obviously you should substitute my Widget for your Store type. beyond that, manipulating the List<Store> as a normal member variable of your activity should be straightforward.

i hope that helped.



来源:https://stackoverflow.com/questions/34346224/trying-to-get-retrofit-onresponse-executed-first

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!