简体   繁体   中英

Get API data to RecyclerView in AsyncTask

I'm writing a simple Android app to get a JSON array into RecyclerView with AsyncTask. I know that I can use libraries as Retrofit or OKHTTP, but this time I tried to write the connection IO from scratch. The connection succeeded and data has been parsed and added to ArrayList. I do all of these in doInBackground() , and in onPostExecute() I just call notifyDataSetChanged() to the adapter, but it didn't work. I tried several ways such as move setAdapter() to onPostExecute() , or move all the AsyncTask to Adapter class and they didn't help anything. Can someone tell me what I miss, if I cannot fix it in 2 or 3 days, I think I will use Retrofit instead.
This is my Main class, I think the bug is only here, but if you need to see my adapter please leave a comment, thanks a lot.

public class MainActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    ProgressDialog progressDialog;
    String apiUrl;
    Gson gson;
    List<User> userList;
    UserAdapter userAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recycle_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));

        apiUrl = "https://lebavui.github.io/jsons/users.json";
        gson = new Gson();
        userList = new ArrayList<>();

        userAdapter = new UserAdapter(userList, MainActivity.this);
        recyclerView.setAdapter(userAdapter);

        DataGetter dataGetter = new DataGetter();
        dataGetter.execute();

    }

    private class DataGetter extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            progressDialog = new ProgressDialog(MainActivity.this);
            progressDialog.setMessage("Loading...");
            progressDialog.setCancelable(false);
            progressDialog.show();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            StringBuilder response = new StringBuilder();
            URL url;
            HttpsURLConnection urlConnection = null;
            try {
                url = new URL(apiUrl);
                urlConnection = (HttpsURLConnection) url.openConnection();
                InputStream is = urlConnection.getInputStream();
                InputStreamReader isr = new InputStreamReader(is);
                int data = isr.read();

                while (data != -1) {
                    response.append((char) data);
                    data = isr.read();
                }

                JSONArray jsonArray = new JSONArray(response.toString());
                for (int i = 0; i < jsonArray.length(); i++) {
                    userList.add(gson.fromJson(jsonArray.getJSONObject(i).toString(), User.class));
                }

            }
            catch (Exception e) {
                e.printStackTrace();
            }
            finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }
            return null;
        }

        @SuppressLint("NotifyDataSetChanged")
        @Override
        protected void onPostExecute(Void unused) {
            super.onPostExecute(unused);
            progressDialog.dismiss();

            userAdapter.notifyDataSetChanged();
        }
    }
}

I think the DataGetter class need to executed first than you can set adapter i test this code and it works

@SuppressLint("NotifyDataSetChanged")
@Override
protected void onPostExecute(Void unused) {
     super.onPostExecute(unused);
     progressDialog.dismiss();

     userAdapter = new UserAdapter(userList, MainActivity.this);
     recyclerView.setAdapter(userAdapter);
}

As mentioned in comments you should be using something other than depreciated classes. Below is an example of using runnable, simply add your parser and adapter

This should be moved to android view model.

public class MainActivity extends AppCompatActivity {

    private final String LOG_TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v(LOG_TAG, "on Create");
        String apiUrl = "https://lebavui.github.io/jsons/users.json";
        getUsers(apiUrl);
    }

    //return interface
    public  interface Completion{
        void onCompletion(List<String> list);
    }

    //calls a function which call Completion.onCompletion interface off of main thread
    public void getUsers(String apiUrl){
        getAsyncData(apiUrl, this::setListDataOnMain);
    }

    //bring back to main thread
    //This should be in Android View model for application context instead of this.getMainLooper
    private void setListDataOnMain(List<String> list){
        Handler mainHandler = new Handler(this.getMainLooper());
        Runnable myRunnable = () -> {
            //Set local object "list" to your global variable
            //Then notify adapter change
            //only logging here as example
            Log.v(LOG_TAG, "List: " + list);
        };
        mainHandler.post(myRunnable);
    }


    //make async
    public void  getAsyncData(String apiUrl, Completion completion) {
        Runnable runnable = () -> {
            List<String> userList = makeRequest(apiUrl);
            completion.onCompletion(userList);
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }


    //This is not async calling this func from main thread will crash
    public List<String> makeRequest(String apiUrl )  {
        List<String> userList = new ArrayList<>();
        StringBuilder response = new StringBuilder();
        URL url;
        HttpsURLConnection urlConnection = null;
        try {
            url = new URL(apiUrl);
            urlConnection = (HttpsURLConnection) url.openConnection();
            InputStream is = urlConnection.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            int data = isr.read();

            while (data != -1) {
                response.append((char) data);
                data = isr.read();
            }

            JSONArray jsonArray = new JSONArray(response.toString());
            for (int i = 0; i < jsonArray.length(); i++) {
                //your json parsing here
                userList.add(String.valueOf(i));
            }

        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
        return userList;

    }




}

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