问题
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