I set my ProfileActivity to extend to AppCompatActivity implementing View.OnClickListener. When the assigned button is clicked,it sends a StringRequest to a url using Volley getting a JSON format and parse it then store it in another class. The data stored is needed on the next activity that is started onClick which is myTabActivity. I need to make the ProfileActivity wait until the request, parsing, and storing is done in the ParseWorkOrderJSON.java before starting myTabActivity. Any thoughts?
I am fairly new to Android too.
Here are snippets from my code:
public class ProfileActivity extends AppCompatActivity implements View.OnClickListener {
public static final String JSON_URL = "/*I ommitted my URL on purpose here*/";
private TextView username_txt;
private Button fetch_data_btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
username_txt = (TextView)findViewById(R.id.txt_username);
fetch_data_btn = (Button)findViewById(R.id.btn_fetch_data);
SharedPreferences sharedPreferences = getSharedPreferences(Config.SHARED_PREF_NAME, Context.MODE_PRIVATE);
String username = sharedPreferences.getString(Config.USERNAME_SHARED_PREF,"Not Available");
username_txt.setText(username);
sendRequest();
fetch_data_btn.setOnClickListener(this);
}
private void sendRequest(){
final ProgressDialog loading = ProgressDialog.show(this,"Fetching data.","Please wait...", false,false);
StringRequest stringRequest = new StringRequest(Request.Method.GET,JSON_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
System.out.println(response.substring(0));
showJSON(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("Something went wrong!");
Toast.makeText(ProfileActivity.this, "Something went wrong!", Toast.LENGTH_LONG).show();
}
});
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
private void showJSON(String json){
ParseWorkOrderJSON pwoj = new ParseWorkOrderJSON();
pwoj.parseWorkOrderJSON(json);
}
#Override
public void onClick(View v){
sendRequest();
Intent intent = new Intent(ProfileActivity.this, MytabActivity.class);
startActivity(intent);
}
}
I am trying to use ProgressDialog while the request, parsing, and storing is happening then dismiss it when all is done. Only then the next activity is loaded which is myTabActivity.
If you would like a snippet of ParseWorkOrderJSON please say so in the comments.
You don't need to slow down your app. You just need a better understanding of how asynchronous operation actually function.
Some operations (like HTTP request) may take a lot of time. If you do them on the main application thread (that is the thread that is in charge of updating and drawing the UI) your users may experience a frozen UI, irresponsive behaviour and all other bar stuff. In fact, Android has a build - in mechanism to prevent against this and, if an app takes too long to process a user input, it will try and force close it (the dreaded ANR dialog).
The way to deal with this is to offload those heavy operations aways from the main thread. Then you have another problem - you need to know when the operations finishes and react accordingly. This is called an async operation - it starts and returns a result in some moment in the future.
You are using Volley - a library, that does all this work for you - it creates a worker thread and uses it to execute HTTP requests, providing you with a callback to get notified when the request is complete.
The problem is that you are starting your activity when you are starting the Volley request. At this point in time there is still no data loaded, and nothing will get displayed. You should wait for the completion of the request, before continuing. How to know when the request is finished - it's when the onResponse() method is called. To have everything working, you should move your code to start your second activity there. That way you will be sure that the data has been loaded and you can proceed to show it to the user.
Related
I'm using Volley for Android. I have a ListView in a fragment. If this ListView is empty (only possible if the connection failed/no internet/etc.), I want to send a GET request to the server for the data, then populate the ListView accordingly if it succeeds. If the call failed, I want to call it again in 5 minutes. This goes on until it succeeds.
What is the best way to achieve this? I'm new to Android development. I read about Services, but IDK if that is overkill.
You could use ScheduledExecutorService to manage and schedule your request.
Take a look at:
http://tutorials.jenkov.com/java-util-concurrent/scheduledexecutorservice.html
http://developer.android.com/reference/java/util/concurrent/ScheduledExecutorService.html
I use to have a layer to define all my calls to services. Lets say ServiceLayer.java for example.
You could define a Handler as a global variable. (You will need to create the ServiceLayer in the MainThread). And then manage the error in the service call making the handler recall the service in 5 minutes. Something like this
public class ServiceLayer {
Handler handler = new Handler();
...
public void callToService(final String parameter,final String moreParameters,final Callback callbackDefinedByYou){
StringRequest req = new StringRequest(Method.GET, url, new Response.Listener<String>(){
#Override
public void onResponse(String s) {
//Do whatever you need, populate listviews etc
callbackDefinedByYou.populateListView(s);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
//Manage the error and recall again this service
callbackDefinedByYou.onError(volleyError);
handler.postDelayed(new Runnable() {
public void run(){
callToService(parameter, moreParameter, callbackDefinedByYou);
}
}, 300000); //5 minutes
}
});
VolleyHelper.addRequestToQueue(req);
}
In this code, everytime service fails a recall is made but, in some cases you should stop doing net calls. For example when you detect there is no internet conexion, and let the user refresh screen
I have fews questions about RoboSpice (advanced usages).
Note: I use RoboSpice with OrmLite.
My Android application is composed of two main activities: the first one is SplashActivity (start on launch) and the second is MainActivity (lauched 5s after the Splash Screen). I perform 2 requests on splash:
SplashActivity
public class SplashActivity extends BaseActivity {
private fooRequest fooRequest;
private barRequest barRequest;
private exampleRequest exampleRequest;
private NewsRequest newsRequest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
fooRequest = new fooRequest();
barRequest = new barRequest();
...
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Launch MainActivity...
}
}, 5000);
}
#Override
protected void onStart() {
super.onStart();
getSpiceManager().execute(fooRequest, new Integer(0), DurationInMillis.ONE_DAY, new fooRequestListener());
getSpiceManager().execute(barRequest, new Integer(1), DurationInMillis.ONE_DAY, new barRequestListener());
}
public final class fooRequestListener implements RequestListener<Foo> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Foo request failure");
}
#Override
public void onRequestSuccess(final Foo result) {
}
}
public final class barRequestListener implements RequestListener<Bar> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Bar request failure");
}
#Override
public void onRequestSuccess(final Bar result) {
}
}
}
The problem
My application logic is not really reliable: we can not be sure that requests are finished when MainAcitivty is launched. On the MainActivity I query my database with OrmLite for fetch some data and display them. So if request started on SplashActivity are not finished, my View display nothing.
Questions
1) I think that I need to add a listener to my pending requests (if such a request exists). On the RoboSpice Wiki, it said to use spiceManager.addListenerToPendingRequest. I have not managed to put it out, despite my tests. Maybe I'm doing something wrong? Can you give me code example? Resolved (see below)
2) Currently, the user is still waiting 5 seconds (timer Splash) before arriving at the home screen. How to check if data are in cache? With spiceManager.getDataFromCache() (it takes into account the expiration duration?).
3) What is the best retry policy to failed requests (a. at the first launched if database is not again created; b. if the database exists but data are expired)?
Edit
Question #1 resolved (I make a mistake in my original code) - #2 and #3 still relevant. Here's what to do (if it can help someone ...):
public class MainActivity extends BaseActivity {
private SpiceManager spiceManager = new SpiceManager(CalendarSpiceService.class);
...
#Override
protected void onStart() {
super.onStart();
spiceManager.start(this);
spiceManager.addListenerIfPending(Foo.class, new Integer(0), new fooRequestListener());
spiceManager.addListenerIfPending(Bar.class, new Integer(2), new newsRequestListener());
spiceManager.getFromCache(Foo.class, new Integer(0), DurationInMillis.ONE_DAY, new fooRequestListener());
spiceManager.getFromCache(Bar.class, new Integer(2), DurationInMillis.ONE_DAY, new newsRequestListener());
}
...
public final class fooRequestListener implements RequestListener<Foo> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Foo request failure");
}
#Override
public void onRequestSuccess(final Foo result) {
String test = result.getResult().iterator().next().getTitle();
Log.w("MyApp", "RoboSpice - Foo request OK ! "+test);
}
}
public final class barRequestListener implements RequestListener<Bar> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Log.w("MyApp", "RoboSpice - Bar request failure");
}
#Override
public void onRequestSuccess(final Bar result) {
Log.w("MyApp", "RoboSpice - Bar request OK ! "+test);
}
}
}
But I don't understand the aim of spiceManager.getFromCache here...
Thanks!
I would make the splash screen last longer. It looks like there is a realy ambiguity on its purpose : displaying a logo or something, or execute requests and display a "please wait message".
In the first case, it would be fine not to execute any request. In the second, it would be fine to wait for them to return.
I really prefer applications that, if they need a splashscreen at all, boot quickly. The splash screen should last less than 1 second. The first screen after it should handle the data querying and please wait message.
But to answer your questions :
2) yes, SpiceManager cache methods use cache expiry in the same way as executeRequest. You can check if there is something in the cache, but you will have to specify what you mean by "valid" data by specifying the expiry limit of the data.
3) I don't see the link with a retry policy and your overall problem. By default, RoboSpice has a retry policy to retry 3 times a failed request. If you can't get the data from your cache then it means there is nothing in it. If your listener's failure hook is invoked, then the network request failed.
Both could be the same listener, and your app should have a general mechanism to 1) display that something is wrong, 2) relaunch request if needed after a while (that could be a refresh button/item menu).
I hope I helped, but not so sure.
I've been working on an android app which regularly checks a mysql database using JSON and everything works fine with my code.
Im having trouble running this as a timer as it only runs once and then stops.
The only code i managed to get working runs the http request on the UI thread which freezes up.
Any help would be most appreciated.
Thank in advance,
#Override
protected void onCreate(Bundle savedInstanceState) {
...
checkUpdate.start();
...
}
private Thread checkUpdate = new Thread() {
public void run() {
try {
// my code here to get web request to return json string
}
String response = httpclient.execute(httppost, responseHandler);
mHandler.post(showUpdate);
}
...
}
private Runnable showUpdate = new Runnable(){
public void run(){
try{
// my code here handles json string as i need it
Toast.makeText(MainActivity.this,"New Job Received...", Toast.LENGTH_LONG).show();
showja();
}
}
}
private void showja(){
Intent i = new Intent(this, JobAward.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
As #Raghunandan suggested, the standard way to perform work in the background on Android, and then modify the UI when that work is done, is using AsyncTask.
First define a new subclass of AsyncTask:
private class JsonRequestTask extends AsyncTask<HttpUriRequest, Void, String> {
protected String doInBackground(HttpUriRequest... requests) {
// this code assumes you only make one request at a time, but
// you can easily extend the code to make multiple requests per
// doInBackground() invocation:
HttpUriRequest request = requests[0];
// my code here to get web request to return json string
String response = httpclient.execute(request, responseHandler);
return response;
}
protected void onPostExecute(String jsonResponse) {
// my code here handles json string as i need it
Toast.makeText(MainActivity.this, "New Job Received...", Toast.LENGTH_LONG).show();
showja();
}
}
and then you would use the task like this, instead of your Thread:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
JsonRequestTask task = new JsonRequestTask();
task.execute(httppost);
...
}
You may run the task again by simply creating a new JsonRequestTask() and calling its execute() method.
A common practice for a simple async task like this is to make it a private inner class within the Activity class that uses it (if only one Activity needs it). You may need to change the scope of some of your activity's variables so that the inner class may use them (e.g. move local variables to member variables).
i am making the app in which user will be able to c the routine services available to him . For this i am using the website where the admin will be able to update the services . User from android will be able to get the list of services by parsing the xml file available on the server .
The thing i am wondering about is that is there any way the activity automatically refresh itself and user can c the updates done in the services by admin.
Thanks
If you need to do some job on a regular basis, you should have a look at the Handler and the AsyncTask. The general scheme is the following:
//This handler launches the task
private final Handler handler = new Handler();
//This is a task which will be executed
private class RefreshTask extends AsyncTask<String, Void, Object> {
protected Object doInBackground(String... params) {
//Do refreshing here
}
protected void onPostExecute(Object result) {
//Update UI here
}
}
//This is a Runnable, which will launch the task
private final Runnable refreshRunnable = new Runnable() {
public void run() {
new RefreshTask(param1, param2).execute();
handler.postDelayed(refreshRunnable, TIME_DELAY);
}
};
Then you call handler.post(refreshRunnable) when you want to start updates. To cancel them, call handler.removeCallbacks(refreshRunnable). I should note, of course, that I haven't tested this code, but it should give a general idea what to do.
in this app, the user logs in and their credentials are checked against a server.
The user could be waiting a few seconds, depending on how fast the phone can open a data connection if at all. I need dialog box saying "please wait" or "verifying credentials" or something a long those lines after the user clicks log in.
Desired visual order: press log in -> "please wait" dialog is show in this same activity -> when result comes in from server a new activity is loaded (or error is thrown)
Current visual order: press log in -> user waits as if the app is frozen -> new activity is loaded
I'm trying to do this threading thing with AsyncTask but I'm just not getting it right now!
class Progressor extends AsyncTask<Void, Void, Void> {
ProgressDialog dialog;
protected void onPreExecute(){
dialog = ProgressDialog.show(Login.this, "Logging In",
"Verifying Credentials, Please wait...", true);
}
Then in my oncreate method I had all of the other logic like user clicking the button and stuff, but I've since moved that into the AsyncTask method's doInBackGround function
/* When the Login Button is clicked: */
Button loginButton = (Button) findViewById(R.id.loginButton);
loginButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Progressor showMe = new Progressor();
showMe.onPreExecute();
showMe.doInBackground(null);
showMe.onPostExecute();
and onPostExecute simply dismisses the dialog box
Why doesn't this work and how should it be re-arranged. What variable should I be passing into the showMe.doInBackGround() function, it is void. In debugging it never goes in here
#Override
protected Void doInBackground(Void... arg0) {
Don't call the onPreExecute/doInBackground methods of an AsyncTask manually; just call execute() on it and it will call all your methods in the proper places from the correct threads. It defeats the entire purpose of an asynchronous task to call all of its methods synchronously from the UI thread (which is what your sample code does).
That isn't how you use an AsyncTask, have a look at the documentation. Once you have created a new instance of your task, just call execute(), not the individual methods:
Progressor showMe = new Progressor();
showMe.execute();
I have a similar code at the start of my application i load the current settings from the server, it works for me with:
public static ProgressDialog verlauf;
public static String vmessage = "";
static Handler handler = new Handler();;
public static void initialize_system(final Context ctx)
{
verlauf = ProgressDialog.show(ctx, "Starte FISforAndroid..", "synchronisiere Einstellungen",true,false);
new Thread(){
#Override
public void run(){
Looper.prepare();
GlobalVars.table_def.initialize();
vmessage = "erstelle Tabellen";
handler.post(verlauf_message);
builded = sqldriver.create_tables();
vmessage = "setze Systemkonstanten";
handler.post(verlauf_message);
builded = setsystemvars(ctx);
vmessage = "synchronisiere Einstellungen";
handler.post(verlauf_message);
builded = settings.sync_ini();
builded = settings.set_ini();
GlobalVars.system_initialized = builded;
switch(GlobalVars.init_flags.FLAG){
case 0:
break;
case GlobalVars.init_flags.UPDATE:
//load the update
break;
}
verlauf.dismiss();
}
}.start();
}
You need to call showMe.execute(), rather than directly calling doInBackground or onPreExecute etc.