Android Retrofit - Wait for response before executing other code?

孤者浪人 提交于 2020-01-07 03:55:16

问题


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

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