I am trying to populate recyclerview from an async task. and in Async task the data is fetched through API call and pushed to a SQLite table, then the list populates from the SQLite Table. The list fails to display on first load. But when I close the app and reopen the app, the list populates. Is this a general issue that need to be addressed properly or I am missing with something else?
public class KingsActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
RecyclerView.Adapter mAdapter;
RecyclerView.LayoutManager mLayoutManager;
List<King> kingList=new ArrayList<King>();
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("method_track","onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//To avoid the load on the main thread
new DownloadDataAsync().execute(this);
}
public class DownloadDataAsync extends AsyncTask<Context, Integer, Context> {
#Override
protected Context doInBackground(Context... ctx) {
downloadData(ctx[0]);
return ctx[0];
}
#Override
protected void onProgressUpdate(Integer... progress) {
}
#Override
protected void onPostExecute(Context result) {
Log.d("method_track","PostExecute");
Cursor kingsRows=DatabaseHelper.getInstance(result.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS);
kingsRows.moveToFirst();
while(kingsRows.moveToNext()){
String kingName=kingsRows.getString(kingsRows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_NAME));
int battleCount=kingsRows.getInt(kingsRows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
int rating=kingsRows.getInt(kingsRows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
kingList.add(new King(kingName,rating,battleCount));
}
kingsRows.close();
mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
mLayoutManager = new LinearLayoutManager(result);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new KingsAdapterRC(kingList,result);
mRecyclerView.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
}
public void downloadData(final Context ctx){
// Get a RequestQueue
RequestQueue queue = HttpRequestHandler.getInstance(ctx.getApplicationContext()).
getRequestQueue();
String url ="http://starlord.hackerearth.com/gotjson";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
//Log.d("result_check",response.substring(0,500));
loadToDb(response,ctx);
populateList(ctx);
calculateRating(ctx);
logRatings(ctx);
//mTextView.setText("Response is: "+ response.substring(0,500));
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("result_check","Error");
//mTextView.setText("That didn't work!");
}
});
// Add a request to RequestQueue.
HttpRequestHandler.getInstance(ctx).addToRequestQueue(stringRequest);
}
protected void loadToDb(String jsonResponse,Context ctx){
DatabaseHelper.getInstance(ctx.getApplicationContext()).truncate(DatabaseHelper.TABLE_WESTEROS_DATA);
try {
JSONArray jsonArray = new JSONArray(jsonResponse);
for(int i=0;i<jsonArray.length();i++){
JSONObject jsonObj = (JSONObject)jsonArray.get(i);
Iterator<String> iter = jsonObj.keys();
HashMap<String,String> fieldVales=new HashMap<String, String>();
while (iter.hasNext()) {
String key = iter.next();
try {
Object value = jsonObj.get(key);
fieldVales.put(key,value.toString());
} catch (JSONException e) {
// Something went wrong!
Log.e("loadTodb",e.toString());
}
}
DatabaseHelper.getInstance(ctx.getApplicationContext()).insert(DatabaseHelper.TABLE_WESTEROS_DATA,fieldVales);
}
} catch (Throwable t) {
//Log.e("My App", "Could not parse malformed JSON: \"" + json + "\"");
}
}
protected void populateList(Context ctx){
List<King> kingList=new ArrayList<King>();
String[] kings;
int kingsCount=0;
String sql="SELECT DISTINCT "+DatabaseHelper.KEY_WESTEROS_ATTACKER_KING+ " FROM "+ DatabaseHelper.TABLE_WESTEROS_DATA
+" WHERE "+ DatabaseHelper.KEY_WESTEROS_ATTACKER_KING +" <> ''";
Cursor rows=DatabaseHelper.getInstance(ctx.getApplicationContext()).getReadableDatabase().rawQuery(sql, null);
kingsCount+=rows.getCount();
String sql2="SELECT DISTINCT "+DatabaseHelper.KEY_WESTEROS_DEFENDER_KING + " FROM "+ DatabaseHelper.TABLE_WESTEROS_DATA
+ " WHERE "+ DatabaseHelper.KEY_WESTEROS_DEFENDER_KING + " NOT IN ( "+sql+ " ) AND "
+ DatabaseHelper.KEY_WESTEROS_DEFENDER_KING +" <> ''";
Cursor rows2=DatabaseHelper.getInstance(ctx.getApplicationContext()).getReadableDatabase().rawQuery(sql2, null);
kingsCount+=rows2.getCount();
kings=new String[kingsCount];
int i=0;
if (rows.moveToFirst()) {
while (!rows.isAfterLast()) {
//your code to implement
kings[i]=rows.getString(rows.getColumnIndex(DatabaseHelper.KEY_WESTEROS_ATTACKER_KING));
i++;
rows.moveToNext();
}
}
rows.close();
if (rows2.moveToFirst()) {
while (!rows2.isAfterLast()) {
//your code to implement
kings[i]=rows2.getString(rows2.getColumnIndex(DatabaseHelper.KEY_WESTEROS_DEFENDER_KING));
i++;
rows2.moveToNext();
}
}
rows2.close();
DatabaseHelper.getInstance(ctx.getApplicationContext()).truncate(DatabaseHelper.TABLE_WESTEROS_KINGS);
for(i=0;i<kingsCount;i++){
HashMap<String,String> fieldValues=new HashMap<String, String>();
fieldValues.put(DatabaseHelper.KEY_WESTEROS_KINGS_NAME,kings[i]);
fieldValues.put(DatabaseHelper.KEY_WESTEROS_KINGS_RATING,"400");
fieldValues.put(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT,"0");
DatabaseHelper.getInstance(ctx.getApplicationContext()).insert(DatabaseHelper.TABLE_WESTEROS_KINGS,fieldValues);
}
}
protected void calculateRating(Context ctx){
Cursor battles_cur=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_DATA);
if(battles_cur.moveToFirst()){
while(!battles_cur.isAfterLast()){
String attackingKing=battles_cur.getString(battles_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_ATTACKER_KING));
String defendingKing=battles_cur.getString(battles_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_DEFENDER_KING));
if(!attackingKing.equals("") && !defendingKing.equals("")){
HashMap<String,String> whereConDfk=new HashMap<String,String>();
whereConDfk.put(DatabaseHelper.KEY_WESTEROS_KINGS_NAME,defendingKing);
Cursor cursor1=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS,whereConDfk);
double defKing_rating;
double defKing_battleCount;
if(cursor1.moveToFirst()){
defKing_rating=cursor1.getDouble(cursor1.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
defKing_battleCount=cursor1.getDouble(cursor1.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
cursor1.close();
HashMap<String,String> whereConAtk=new HashMap<String,String>();
whereConAtk.put(DatabaseHelper.KEY_WESTEROS_KINGS_NAME,attackingKing);
Cursor cursor2=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS,whereConAtk);
Double atkKing_rating;
Double atkKing_battleCount;
if(cursor2.moveToFirst()){
atkKing_rating=cursor2.getDouble(cursor2.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
atkKing_battleCount=cursor2.getDouble(cursor2.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
cursor2.close();
atkKing_battleCount++;
defKing_battleCount++;
Double defKing_rating_tr=Math.pow(10,(defKing_rating/400));
Double atkKing_rating_tr=Math.pow(10,(atkKing_rating/400));
Double defKing_rating_ex=defKing_rating_tr/(defKing_rating_tr+atkKing_rating_tr);
Double atkKing_rating_ex=atkKing_rating_tr/(defKing_rating_tr+atkKing_rating_tr);
String attackerStatus=battles_cur.getString(battles_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_ATTACKER_OUTCOME));
Double atkKing_rating_new=atkKing_rating;
Double defKing_rating_new=defKing_rating;
if(attackerStatus.equals("win")){
atkKing_rating_new=atkKing_rating+(32*(1-atkKing_rating_ex));
defKing_rating_new=defKing_rating+(32*(0-defKing_rating_ex));
}else if(attackerStatus.equals("loss")){
atkKing_rating_new=atkKing_rating+(32*(0-atkKing_rating_ex));
defKing_rating_new=defKing_rating+(32*(1-defKing_rating_ex));
}else if(attackerStatus.equals("draw")){
atkKing_rating_new=atkKing_rating+(32*(0.5-atkKing_rating_ex));
defKing_rating_new=defKing_rating+(32*(0.5-defKing_rating_ex));
}
String update_atkKing_ratingQuery="UPDATE "+ DatabaseHelper.TABLE_WESTEROS_KINGS + " SET "
+ DatabaseHelper.KEY_WESTEROS_KINGS_RATING+" = "+atkKing_rating_new+", "
+ DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT+" = "+atkKing_battleCount
+ " WHERE "+ DatabaseHelper.KEY_WESTEROS_KINGS_NAME +" =\""+attackingKing+"\"";
String update_defKing_ratingQuery="UPDATE "+ DatabaseHelper.TABLE_WESTEROS_KINGS + " SET "
+ DatabaseHelper.KEY_WESTEROS_KINGS_RATING+" = "+defKing_rating_new+", "
+ DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT+" = "+defKing_battleCount
+ " WHERE "+ DatabaseHelper.KEY_WESTEROS_KINGS_NAME +" =\""+defendingKing+"\"";
DatabaseHelper.getInstance(ctx.getApplicationContext()).getWritableDatabase().execSQL(update_atkKing_ratingQuery);
DatabaseHelper.getInstance(ctx.getApplicationContext()).getWritableDatabase().execSQL(update_defKing_ratingQuery);
}
}
}
battles_cur.moveToNext();
}
}
}
protected void logRatings(Context ctx){
Log.d("method_track","logratings");
Cursor kings_cur=DatabaseHelper.getInstance(ctx.getApplicationContext()).getData(DatabaseHelper.TABLE_WESTEROS_KINGS);
try {
while (kings_cur.moveToNext()) {
String name=kings_cur.getString(kings_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_NAME));
String rating=kings_cur.getString(kings_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_RATING));
String battleCount=kings_cur.getString(kings_cur.getColumnIndex(DatabaseHelper.KEY_WESTEROS_KINGS_BATTLE_COUNT));
//Log.d("method_track", "logratings");
Log.d("rating_inspect",name+" - "+rating+" - "+battleCount);
}
}finally {
kings_cur.close();
}
}
}
}
Here:
downloadData(ctx[0]);
Problem caused by downloadData method.
In downloadData method using StringRequestto get data from server. StringRequest process all requests on worker Thread and return result back using Response.Listener.
doInBackground also done all work on worker Thread. so doInBackground method's Work Thread just executing downloadData method without waiting to get response from StringRequest.
No need to use extra Thread when using StringRequest. just remove AsyncTask and use only StringRequest to get it work properly.
Yo should not call volley StringRequest from doInBackground because async task immediately shot the post execute method and your list will be empty so that it won't show any data. Simply use string request and after getting response save it in your 'database' and fetch from there(you can save and fetch the data from database directly or using async task).
For not just use StringRequest without any async task and test it.
Related
I am building an android app that displays the COVID19 statistics for India, I am getting the stats in JSON format from https://api.covid19india.org/data.json , this API contains data of individual states too,
Below is the snip of Json array(contains json objects representing each state) that i am requesting
as of Now i am displaying the entire data ( all states ) at a time on my screen, However i want to give the state name as the input and display the stats of only that state For eg. in the below image in place of sample i want to write a state name and the stats of that state must be displayed on click of the button.
Here is the code of mainActivity.java, I am using Volley Library for fetching data from API
public class MainActivity extends AppCompatActivity {
private TextView result;
private RequestQueue mq;
public String value;
int flag = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
result = findViewById(R.id.textView4);
Button parse = findViewById(R.id.button);
mq = Volley.newRequestQueue(this);
EditText text = (EditText)findViewById(R.id.state_ip);
value = text.getText().toString();
parse.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
jsonParse(value);
**//How do i pass 'value' i.e the state name entered by user to jsonParse**
}
});
}
private void jsonParse(final String value) {
Log.d("val_state",value);
String url = "https://api.covid19india.org/data.json";
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonArray = response.getJSONArray("statewise");
for (int i = 0; i < jsonArray.length(); i++)
{
JSONObject st = jsonArray.getJSONObject(i);
String statename = st.getString("state");
String active = st.getString("active");
String confirmed = st.getString("confirmed");
String deaths = st.getString("deaths");
String recovered = st.getString("recovered");
if(statename.equals(value))
{
flag= 1;
}
statename = "State : " + statename;
active = "Active Cases : " + active;
confirmed = "Confirmed Cases : " + confirmed;
deaths = "Total Deaths : " + deaths;
recovered = "Total Recovered : " + recovered;
if(flag==1)
{
flag=0;
result.append(statename + "\n" + String.valueOf(active) + "\n" + String.valueOf(confirmed) + "\n" + String.valueOf(deaths) + "\n" + String.valueOf(recovered) + "\n\n\n");
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
mq.add(request);
}
}
Here , i want to pass the value of state entered by the user to the method jsonParse() so that i check the state name with the received JSON data and append it to the TextView, but when i do this , and try to log the value inside the jsonParse() method i get nothing, why is this happening , How do i implement the above ?
Your EditText value is update and has to be captured after the button is clicked.
parse.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
value = text.getText().toString();
jsonParse(value);
}
});
I want to execute taking data from JSON as shown below. But when
Toast.makeText(this, MangIDtrailer.size () + "..... check size of Array IDtrailer .....", Toast.LENGTH_LONG).show();
it returns 0.
I don't know what the cause is.
public class Main2Activity extends AppCompatActivity {
ListView Listmovie;
ArrayList<String> MangIDtrailer;
public static ArrayList<InfoMovie> inforMovieArrayList;
AdapterMovie adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
BottomNavigationView navView = findViewById(R.id.nav_view);
navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
String url1 ="http://the....ying";
inforMovieArrayList = new ArrayList<>();
MangIDtrailer = new ArrayList<>();
MangIDtrailer = GetIDMovie(url1);
inforMovieArrayList = DataMovie(MangIDtrailer);
Listmovie = (ListView) findViewById(R.id.ListMovie);
adapter = new AdapterMovie(this, R.layout.movielist, inforMovieArrayList);
Listmovie.setAdapter(adapter);
Listmovie.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(Main2Activity.this,Review_Movie.class);
intent.putExtra("IDmovie",i);
//Toast.makeText(MainActivity.this, ""+i, Toast.LENGTH_SHORT).show();
startActivity(intent);
}
});
}
public ArrayList<String> GetIDMovie (String Url) {
final ArrayList<String> ArrayID = new ArrayList<>();
final RequestQueue queue = Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, Url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
String IDTrailer;
JSONArray jsonArrayFreeMovies = response.getJSONArray("FreeMovies");
for (int i=0; i < jsonArrayFreeMovies.length(); i++) {
JSONObject jsonObjectFreeMovies = jsonArrayFreeMovies.getJSONObject(i);
IDTrailer = jsonObjectFreeMovies.getString("trailer_id");
ArrayID.add(IDTrailer);
Toast.makeText(Main2Activity.this, i+"************", Toast.LENGTH_SHORT).show();
}
Toast.makeText(Main2Activity.this, MangIDtrailer.get(2)+"check Data ", Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
queue.add(jsonObjectRequest);
queue.cancelAll(jsonObjectRequest);
return ArrayID;
}
public ArrayList <InfoMovie> DataMovie (ArrayList<String> MangIDtrailer) {
final ArrayList<InfoMovie> inforMovieArray = new ArrayList<>();
final String linkDetail = "http://tk/api/trailers/movDetail?trailer_id=";
final RequestQueue queue2 = Volley.newRequestQueue(this);
//////////////Check that MangIDtrailer.size () has no data////////////////////////////////////
Toast.makeText(this, MangIDtrailer.size()+".....check size of Array IDtrailer .....",Toast.LENGTH_LONG).show();
for (int i=0; i<MangIDtrailer.size(); i++) {
JsonObjectRequest jsonObjectRequest2 = new JsonObjectRequest(Request.Method.GET, linkDetail + MangIDtrailer.get(i) + "&test_fullVer=1", null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
String linkposter, linkbackdrop, namemovie, overviewmovie, Release_date, Urltrailer;
Float Vote_average;
String linkHot = "https://image.tmdb.org/t/p/w500/";
JSONObject jsonObjectInfo = null, jsonObjectMore = null;
JSONObject jsonopFreeMovies1 = response.getJSONObject("FreeMovies");
if (jsonopFreeMovies1.has("FreeMovies")) {
//Toast.makeText(MainActivity.this, "Cos ", Toast.LENGTH_SHORT).show();
JSONObject jsonObjectFreeMovies2 = jsonopFreeMovies1.getJSONObject("FreeMovies");
jsonObjectInfo = jsonObjectFreeMovies2.getJSONObject("Info");
jsonObjectMore = jsonObjectFreeMovies2.getJSONObject("More");
} else {
//Toast.makeText(MainActivity.this, "Khoong cos", Toast.LENGTH_SHORT).show();
jsonObjectInfo = jsonopFreeMovies1.getJSONObject("Info");
jsonObjectMore = jsonopFreeMovies1.getJSONObject("More");
}
namemovie = jsonObjectInfo.getString("title");
Urltrailer = jsonObjectInfo.getString("trailer_urls");
linkposter = linkHot + jsonObjectInfo.getString("thumbnail");
overviewmovie = jsonObjectMore.getString("overview");
linkbackdrop = linkHot + jsonObjectMore.getString("backdrop_path");
Release_date = jsonObjectMore.getString("release_date");
Vote_average = Float.valueOf(jsonObjectMore.getLong("vote_average"));
inforMovieArray.add(new InfoMovie(namemovie, overviewmovie, linkposter, linkbackdrop, Vote_average, Release_date));
Toast.makeText(Main2Activity.this,namemovie + "-" + overviewmovie + "-" + Vote_average, Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
Toast.makeText(Main2Activity.this, "Lỗi", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(Main2Activity.this, "Lỗi Try catch", Toast.LENGTH_SHORT).show();
}
});
queue2.add(jsonObjectRequest2);
}
return inforMovieArray;
}
}
As you suggested
Toast.makeText(this, MangIDtrailer.size()+".....check size of Array IDtrailer .....",Toast.LENGTH_LONG).show();
This is where you are getting size zero, which is absolutely true, because you have only initialized your array MangIDtrailer and it is an empty array. Your function GetIDMovie(url1); has a loop which populates your MangIDtrailer array which is below where you have called the toast. So your array is empty and thus its size returns zero.
One handy tip for you, you should name your functions in camelCase with first letter of your word in lowercase. GetIDMovie(url1) seems more like a class constructor. :)
EDIT:
The above solves your initial problem.
To fully solve your problem, you have to understand that Network Operations are asynchronous, meaning they will execute after sometime or they may return no value at all depending on various conditions, like network bandwidth, your server state, the parameters passed to your HTTP requests, etc.
You have two network calls in your above code; in functions: GetIDMovie() and DataMovie(). The second function requires an array of IDs which is only available if your first request is complete and returns an array of ids. So what you would want to do is, only after you get the array of ids ie. in onResponse of GetIDMovie() after the for loop, you should make a call to DataMovie().
This however is really ugly solution. I hope you will research further for better solution.
I have an activity that calls JSON data from a foreign database.
Below is my ideal case for my app:
The JSON data is parsed and inserted into an SQLite database on Android
Next activity is started and the newly inserted data is read from the SQLite database
What actually happens:
The JSON data is parsed and inserted into an SQLite database on Android
The next activity is started while data is still being inserted and returns zero when reading from the desired databse for my ListArray in that activity.
How do I force Android to wait until database insertion is completed before starting the next activity?
EDIT
My doInBackground looks as follows:
#Override
protected String doInBackground(String... params) {
StringRequest strReq = new StringRequest(Request.Method.GET,
str, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
// Check for error node in json
if (!error) {
JSONArray jObjInside = jObj.getJSONArray("service_prov_services");
for (int i = 0; i < jObjInside.length(); i++) {
// Now store the user in SQLite
try {
// JSONObject user = jObj.getJSONObject("user");
String service_prov_type = jObj.getString("service_prov_type");
String service_prov_name = jObj.getString("service_prov_name");
String addr_street = jObj.getString("addr_street");
String addr_num = jObj.getString("addr_number");
String addr_plz = jObj.getString("addr_plz");
String addr_city = jObj.getString("addr_city");
JSONObject elem = jObjInside.getJSONObject(i);
if(elem != null){
String service_id = elem.getString("service_id");
String service_type = elem.getString("service_type");
String service_measure = elem.getString("service_measure");
// Inserting row in userServiceProvServices table
db.addUserServiceProvServices(service_id, service_prov_type,
service_prov_name, addr_street, addr_num, addr_plz, addr_city, service_type, service_measure);
Log.d("post_url for service", addr_plz );
}
} catch (JSONException e) {
// JSON error
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(), "Json error: " +
e.getMessage(), Toast.LENGTH_LONG).show();
}
}
} else {
// Error in login. Get the error message
String errorMsg = jObj.getString("error_msg");
Toast.makeText(getActivity().getApplicationContext(),
errorMsg, Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
// JSON error
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(), "Json error: " +
e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Login Error: " + error.getMessage());
Toast.makeText(getActivity().getApplicationContext(),
error.getMessage(), Toast.LENGTH_LONG).show();
}
});
Log.d("test string to appcntr",strReq.toString());
// Adding request to request queue
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
return params[0];
}
onPostExecute looks as follows:
#Override
protected void onPostExecute(String Result) {
//super.onPostExecute(Result);
pdLoading.dismiss();
//this method will be running on UI thread
Log.d(TAG, "Stamp: " + Result);
Bundle args = new Bundle();
args.putString("stampID", Result);
ProviderServiceListFragment frag = new ProviderServiceListFragment();
frag.setArguments(args);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame,
frag)
.commit();
}
With the way I am doing it now, my next Fragment is already called, although the data has not finished being entered into the database. This means the ListArray in the follwoing Fragment is empty because of the missing database data.
I worked on this for a month and finally figured it out for myself (stupid nube I am..) So here is a piece of code inserting a record to sqlite.
On the chosen event ("onClick actionbutton1") a new AsyncTask is created with doInBackground, onPreExecute and onPostExecute.
onPreExecute will setMessage() and show() the progressDialog which will start spinning
onPostExecute will handle the new/next Activity
READ BELOW FOR doInBackground!!
actionButton1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final ProgressDialog progressDialog = new ProgressDialog(AddUpdateEvf.this);
new AsyncTask<Void, Void, Boolean>() {
protected Boolean doInBackground(Void... params) {
doOneThing();
return null;
}
#Override
protected void onPreExecute() {
progressDialog.setMessage("Processing...");
progressDialog.show();
}
protected void onPostExecute(Boolean result) {
evaluationFormOps.close();
progressDialog.dismiss();
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AddUpdateEvf.this);
alertDialogBuilder.setMessage("Added to Database...")
.setCancelable(false)
.setPositiveButton("Continue", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
i = new Intent(AddUpdateEvf.this, ViewProduct.class);
i.putExtra(EXTRA_ADD_UPDATE, "View");
i.putExtra(EXTRA_PRODUCT_ID, hiddenTextId.getText().toString());
i.putExtra(EXTRA_PRODUCT_NO, productNo_tv.toString());
startActivity(i);
dialog.dismiss();
finish();
}
});
AlertDialog alert = alertDialogBuilder.create();
alert.show();
}
private void doOneThing() {
makeDbRequest();
do {
evfId = newEvf.getEvfId();
}
while (evfId<1);
}
}.execute();
}
});
Note this snippet in the above code called in doInBackground():
private void doOneThing() {
makeDbRequest();
do {
evfId = newEvf.getEvfId();
}
while (evfId<1);
}
Note: The makeDbRequest() handles the insert to sqlite by setting the values and then passing it to another class which handles the cursor and puts the values etc.
Heres a small snippet of relevant code in that class (which you should already have mastered...):
public Evf addEvf(Evf evf, String dBsuccess){
ContentValues values = new ContentValues();
values.put(TableHelper.PRODUCT_IDE,evf.getPRODUCTId());
values.put(TableHelper.CSCORE,evf.getcScore());
values.put(TableHelper.FSCORE,evf.getfScore());
values.put(TableHelper.TSCORE,evf.gettScore());
values.put(TableHelper.WEIGHT,evf.getWeight());
values.put(TableHelper.TEMP,evf.getTemp());
values.put(TableHelper.STATUS,evf.getStatus());
values.put(TableHelper.TIMESTAMP, String.valueOf(evf.getTimeStamp()));
values.put(TableHelper.LOADED, dBsuccess);
long insertid = database.insert(TableHelper.TABLE_EVFS,null,values);
evf.setEvfId((int) insertid);
return evf;
}
So above you can see the Id of, in my case evaluationform(Evf), being set to the insert id. This happens after the insert and you can set any value in your object class (the one with getters and setters...Evf())
Finally, use the do...while statement above to "listen" for the value being set in the object class
This can obviously only happen if the insert was finished and the onPosteExecute takes care of the rest
Hope it helps, crit is welcome, PEACHES!!
Use AsyncTask to process the Database insertion process & then use the onPostExecute method to move away from the current activity.
private class ProcessDatabase extends AsyncTask<String, String, String> {
String sampleData;
#Override
protected String doInBackground(String... params) {
//Call your Database Insert method here.
//In this example, I am inserting sampleData to the DB
return null;
}
#Override
protected void onPostExecute(String result) {
//This gets triggered when the process is complete
}
}
You can start the AsyncTask by adding the following code in your onCreate or where ever you want to start the DB Insertion process:
//in this case I am just passing a string, You can create your own
//custom class & send that as well
ProcessDatabase.execute(myData);
Refer this link for more information. Good luck!
The StringRequest is an Asynchronous request, so upon the executing the those lines onPostExecute will called immediately, so there is no guarantee that the sql update will complete before the next activity is launched.
Call the nextActivity at the end of the onResponse callback method of the StringRequest which way you can guarantee to insert the data to db first and then call the nextActivity.
private void makeJsonRequest(String str) {
StringRequest strReq = new StringRequest(Request.Method.GET,
str, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
// Check for error node in json
if (!error) {
JSONArray jObjInside = jObj.getJSONArray("service_prov_services");
for (int i = 0; i < jObjInside.length(); i++) {
// Now store the user in SQLite
try {
// JSONObject user = jObj.getJSONObject("user");
String service_prov_type = jObj.getString("service_prov_type");
String service_prov_name = jObj.getString("service_prov_name");
String addr_street = jObj.getString("addr_street");
String addr_num = jObj.getString("addr_number");
String addr_plz = jObj.getString("addr_plz");
String addr_city = jObj.getString("addr_city");
JSONObject elem = jObjInside.getJSONObject(i);
if (elem != null) {
String service_id = elem.getString("service_id");
String service_type = elem.getString("service_type");
String service_measure = elem.getString("service_measure");
// Inserting row in userServiceProvServices table
db.addUserServiceProvServices(service_id, service_prov_type,
service_prov_name, addr_street, addr_num, addr_plz, addr_city, service_type, service_measure);
Log.d("post_url for service", addr_plz);
}
} catch (JSONException e) {
// JSON error
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(), "Json error: " +
e.getMessage(), Toast.LENGTH_LONG).show();
}
}
goNextActivity();
} else {
// Error in login. Get the error message
String errorMsg = jObj.getString("error_msg");
Toast.makeText(getActivity().getApplicationContext(),
errorMsg, Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
// JSON error
e.printStackTrace();
Toast.makeText(getActivity().getApplicationContext(), "Json error: " +
e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Login Error: " + error.getMessage());
Toast.makeText(getActivity().getApplicationContext(),
error.getMessage(), Toast.LENGTH_LONG).show();
}
});
Log.d("test string to appcntr", strReq.toString());
// Adding request to request queue
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
}
private void goNextActivity(){
//this method will be running on UI thread
ProviderServiceListFragment frag = new ProviderServiceListFragment();
frag.setArguments(args);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame,
frag)
.commit();
}
if (isConnected()) {
Event eInstance = new Event();
theEvents = eInstance.downloadEvents(eventsNightlife, getActivity());
rAdapter = new RecyclerAdapter(theEvents);
recyclerView.setAdapter(rAdapter);
progrsBar.setVisibility(View.GONE);
....
This is part of the code that runs at "onCreateView". The method downloadEvents uses Volley to download JSON data, extract it and return a list of items (theEvents). Now when my app starts, the recycler view is empty. If I go to my home screen out of the app and then run my app again, this time the data sometimes gets downloaded.
I debugged step by step, and at first launch (i mean when the app is not just resuming), theEvents is empty, so the download didn't return or manage to return anything...
Suggestions on how to execute things before the UI has been shown to the user or what actually needs to be done to approach this task better?
Also, I use a swipeRefreshLayout and at its onRefresh method I do:
public void onRefresh() {
Event eInstance = new Event();
theEvents = eInstance.downloadEvents(eventsNightlife, getActivity());
rAdapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
but it doesn't work. I also tried to
rAdapter = new RecyclerAdapter(theEvents);
rAdapter.notifyDataSetChanged();
recyclerView.swapAdapter(rAdapter, false);
still not working.
EDIT: My downloadEvents method implementing Volley:
public List<Event> downloadEvents(String urlService, Context context) {
eventsList = new ArrayList<>();
RequestQueue requestQueue = Volley.newRequestQueue(context);
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest
(Request.Method.GET, urlService, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
String durationStr = null;
for (int i = 0; i < response.length(); i++) {
JSONObject eventJson = response.getJSONObject(i);
String title = eventJson.getString("EventTitle");
String body = eventJson.getString("EventBody");
String date = eventJson.getString("EventDate");
String time = eventJson.getString("EventTime");
int duration = Integer.parseInt(eventJson.getString("EventDuration"));
if (duration > 60) {
durationStr = "Duration: " + duration / 60 + " h";
} else if (duration < 60) {
durationStr = "Duration: " + duration + " m";
}
String place = eventJson.getString("EventPlace");
String organ = eventJson.getString("Organization");
Event event = new Event(title, body, date, time, durationStr, place, organ);
eventsList.add(event);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("VOLLEY ERROR", "" + error);
}
}
);
requestQueue.add(jsonArrayRequest);
return eventsList;
}
You can use EventBus for your purpose that is a simple and truth way.
Here, i write an example for how to use EventBus with volley.
Consider that i want to download some data.
This is the class that my download methods is inside it (you can add more methods to it in the future):
Im used volley to download my data:
// Download methods is inside volley
public class MyDownloader{
public static void downloadData(){
DownloadDataEvent dlDataEvent=new DownloadDataEvent();
List<String> myResult=new ArrayList<>();
...
#Override
public void onResponse(JSONArray response) {
super.onResponse(response);
if(respone!=null){
// Do what i want with my received data
dlDataEvent.setData(response);
}
// Post my event by EventBus
EventBus.getDefault().post(dlDataEvent);
...
}
}
}
This is my event:
public class DownloadDataEvent{
private JSONArray mData;
public void setData(JSONArray data){
mData=data;
}
public JSONArray setData(){
return mData;
}
}
Now i want to use my downloadData() method inside my MainActivity:
(I called my downloadData method inside onCreate.)
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
// I have to register this class for EventBus subscriber:
if(!EventBus.getDefault().isRegister(this)){
EventBus.getDefault().registerSticky(this);
}
// Call my downloadData method
if(isConnected()){
MyDownloader.downloadData();
}
}
// And for receive the data through EventBus, i have to create a
// method (subscriber) in this template:
public void onEventMainThread(DownloadDataEvent downloadDataEvent){
JSONArray result=downloadDataEvent.getData();
// Do what i want with my received data
}
}
you can create more than one subscriber every where you want to use received data.
I passed JSONArray to my DownloadDataEvent that it is not good. you can deserialize your received data and pass it to your DownloadDataEvent.
I used Volley to download data
Maybe my descriptions were confusing, but EventBus is a well-known library and is very easy to use.
I've created this code to access user from my database for Login purpose. I can access the object 'st' when I'm inside OnResponse method but when I try to return return the object, it gives me null. Also when I try to access this st object before returning, it gives NullPointerException. What is the exact problem?
public class ServerRequests {
ProgressDialog progressDialog;
public static user_Student st;
public static final int CONNECTION_TIMEOUT = 1000 * 15;
public static final String SERVER_ADDRESS = "http://prem-pc:8989/";
Context ct;
public ServerRequests(Context context) {
ct = context;
progressDialog = new ProgressDialog(context);
progressDialog.setCancelable(false);
progressDialog.setTitle("Processing");
progressDialog.setMessage("Please Wait....");
}
public ServerRequests() {
}
public user_Student fetchUserDataInBackground(user_Student user) {
progressDialog.show();
Toast.makeText(ct, "Data in background: ", Toast.LENGTH_SHORT).show();
user_Student ust = doInBackground(user);
progressDialog.dismiss();
return ust;
}
public user_Student doInBackground(user_Student user) {
String URL = SERVER_ADDRESS + "connect.php?prn=" + user.prn + "&password=" + user.password;
RequestQueue req = Volley.newRequestQueue(ct);
Toast.makeText(ct, "Do in Background", Toast.LENGTH_SHORT).show();
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET, URL, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject jObject) {
try {
// Parsing json object response
// response will be a json object
if (jObject.length() == 0) {
st = null;
Toast.makeText(ct, "Null JSON Object", Toast.LENGTH_SHORT).show();
} else {
String prn = jObject.getString("prn");
String fname = jObject.getString("fname");
String mname = jObject.getString("mname");
String lname = jObject.getString("lname");
String clas = jObject.getString("clas");
String dept = jObject.getString("dept");
String batch = jObject.getString("batch");
String scontact = jObject.getString("scontact");
String pcontact = jObject.getString("pcontact");
String email = jObject.getString("email");
String password = jObject.getString("password");
String dob = jObject.getString("dob");
st = new user_Student(prn, fname, mname, lname, clas, dept, batch, scontact, pcontact, email, password, dob);
Toast.makeText(ct, "JSON Object:" + st.fname, Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(ct, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(ct, error.getMessage(), Toast.LENGTH_SHORT).show(); // hide the progress dialog
}
});
req.add(jsonObjReq);
//Toast.makeText(ct,"DO in back End"+st.fname,Toast.LENGTH_SHORT).show();
return st;
}
}
You can't return from anonymous inner classes, but you could create a method inside ServerRequests that takes a user_Student as a parameter and call that method from within onResponse. This method could then do whatever you need.
You must use AsyncTask to do funtion doInBackground(user_Student user)
You can view this post to understand AsyncTask:
How to use AsyncTask correctly in Android