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
Related
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);
}
So I have been trying to make a feature in my app where I can login and then fetch data from my database through the Django REST Framework. My logging in works as it only uses POST, but retrieving items does not work.
For some reason my AsyncTask does not get called for retrieving posts.
I have placed my AsyncTask for both activities, which are login and posts, on a separate java file only for handling Web Server stuff.
I am wondering if this is because I should put AsyncTask on each activities.
login.java
public class Login extends AppCompatActivity {
Button LoginButton;
EditText uUserName, uPassWord;
WSAdapter.SendAPIRequests AuthHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//SetupHomeBtn = (ImageButton) findViewById(R.id.SetupHomeBtn);
LoginButton = (Button) findViewById(R.id.LoginButton);
uUserName = (EditText) findViewById(R.id.LoginUserBox);
uPassWord = (EditText) findViewById(R.id.LoginPassBox);
//AuthHelper = new WSAdapter().new SendDeviceDetails();
// Moves user to the main page after validation
LoginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// gets the username and password from the EditText
String strUserName = uUserName.getText().toString();
String strPassWord = uPassWord.getText().toString();
// API url duh
String APIUrl = "http://192.168.0.18:8000/token-auth/";
// If the user is authenticated, then transfer to the MainActivity page
if (APIAuthentication(strUserName, strPassWord, APIUrl)){
startActivity(new Intent(Login.this, Posts.class));
}
}
});
}
private boolean APIAuthentication(String un, String pw, String url){
// when it wasn't static -> AuthHelper = new WSAdapter().new SendAPIRequests();
AuthHelper = new WSAdapter.SendAPIRequests();
JSONObject postData = new JSONObject();
try {
// Attempt to input info to the Django API
postData.put("username", un);
postData.put("password", pw);
// Putting the data to be posted in the Django API
AuthHelper.execute(url, postData.toString());
return true;
} catch (JSONException e) {
e.printStackTrace();
}
return false;
}
}
posts.java
public class Posts extends AppCompatActivity {
TextView postsSect;
Button postsDoneBtn;
WSAdapter.SendAPIRequests PostsHelper;
StringBuilder postsBuffer = new StringBuilder();
#Override
protected void onResume(){
super.onResume();
PostsDetails postDetailsHelper = new PostsDetails();
postDetailsHelper.ListPosts();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_posts);
PostsDetails postDetailsHelper = new PostsDetails();
postsDoneBtn = (Button) findViewById(R.id.PostsDoneButton);
postDetailsHelper.callPostDetails("192.168.0.18:8000/api");
postDetailsHelper.ListPosts();
postDetailsHelper.postDetailsCalled('n');
postsDoneBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(Posts.this, MainActivity.class));
}
});
}
public class PostsDetails {
//String post_title, post_content;
ArrayList<Integer> post_id = new ArrayList<Integer>();
ArrayList<String> post_title = new ArrayList<String>();
ArrayList<String> post_content = new ArrayList<String>();
boolean isPDCalled;
// sets if Post details are called
boolean postDetailsCalled(char called) {
if (called == 'y'){
return true;
}
return false;
}
// checks if postsDetails functions are called for AsyncTask
boolean getIsPDCalled(){
return isPDCalled;
}
// calls the execute for AsyncTask
private void callPostDetails(String theurl){
PostsHelper = new WSAdapter.SendAPIRequests();
// sets if post details are called
postDetailsCalled('y');
// executes AsyncTask
PostsHelper.execute(theurl);
}
// sets values for the posts arrays
public void setPost(int p_id, String p_title, String p_content) {
post_id.add(p_id);
post_title.add(p_title);
post_content.add(p_content);
}
// Lists the posts from the database
public void ListPosts() {
/////////// add functionality if a post was deleted and was clicked
postsSect = (TextView) findViewById(R.id.PostsSection);
postsSect.setText(post_title.get(post_title.size()) + "\n");
for (int i = post_id.size() - 1; i > 0; i--)
{
postsSect.append(post_title.get(i));
}
}
}
}
WSAdapter.java
// I forgot what WS stands for, but this class serves as an adapter for JSON and Online stuff
// I think it stands for With-Server Adapter
public class WSAdapter extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
static public class SendAPIRequests extends AsyncTask<String, String, String> {
// Add a pre-execute thing
#Override
protected String doInBackground(String... params) {
Log.e("TAG", params[0]);
Log.e("TAG", params[1]);
String data = "";
HttpURLConnection httpURLConnection = null;
try {
// Sets up connection to the URL (params[0] from .execute in "login")
httpURLConnection = (HttpURLConnection) new URL(params[0]).openConnection();
// Sets the request method for the URL
httpURLConnection.setRequestMethod("POST");
// Tells the URL that I am sending a POST request body
httpURLConnection.setDoOutput(true);
// To write primitive Java data types to an output stream in a portable way
DataOutputStream wr = new DataOutputStream(httpURLConnection.getOutputStream());
// Writes out a byte to the underlying output stream of the data posted from .execute function
wr.writeBytes("postData=" + params[1]);
// Flushes the postData to the output stream
wr.flush();
wr.close();
// Representing the input stream
InputStream in = httpURLConnection.getInputStream();
// Preparing input stream bytes to be decoded to charset
InputStreamReader inputStreamReader = new InputStreamReader(in);
StringBuilder dataBuffer = new StringBuilder();
// Translates input stream bytes to charset
int inputStreamData = inputStreamReader.read();
while (inputStreamData != -1) {
char current = (char) inputStreamData;
inputStreamData = inputStreamReader.read();
// concatenates data characters from input stream
dataBuffer.append(current);
}
data = dataBuffer.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
// Disconnects socket after using
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
Log.e("TAG", data);
return data;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// expecting a response code fro my server upon receiving the POST data
Log.e("TAG", result);
Posts.PostsDetails postsHelper = new Posts().new PostsDetails();
// For posts
try {
if (postsHelper.getIsPDCalled()){
JSONObject pJObj = new JSONObject(result);
JSONArray pJObjArray = pJObj.getJSONArray("posts");
for (int i = 0; i < pJObjArray.length(); i++) {
JSONObject pJObj_data = pJObjArray.getJSONObject(i);
postsHelper.setPost(pJObj_data.getInt("id"), "post_title", "post_content");
}
}
} catch (JSONException e) {
//Toast.makeText(JSonActivity.this, e.toString(), Toast.LENGTH_LONG).show();
Log.d("Json","Exception = "+e.toString());
}
}
}
}
Yes, you can and should put the network calls functions in a separate java file for better readability and test-coverage.
Apart from that, i would suggest to use Retrofit as your HTTP client. It helps you to manage all the dirty things like headers and converters etc, so you can put all your effort on your logic and implementing your callback actions.
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 want to read and store all JSON values from this api Link with get request "Mini" as example (which is actually an user input variable) and the last number is the page your are viewing. Every page can hold a max of 50 results. The same link is also in XML format (I must read and store as JSON, this is for easier understanding)
In this exmaple there are 8 pages with a total of 359 results. I need to loop through all pages and add all the JSON values to the same object list.
I have the code which work to read one page. I do not know how to make it loop through all pages and add to same object list.
In the acitivty.java onCreate I call the AsyncTask.
String userSearchRequest = search_activity_data.getString("userSearchRequest");
int page = 0;
String spidy_iTN_url = "http://www.gw2spidy.com/api/v0.9/json/item-search/" + userSearchRequest + "/" + page;
itemsByInput_AsyncTask itemsByInput_AsyncTask = new itemsByInput_AsyncTask();
itemsByInput_AsyncTask.setItemListToListings(this);
itemsByInput_AsyncTask.execute(spidy_iTN_url);
This is my AsyncTask class called itemsByInput_AsyncTask.java
import constructors.itemResults_api_constr;
import constructors.itemRoot_api_constr;
public class itemsByInput_AsyncTask extends AsyncTask<String, Void, JSONObject> {
JSONObject Jo_result;
private itemListToListings itemListToListings;
public void setItemListToListings (itemListToListings itemListToListings) {
this.itemListToListings = itemListToListings;
}
#Override
protected JSONObject doInBackground(String... params) {
return spidyHttpGetRequest(params[0]);
}
public JSONObject spidyHttpGetRequest(String URL){
try {
HttpGet get = new HttpGet(URL);
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity);
Jo_result = new JSONObject(result);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return Jo_result;
}
#Override
protected void onPostExecute(JSONObject jsonObject) {
super.onPostExecute(jsonObject);
this.itemListToListings.itemListToListings(JoToJO_constructor(jsonObject));
}
public itemRoot_api_constr JoToJO_constructor(JSONObject Jo_result) {
itemRoot_api_constr spidy_iTN_rootO = new itemRoot_api_constr();
try {
spidy_iTN_rootO.setCount(Jo_result.getInt("count"));
spidy_iTN_rootO.setPage(Jo_result.getInt("page"));
spidy_iTN_rootO.setLast_page(Jo_result.getInt("last_page"));
spidy_iTN_rootO.setTotal(Jo_result.getInt("total"));
JSONArray list = new JSONArray(Jo_result.getString("results"));
for (int i = 0; i < spidy_iTN_rootO.getCount(); i++) {
JSONObject resultsObject = list.getJSONObject(i);
itemResults_api_constr spidy_iTN_resultsO = new itemResults_api_constr();
spidy_iTN_resultsO.setData_id(resultsObject
.getInt("data_id"));
spidy_iTN_resultsO.setName(resultsObject
.getString("name"));
spidy_iTN_resultsO.setRarity(resultsObject
.getInt("rarity"));
spidy_iTN_resultsO.setRestriction_level(resultsObject
.getInt("restriction_level"));
spidy_iTN_resultsO.setImg(resultsObject
.getString("img"));
spidy_iTN_resultsO.setType_id(resultsObject
.getInt("type_id"));
spidy_iTN_resultsO.setSub_type_id(resultsObject
.getInt("sub_type_id"));
spidy_iTN_resultsO.setPrice_last_changed(resultsObject
.getString("price_last_changed"));
spidy_iTN_resultsO.setMax_offer_unit_price(resultsObject
.getInt("max_offer_unit_price"));
spidy_iTN_resultsO.setMin_sale_unit_price(resultsObject
.getInt("min_sale_unit_price"));
spidy_iTN_resultsO.setOffer_availability(resultsObject
.getInt("offer_availability"));
spidy_iTN_resultsO.setSale_availability(resultsObject
.getInt("sale_availability"));
spidy_iTN_resultsO.setSale_price_change_last_hour(resultsObject
.getInt("sale_price_change_last_hour"));
spidy_iTN_resultsO.setOffer_price_change_last_hour(resultsObject
.getInt("offer_price_change_last_hour"));
spidy_iTN_rootO.addObject(spidy_iTN_resultsO);
}
} catch (JSONException e) {
e.printStackTrace();
}
return spidy_iTN_rootO;
}
public interface itemListToListings {
public void itemListToListings(itemRoot_api_constr resultClass);
}
}
And finally in my activity.java i can use my object in the method itemListToListings().
How can I make this loop through all pages (last_page property) and add all JSON values as object in the same list.
EDIT: My itemListToListings function in my activity.
public void itemListToListings(final itemRoot_api_constr spidy_iTN_construct) {
ArrayList<listItemWidgets_constr> image_details = getListData(spidy_iTN_construct);
final ListView lv1 = (ListView) findViewById(R.id.listView);
lv1.setAdapter(new itemListAdapter(this, image_details));
lv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
//listItemWidgets_constr newsData = (listItemWidgets_constr) lv1.getItemAtPosition(position);
Toast.makeText(resultsActivity.this, "Selected :" + spidy_iTN_construct.results(position).name, Toast.LENGTH_LONG).show();
Intent i = new Intent(resultsActivity.this, listingsActivity.class);
i.putExtra("itemId", spidy_iTN_construct.results(position).data_id);
startActivity(i);
}
});
}
EDIT 3: error log
05-01 07:17:39.828 3620-3620/com.example.krijn.gw2TP_androidMobile E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.krijn.gw2TP_androidMobile, PID: 3620
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.krijn.gw2TP_androidMobile.AsyncTasks.itemsByInput_AsyncTask$itemListToListings.itemListToListings(com.example.krijn.gw2TP_androidMobile.constructors.itemRoot_api_constr)' on a null object reference
at com.example.krijn.gw2TP_androidMobile.AsyncTasks.itemsByInput_AsyncTask.onProgressUpdate(itemsByInput_AsyncTask.java:88)
at com.example.krijn.gw2TP_androidMobile.AsyncTasks.itemsByInput_AsyncTask.onProgressUpdate(itemsByInput_AsyncTask.java:27)
After I get this error in the Logcat I still see the Log updating with the following in doInBackground
for (int n = 1; n < nPage; n++){
Log.i("gw2Log", "n: " + n);
publishProgress(JoToJO_constructor(spidyHttpGetRequest(makeUrl(n))));
}
After that is done looping the application crashes.
I think you want to make chain calls depending on last_page property you get from the first page. I would do somethig like this where upon each completion of a request the UI is updated on onProgressUpdate
public class itemsByInput_AsyncTask extends AsyncTask<Void, itemRoot_api_constr, Void> {
JSONObject Jo_result;
private itemListToListings itemListToListings;
String userSearchRequest;
public itemsByInput_AsyncTask(String userSearchRequest){
this.userSearchRequest = userSearchRequest;
}
private String makeUrl(int page){
return "http://www.gw2spidy.com/api/v0.9/json/item-search/" +
this.userSearchRequest + "/" + page;
}
#Override
protected Void doInBackground(Void... params) {
itemRoot_api_constr iac;
iac = JoToJO_constructor(spidyHttpGetRequest(makeUrl(0)));
nPage = iac.getLast_page();
publishProgress(iac);
for (int n = 1; n<nPage; n++){
publishProgress(spidyHttpGetRequest(makeUrl(n)));
}
return null;
}
#Override
protected void onProgressUpdate(itemRoot_api_constr... iacs) {
super.onProgressUpdate(iacs);
// assuming method itemListToListings updates UI
// if it doesn't then publishProgress and onProgressUpdate are not needed
// and itemListToListings can be done in doInBackground
this.itemListToListings.itemListToListings(iacs[0]);
}
#Override
protected Void onPostExecute(Void void) {
super.onPostExecute(void);
// unused
}
}
Also:
Adapter, views, and related click listeners should be initiated once. You should move all variables inside of itemListToListings as your Activity field so everytime this callback is called, they won't need to be initiated again.
ListView lv1;
ArrayList<listItemWidgets_constr> image_details = new ArrayList<>();
itemListAdapter adapter;
void onCreate(){
...
lv1 = (ListView) findViewById(R.id.listView);
adapter = new itemListAdapter(this, image_details);
lv1.setOnItemClickListener(...);
}
public void itemListToListings(final itemRoot_api_constr spidy_iTN_construct) {
image_details.clear();
image_details.addAll(getListData(spidy_iTN_construct));
adapter.notifyDataSetChanged();
}
So, I want to display a spinning loading indicator while my ListView is being populated. I successfully have implemented the progress bar, BUT for some reason it disappears BEFORE all of the listings are displayed. What I want is the progressbar to be present during the TOTAL load time of the listings. Basically, what it seems like, each listing is being displayed one at a time, not all at once when they are all loaded.
What I'm doing is
1. Creating a new custom adapter class
2. Populating the ListView in an AsyncTask using this adapter class
3. Setting the ListView to this adapter
This works properly, the progress bar just disappears before all of the listings are displayed. Does anyone have any ideas?
Activity class:
public class MainActivity extends ActionBarActivity {
ArrayList<Location> arrayOfLocations;
LocationAdapter adapter;
// public static Bitmap bitmap;
Button refresh;
ProgressBar progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progress=(ProgressBar)findViewById(R.id.progressbar_loading);
// Construct the data source
arrayOfLocations = new ArrayList<Location>();
// Create the adapter to convert the array to views
adapter = new LocationAdapter(this, arrayOfLocations);
FillLocations myFill = new FillLocations();
myFill.execute();
refresh = (Button) findViewById(R.id.refresh);
refresh.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
startActivity(getIntent());
}
});
}
private class FillLocations extends AsyncTask<Integer, Void, String> {
String msg = "Done";
protected void onPreExecute() {
progress.setVisibility(View.VISIBLE);
}
// Decode image in background.
#Override
protected String doInBackground(Integer... params) {
String result = "";
InputStream isr = null;
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://afs.spotcontent.com/"); // YOUR
// PHP
// SCRIPT
// ADDRESS
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
isr = entity.getContent();
// resultView.setText("connected");
} catch (Exception e) {
Log.e("log_tag", "Error in http connection " + e.toString());
}
// convert response to string
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(isr, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
isr.close();
result = sb.toString();
} catch (Exception e) {
Log.e("log_tag", "Error converting result " + e.toString());
}
// parse json data
try {
JSONArray jArray = new JSONArray(result);
for (int i = 0; i < jArray.length(); i++) {
final JSONObject json = jArray.getJSONObject(i);
try {
BitmapWorkerTask myTask = new BitmapWorkerTask(
json.getInt("ID"), json);
myTask.execute();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error Parsing Data " + e.toString());
}
return msg;
}
protected void onPostExecute(String msg) {
// Attach the adapter to a ListView
ListView listView = (ListView) findViewById(R.id.listView1);
// View header = (View) getLayoutInflater().inflate(
// R.layout.listview_header, null);
// listView.addHeaderView(header);
listView.setAdapter(adapter);
progress.setVisibility(View.GONE);
}
}
}
Adapter class:
public class LocationAdapter extends ArrayAdapter<Location> {
public LocationAdapter(Context context, ArrayList<Location> locations) {
super(context, R.layout.item_location, locations);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Location location = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_location, parent, false);
}
// Lookup view for data population
TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
TextView tvDetails = (TextView) convertView.findViewById(R.id.tvDetails);
TextView tvDistance = (TextView) convertView.findViewById(R.id.tvDistance);
TextView tvHours = (TextView) convertView.findViewById(R.id.tvHours);
ImageView ivIcon = (ImageView) convertView.findViewById(R.id.imgIcon);
// Populate the data into the template view using the data object
tvName.setText(location.name);
tvDetails.setText(location.details);
tvDistance.setText(location.distance);
tvHours.setText(location.hours);
ivIcon.setImageBitmap(location.icon);
// Return the completed view to render on screen
return convertView;
}
}
The reason for that behavior is that you are starting multiple threads.
FillLocations preExecute --> SHOW ProgressBar
BitmapWorkerTask_1 --> new thread
BitmapWorkerTask_2 --> new thread
...
BitmapWorkerTask_N --> new thread
FillLocations postExecute --> HIDE ProgressBar
BitmapWorkerTask_K --> continue execution
BitmapWorkerTask_K+1 --> continue execution
etc.
If you want the list to be displayed until it's all loaded, Simply make BitmapWorker's processing synchronous. If you still want to display the list right away but keep the spinner until it's all finished, then keep a counter in your activity and increase it in preexecute and decrease it in postExecute of BitmapWorker via a setter. Once the counter hits 0, remove hide the progressBar.
In activity:
private int asynchCounter = 0;
private void updateCounter(int delta){
asynchCounter+=delta;
if(asynchCounter<=0){
progress.setVisibility(View.GONE);
}else{
progress.setVisibility(View.VISIBLE);
}
}
And instead of BitmapWorkerTask use
class CountedBitmapWorkerTask extends BitmapWorkerTask {
protected void onPreExecute() {
super.onPreExecute();
updateCounter(1);
}
protected void onPostExecute(String msg) {
super.onPostExecute();
updateCounter(-1);
}
}
I had this exact problem, to solve it I had to write AsyncTask complete listener. Which sends a notification to UI thread, that data was loaded and it has to change something, in this case hide the ProgressBar.
This is the basic example of how this should look like. I am not sure this will work for you after you copy it to your project, but complete listener is what you need, so after studying this case you should be able to find a solution.
AsyncTaskCompleteListener.java - listener interface.
public interface AsyncTaskCompleteListener {
public void onTaskComplete();
}
LoadDataTask.java
class LoadDataTask extends AsyncTask<Object, Object, Object> {
/* Your object types according to your task. */
private AsyncTaskCompleteListener callback; // Callback field
public LoadDataTask(AsyncTaskCompleteListener cb){
this.callback = cb;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Object doInBackground(String... urls) {
/* Your task here */
return result;
}
#Override
protected void onPostExecute(Object o) {
callback.onTaskComplete(); // Set the Callback
}
}
MainActivity.java
public class MainActivity implements AsyncTaskCompleteListener{
/* ...Other methods and fields... */
/* onTaskComplete method which fires after your data is loaded. */
#Override
public void onTaskComplete(){
// Hide ProgressBar
}
}
Self Plug: https://github.com/horvste/EasyWebPageDownloadForAndroid
This would separate the threading from the implementation and solve your problem. This is very similar to what Tony suggested except it's already implemented for you.
Github Readme:
Good for connecting to REST API's, HTML parsing, and many other uses. Using this library is meant to be easy:
Create a class which implements OnProgressUpdate
public class SampleClass implements OnProgressUpdate {
#Override
public void onUpdate(Integer percentProgress) {
}
#Override
public void onUpdateFailure() {
}
#Override
public void onSuccess(StringBuilder result) {
}
#Override
public void onFailure() {
}
}
}
Instantiate DownloadWebPage object
DownloadWebPage webPage = new DownloadWebPage(new SampleClass(), myUrl);
Call .downloadHtml() from the DownloadWebPage
webPage.downloadHtml();
Also if the asynchtask is updating properly and the amount of items is to large look here:
listing a listview is taking too much time and memory in android
Another option would be to only list a certain amount of items then have a next page button or gesture to deal with the ListView loading too slow.