问题
I'm using Retrofit to return a response from an api. The data from that response is to be supplied to a recyclerview. I get a Null pointer exception on the data object when passing it to the recycler view. This is because, the app does not wait for the response to be returned before setting up the recyclerview. Is there anyway to have the app wait for the response before proceeding?
I've tried making a synchronous retrofit request but I still get the same issue. I've tried calling setupRecyclerView() in the onPostExecute method of the AsyncTask but this also does not work.
public class SearchResultsActivity extends BaseActivity {
// UI
private Toolbar toolbar;
private SearchView searchbox;
private RecyclerView rvResults;
// Business Logic
private String searchQuery;
public Response response;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_results);
searchQuery = getSearchQuery();
setupToolbar(searchQuery);
searchForProduct(searchQuery);
}
private void setupRecyclerView() {
rvResults = (RecyclerView) findViewById(R.id.rvResults);
rvResults.setHasFixedSize(true);
rvResults.setLayoutManager(new LinearLayoutManager(activityContext));
ResultAdapter adapter = new ResultAdapter(response.getResults());
rvResults.setAdapter(adapter);
}
private void setupToolbar(String query) {
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("");
}
private void searchForProduct(String query) {
new SearchZaposTask().execute("e");
}
private String getSearchQuery() {
String query = "";
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
query = intent.getStringExtra(SearchManager.QUERY);
}
return query;
}
public class SearchZaposTask extends AsyncTask<String, Void, Response> {
@Override
protected Response doInBackground(String... params) {
RestAdapter retrofit = new RestAdapter.Builder()
.setEndpoint(Constants.BASE_URL)
.build();
ZapposAPI api = retrofit.create(ZapposAPI.class);
return api.searchZappos(searchQuery);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Response searchresponse) {
super.onPostExecute(searchresponse);
//Toast.makeText(activityContext,response.getResults().get(0).getProductName(),Toast.LENGTH_LONG).show();
response = searchresponse;
setupRecyclerView();
}
}
}
Edit: Adding ZapposAPI for @tmalseed to look at
public interface ZapposAPI {
@GET("/mobileapi/v1/search?")
Response searchZappos(@Query("term") String query);
@GET("/mobileapi/v1/search?")
void searchZapos(@Query("term") String query, Callback<Response> responseCallback);
@GET("https://zappos.amazon.com/mobileapi/v1/product/asin/{asin}")
Asin searchByAsin(@Path("asin") String asin);
}
回答1:
Rather do it like this:
zapposApi.getWhatever(new Callback<List<YourPOJO>>() {
@Override
public void success(List<YourPOJO> YourPOJO, Response response) {
setupRecyclerView(YourPOJO);
}
@Override
public void failure(RetrofitError error) {
//handle your error
}
});
Your api will have an interface that might look like something in the line of :
@GET("/user/current")
void getCurrentUser(Callback<YourPOJO> response);
This way the actual network call is done in the background and the callback is on the UI thread. You don't have to use async for this!.
回答2:
Found the solution to my problem. In my original code I wanted to wait until I recieved an api response before setting up the recyclerView. However, Android doesn't like when you delay setting up the recyclerView for too long so I got this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.support.v7.widget.RecyclerView$LayoutManager.onAddFocusables(android.support.v7.widget.RecyclerView, java.util.ArrayList, int, int)' on a null object reference
This brought me back to the dilemna where I didn't have the response data to setup the recyclerview yet. So in setupRecyclerView(), I imported recyclerview from the xml and setup the layout manager but removed the code that created the adapter and added it to the recyclerview. Then I put the adapter creation code in the success() method of my Callback implementation. This fixed the problem!
Found my solution here: Why my android activity is crashing with "No adapter attached; skipping layout" but sporadically?
and the code is below.
public class SearchResultsActivity extends BaseActivity {
// UI
private Toolbar toolbar;
private SearchView searchbox;
private RecyclerView rvResults;
// Business Logic
private String searchQuery;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_results);
searchQuery = getSearchQuery();
setupToolbar();
setupRecyclerView();
searchForProducts(searchQuery);
}
private void setupToolbar() {
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("");
}
private void setupRecyclerView() {
rvResults = (RecyclerView) findViewById(R.id.rvResults);
rvResults.setHasFixedSize(true);
rvResults.setLayoutManager(new LinearLayoutManager(activityContext));
}
private String getSearchQuery() {
String query = "";
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
query = intent.getStringExtra(SearchManager.QUERY);
}
return query;
}
private void searchForProducts(String query) {
RestAdapter retrofit = new RestAdapter.Builder()
.setEndpoint(Constants.BASE_URL)
.build();
ZapposAPI api = retrofit.create(ZapposAPI.class);
api.searchZappos(query, new Callback<Response>() {
@Override
public void success(Response apiResponse, retrofit.client.Response response) {
ResultAdapter adapter = new ResultAdapter(apiResponse.getResults());
rvResults.setAdapter(adapter);
}
@Override
public void failure(RetrofitError error) {
}
});
}
来源:https://stackoverflow.com/questions/32473297/android-retrofit-wait-for-response-before-executing-other-code