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.