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();
}
}
}
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;
}
}
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);
}
Related
I am New to the android studio and want to something more. Actually, I am trying to pass the string that I got from the spinner in onCreateMethod and pass to the onPostExecute function. I will be grateful for the help. Bellow is my code.
I tried making a global variable called First and store the string from spinner and pass it on the onPostExecute function.
public class Convert extends AppCompatActivity implements LocationListener
{
Spinner dropdown;
Button btn;
String text;
String first;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_convert);
dropdown = (Spinner) findViewById(R.id.spinner1);
btn = (Button)findViewById(R.id.btn);
String[] items = new String[]{"United States,USD", "Nepal,NPR", "Bangladesh,BDT","Brazil,BRL"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, items);
dropdown.setAdapter(adapter);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
text = dropdown.getSelectedItem().toString();
first = text.substring(text.length()-3);
Log.i("her", first);
}
});
new DownloadTask().execute("http://openexchangerates.org/api/latest.json?
app_id=XXXXXXXXXX");
}
public class DownloadTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String result = "";
URL url;
HttpURLConnection httpURLConnection = null;
try {
url = new URL(urls[0]);
httpURLConnection = (HttpURLConnection) url.openConnection();
InputStream in = httpURLConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
int data = reader.read();
while (data != -1) {
char counter = (char) data;
result += counter;
data = reader.read();
}
return result;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
try{
JSONObject jsonObject = new JSONObject(result);
JSONObject curr = jsonObject.getJSONObject("rates");
String npr = curr.getString(first);
Log.i("money", npr );
} catch (JSONException e) {
e.printStackTrace();
}
}
}
What I want is to pass the string first on the onPostExecute function.
When you will call your DownloadTask, asyncTask fires with method execute, just pass param though him. Example:
How to pass url
new DownloadTask().execute("url for download");
How to receive url
protected String doInBackground(String... urls) {
String url = urls[0]; // url for download
}
Also you could send and array of params. Also be careful with AsyncTask, do not pass your context/view variable, it could arise memory leaks, read docs.
This is the code I am Using.
public class MainActivity extends AppCompatActivity {
public ArrayList<String> ImageUrls = new ArrayList<>();
public ArrayList<String> ImageNames = new ArrayList<>();
public ArrayList<String> ImageDesc = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initImages();
}
private void initImages(){
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("http://url.in/wp-json/wp/v2/posts?_embed")
.build();
#SuppressLint("StaticFieldLeak") AsyncTask<Void, Void, String> asyncTask = new AsyncTask<Void, Void, String>() {
private static final String TAG = "SlideFragment";
#Override
protected String doInBackground(Void... params) {
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
Log.d(TAG, "doInBackground: REsponse Un Successfull - 56");
return null;
}
String Data = response.body().string();
response.body().close();
return Data;
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "doInBackground: Exceptione on line63");
return null;
}
}
#Override
protected void onPostExecute(String Data) {
super.onPostExecute(Data);
if (Data != null) {
Log.d(TAG, "onPostExecute: line72");
try {
JSONArray json = new JSONArray(Data);
for (int i = 0; i < json.length(); i++) {
JSONObject post = json.getJSONObject(i);
String title = post.getJSONObject("title").getString("rendered");
String description = post.getJSONObject("content").getString("rendered");
String imgURL = post.getJSONObject("_embedded").getJSONArray("wp:featuredmedia").getJSONObject(0).getJSONObject("media_details").getString("file");
String imagUrl = "http://url.in/wp-content/uploads/" + imgURL;
ImageNames.add(title);
ImageDesc.add(description);
ImageUrls.add(imagUrl);
Log.d(TAG, "onPostExecute: " + ImageNames);
}
}catch(JSONException j){
j.printStackTrace();
Log.d(TAG, "onPostExecute: on line 121");
}
}
}
};
asyncTask.execute();
initRecycler();
}
private void initRecycler(){
RecyclerViewPager mRecyclerView = (RecyclerViewPager) findViewById(R.id.list);
// setLayoutManager like normal RecyclerView, you do not need to change any thing.
LinearLayoutManager layout = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mRecyclerView.setLayoutManager(layout);
//set adapter
//You just need to implement ViewPageAdapter by yourself like a normal RecyclerView.Adpater.
RecyclerViewAdapter adapter = new RecyclerViewAdapter(ImageUrls, ImageNames, ImageDesc, this);
mRecyclerView.setAdapter(adapter);
}
}
I have run the same code with local data i..e the ArrayList with hardcoded data. It works. But If I try with API data It shows Nothing. I have checked the ArrayList with logging. It is fine.
I don't know where I am Wrong.
UPDATE
Thanks to #sonhnLab. In the code I have removed initRecycler(); from initImages(); and added to onPostExecute();. That worked.
Due to the asynchronous nature of Asynctask, the following line: "initRecycler();" doesn't necessarily gets called after completion of the network request hence no content. Remember, any task that depends on the asynchronous response needs to be implemented inside response method, in this case inside onPostExecute().
With the Help of sonhnlab I have successfully got the desired output.
I have made this initRecycler(); call into onPostExecute() call. so when the information is ready from the API call it initiates the Recycler.
I have Updating the Code in the question.
You should call initRecyler() onPostExecute when async task is completed
I tried many ways but I got blank layout.I changed lots of lines but the result is always the same. Should I rewrite the code and try something different. I followed some videos on youtube but nobody has the proper solution.I don't think it is caused because array got null result. Anybody knows what might be wrong:
AirportTransportActivity
public class AirportTransportActivity extends AppCompatActivity {
ListView listView;
ArrayAdapter<String> adapter;
String[] data = new String[0];
JSONObject jsonObject = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_airport_transport);
//Get airport details
Intent intent = getIntent();
String getairport = intent.getStringExtra("airport");
final TextView textViewAirport = (TextView) findViewById(R.id.tvairport);
textViewAirport.setText(getairport);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());
//List view setup
listView = (ListView) findViewById(R.id.lvairport);
//Get airport transport
new RetrieveTask().execute();
//Adapter
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
listView.setAdapter(adapter);
}
private class RetrieveTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... voids) {
String strUrl = "http://my database";
URL url = null;
StringBuffer sb = new StringBuffer();
try {
url = new URL(strUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream iStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(iStream));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
iStream.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
#Override
protected void onPostExecute(String result) {
try {
JSONArray jsonArray = new JSONArray(result);
data = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);
data[i] = jsonObject.getString("airporttransportname");
}
} catch (JSONException e) {
e.printStackTrace();
}
adapter.notifyDataSetChanged();
}
}
}
In your original post you have set the adapter before you have added the data. But I also suspect that you are having issues with:
String[] data = new String[0];
So I changed it to
ArrayList<String> data = new ArrayList<>();
I also changed your AsyncTask a bit. Now you can do most of your parsing in the background thread. When the doInBackground is successful just notify the adapter of the changes in onPostExecute.
You will also need to check if the ArrayList<String> fits to your Adapter class. If not just change it as needed.
Do this instead:
public class AirportTransportActivity extends AppCompatActivity {
private static final String TAG = AirportTransportActivity.class.getSimpleName();
ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_airport_transport);
//Get airport details
Intent intent = getIntent();
String getairport = intent.getStringExtra("airport");
final TextView textViewAirport = (TextView) findViewById(R.id.tvairport);
textViewAirport.setText(getairport);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());
//List view setup
listView = (ListView) findViewById(R.id.lvairport);
//Get airport transport
new RetrieveTask().execute();
}
private class RetrieveTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... voids) {
String strUrl = "http://my database";
URL url = null;
StringBuffer sb = new StringBuffer();
try {
url = new URL(strUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream iStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(iStream));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
iStream.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return toString();
}
#Override
protected void onPostExecute(String result) {
if(result.isEmpty()) return;
try{
ArrayList<String> data = new ArrayList<>();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
JSONArray jsonArray = new JSONArray(result);
int len = jsonArray.length();
Log.e(TAG, "Lenth of json array = " + len)
for (int i = 0; i < len; i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
// I add the optString variation just in case the data is corrupt
String s = jsonObject.optString("airporttransportname", "?");
data.add(s);
}
listView.setAdapter(adapter);
}
catch(JSONException e){
e.printStackTrace();
}
}
}
Disclaimer I did this in a text editor and wasn't able to count on "auto-correct" for some of the syntax or method names--so you will need to check it.
There are some other things I would change in your code, but I wanted to leave it as close to the original as I could.
You're setting the data array and calling notifyDataSetChanged() before your async task has a chance to finish. You need to set your adapter and notifyDataSetChanged() from within the onPostExecute() method in your async task.
Just to be clear, when you call asyncTask.execute(), it starts the async task and then immediately keeps executing the rest of the code. So when you set your data array in your list view adapter, the asyncTask hasn't even finished and your array items are still null.
No need to change anything just add adapter.notifyDataSetChanged(); after storing all values into your array
Remove this line adapter.notifyDataSetChanged();in Oncreate ,You
have to use notifyDataSetChanged() when your arraylist values is getting
changed.
#Override
protected void onPostExecute(String result) {
try {
JSONArray jsonArray = new JSONArray(result);
data = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);
data[i] = jsonObject.getString("airporttransportname");
}
} catch (JSONException e) {
e.printStackTrace();
}
adapter.notifyDataSetChanged();
}
I've done a search on another stackoverflow post for 2 hours but still can not solve this problem. I have a variable called copyAudioListIqro with List String datatype in DetailMemilihIqro Activity class. When the variable called audioIqros in the AsyncTask class (precisely in the onPostExecute method) this list has a value from my json and I want to copy audioIqros variable to copyAudioListIqro via updateData method (outside the asynctask class). When I see the log monitor on updateData method I can see the value from copyAudioListIqro, but the problem is, when I access it via readDataAudioURL method(outside the asynctask class) copyAudioListIqro variable becomes null.
What is the solution for this problem?
Thank you
Here is the overall DetailMemilihIqro class
public class DetailMemilhIqro extends AppCompatActivity {
private ProgressDialog pDialog;
private List<ModelAudioIqro> audioIqros;
private List<String> copyAudioListIqro;
private AudioAdapter mAdapter;
private RecyclerView recyclerView;
private String TAG = DetailMemilihIqro.class.getSimpleName();
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail_memilih_iqro);
recyclerView = (RecyclerView) findViewById(R.id.rvCVAudioIqro);
pDialog = new ProgressDialog(this);
audioIqros = new ArrayList<>();
mAdapter = new AudioAdapter(getApplicationContext(), audioIqros);
context = getApplicationContext();
copyAudioListIqro = new ArrayList<>();
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(mAdapter);
Bundle getPosition = getIntent().getExtras();
int position = getPosition.getInt("positionUserClicked");
Log.d(TAG, "Position User clicked " + position);
if (position == 0) {
String endpoint = "http://latihcoding.com/jsonfile/audioiqro1.json";
new DownloadTask().execute(endpoint);
} else if (position == 1) {
String endpoint = "http://latihcoding.com/jsonfile/audioiqro2.json";
new DownloadTask().execute(endpoint);
} else if (position == 2) {
String endpoint = "http://latihcoding.com/jsonfile/audioiqro3.json";
new DownloadTask().execute(endpoint);
}
readDataAudioURL();
}
public void updateData(List<String> pathUrl) {
for (int i = 0; i < pathUrl.size(); i++) copyAudioListIqro.add(pathUrl.get(i));
Log.d(TAG, "updateData Method " + copyAudioListIqro.toString());
}
public void readDataAudioURL() {
Log.d(TAG, "readDataAudioURL Method " + copyAudioListIqro.toString());
}
public class DownloadTask extends AsyncTask<String, Void, List<String>> {
List<String> modelAudioIqroList;
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog.setMessage("Downloading json...");
pDialog.show();
}
#Override
protected List<String> doInBackground(String... strings) {
modelAudioIqroList = new ArrayList<>();
int result;
HttpURLConnection urlConnection;
try {
URL url = new URL(strings[0]);
urlConnection = (HttpURLConnection) url.openConnection();
int statusCode = urlConnection.getResponseCode();
// 200 represents HTTP OK
if (statusCode == 200) {
BufferedReader r = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
response.append(line);
}
parseResult(response.toString());
result = 1; // Successful
Log.d(TAG, "Result " + result);
} else {
//"Failed to fetch data!";
result = 0;
Log.d(TAG, "Result " + result);
}
} catch (Exception e) {
Log.d(TAG, e.getLocalizedMessage());
}
return modelAudioIqroList; //"Failed to fetch data!";
}
#Override
protected void onPostExecute(List<String> audioIqros) {
super.onPostExecute(audioIqros);
pDialog.hide();
if (!audioIqros.isEmpty()) {
updateData(modelAudioIqroList);
} else {
Toast.makeText(context, "Empty", Toast.LENGTH_SHORT).show();
}
}
private void parseResult(String result) {
try {
JSONArray response = new JSONArray(result);
for (int i = 0; i < response.length(); i++) {
JSONObject object = response.getJSONObject(i);
ModelAudioIqro modelAudioIqro = new ModelAudioIqro();
modelAudioIqro.setName(object.getString("name"));
modelAudioIqro.setUrl(object.getString("url"));
String path = modelAudioIqro.getUrl();
Log.d(TAG, "String path " + path);
modelAudioIqroList.add(path);
}
} catch (JSONException e) {
e.printStackTrace();
}
mAdapter.notifyDataSetChanged();
}
}
}
Log for the copyAudioListIqro in the updateDataMethod
Log for the copyAudioListIqro in the readDataAudioURL
readDataAudioURL() call, that is a plain Log call, should be moved. Infact the task is asynch by nature, so oblivously the variable copyAudioListIqro won't have been initialized right after the task's start (.execute() method).
You're doing right, anyway, in notyfiying dataset change to list...You should just move it to postExecute as well...
I suggest to move all "after network" code to that postExecute, so that UI can be updated asynchronously ONLY when data is available and without blocking main thread. You can 'read' variables in the inner class, so just declare them final:
#Override
protected void onPostExecute(List<String> audioIqros) {
super.onPostExecute(audioIqros);
pDialog.hide();
if (!audioIqros.isEmpty()) {
updateData(modelAudioIqroList);
//data is now updated, notify datasets and/or send broadcast
mAdapter.notifyDataSetChanged();
readDataAudioURL();
} else {
Toast.makeText(context, "Empty", Toast.LENGTH_SHORT).show();
}
}
A more elaborate pattern would include broadcast receiver and intents, but I guess this is out of this question's scope.
I'm trying to populate an ArrayList of objects and use those objects to populate a ListView. My Asynctask can get the json data and I can parse it and make the objects I need but my ListView doesn't populate. When I check to see if my ArrayList has any object in it before the adapter runs I can see that it doesn't. I want to know why my ListView isn't populating.
Here's my code: (Sorry if it's messy, some spots I haven't gotten to updating yet)
public class MovieDisplayFragment extends Fragment{
private ArrayList<Movie> movieList = new ArrayList<Movie>();
private MovieAdapter movieAdapter;
ListView listView;
public MovieDisplayFragment(){
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
movieAdapter = new MovieAdapter(getActivity(), movieList);
listView = (ListView) rootView.findViewById(R.id.listview_data);
listView.setAdapter(movieAdapter);
if(movieList.size() > 0) {
Log.e("Hello", "1");
}
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l){
Movie movie = movieAdapter.getItem(position);
Intent i = new Intent(getActivity(), DetailActivity.class)
.putExtra(Intent.EXTRA_TEXT, "Hello");
startActivity(i);
}
});
return rootView;
}
private void updateMovieData(){
getMovieData movieData = new getMovieData();
movieData.execute();
}
#Override
public void onStart(){
super.onStart();
updateMovieData();
}
public class getMovieData extends AsyncTask<Void, Void, List<Movie>> {
private final String LOG_CAT = getMovieData.class.getSimpleName();
private List<Movie> getMovieData(String movieJsonStr) throws JSONException {
final String MOV_ITEMS = "results";
final String MOV_TITLE = "original_title";
final String MOV_DATE = "release_date";
final String MOV_SYNOPSIS = "overview";
final String MOV_VOTE = "vote_average";
final String MOV_POSTER_URL = "poster_path";
JSONObject movieJson = new JSONObject(movieJsonStr);
JSONArray movieArray = movieJson.getJSONArray(MOV_ITEMS);
Log.e("Hello", "2");
for (int i = 0; i < movieArray.length(); i++) {
JSONObject movie = movieArray.getJSONObject(i);
movieList.add(new Movie(movie.getString(MOV_TITLE), movie.getString(MOV_DATE),
movie.getString(MOV_SYNOPSIS), movie.getString(MOV_VOTE), movie.getString(MOV_POSTER_URL)));
}
return movieList;
}
protected List<Movie> doInBackground(Void... params) {
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
String movieJsonStr = null;
try {
final String BASE_URL = "http://api.themoviedb.org/3/genre/10751/movies?api_key=358f3b44734f7e6404f2d01a62d3c176&include_all_movies=true&include_adult=true";
Uri builtUri = Uri.parse(BASE_URL).buildUpon().build();
URL url = new URL(builtUri.toString());
Log.v(LOG_CAT, "Built URI " + builtUri.toString());
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null){
movieJsonStr = null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while((line = reader.readLine()) != null){
buffer.append(line + "\n");
}
if (buffer.length() == 0){
movieJsonStr = buffer.toString();
}
movieJsonStr = buffer.toString();
Log.v(LOG_CAT, "Movie String: " + movieJsonStr);
} catch (IOException e) {
Log.e("Fragment", "Error", e);
movieJsonStr = null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e("PlaceholderFragment", "Error closing stream", e);
}
}
}
try {
return getMovieData(movieJsonStr);
} catch (JSONException e) {
Log.e(LOG_CAT, e.getMessage(), e);
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(List<Movie> movies){
if(movies != null){
movieAdapter.clear();
for(Movie movieData : movies){
movieAdapter.add(movieData);
}
}
}
}
}
Put these 2 lines inside your onPostExecute() in Async.
movieAdapter = new MovieAdapter(getActivity(), movieList);
listView.setAdapter(movieAdapter);
AsyncTask runs in Background Thread. It gets the data from json after few seconds. But your adapter is called few milli seconds after your fragment is created.
So the data from the Json will not be there when you are setting the adapter.
Calling it in onPostExecute solves this problem as the adatpter is set after Json data is retrieved from the server!
Hope it helps a bit.
You are printing size of movieList (movieList.size()) much before movieList() is getting populated. It will never print "Hello" "1" in debugger. The asynctask will fill data in movieList much later than your movieList.size() check code in OnCreateView()
Anyways, after the below code
for(Movie movieData : movies)
{
movieAdapter.add(movieData);
}
you need to insert this bit:
listView.setAdapter(movieAdapter);
You are populating the adapter but not setting it to your listView in your onPostExecute() in your getMovieData Asynctask.
It looks like on post execute you are clearing the List that your adapter is using to populate the listview, then adding in new items to the list. However, in order to update the view after that happens, you need to call notifyDataSetChanged(); after updating the list
#Override
protected void onPostExecute(List<Movie> movies){
if(movies != null){
movieAdapter.clear();
for(Movie movieData : movies){
movieAdapter.add(movieData);
}
movieAdapter.notifyDataSetChanged();
}
}