In my android application, I have written a async task which fetches geo location data from google app engine datastore.All works fine, except that process dialog doesn't show no matter what I do. I have followed all available tutorials etc but of no avail.
Here is what I do:
I have an activity to show map data.
public class MapActivity extends FragmentActivity....
//On click of a button OR when user location changes, following method is called:
private void findAction(Location location){
//this.progressDialog = new ProgressDialog(this);
FetchHospitalsAsyncTask asyncTask = new FetchHospitalsAsyncTask(this);
List<Hospital> nearByHospitals = null;
try {
nearByHospitals = asyncTask.execute(1000.0, latitude, longitude).get();
}catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
...and here is my async class
public class FetchHospitalsAsyncTask extends AsyncTask<Double, Void, List<Hospital>> {
private static String TAG = FetchHospitalsAsyncTask.class.getName();
private static HospitalApi hospitalApiService = null;
ProgressDialog progressDialog = null;
private Activity parentActivity = null;
public FetchHospitalsAsyncTask(Activity parentActivity){
this.parentActivity = parentActivity;
this.progressDialog = new ProgressDialog(parentActivity);
}
#Override
protected void onPreExecute(){
progressDialog.setMessage("Please wait.Fetching data from server....");
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();
}
#Override
protected void onPostExecute(List<Hospital> result){
if(progressDialog.isShowing()){
progressDialog.dismiss();
}
}
#Override
protected List<Hospital> doInBackground(Double... params) {
/*
*/
/*context = params[0].first;
String name = params[0].second;
String address= params[1].second;*/
double radius = params[0];
double lat = params[1];
double lon = params[2];
try {
com.cypherlabs.mumbaihospitalfinder.backend.hospitalApi.HospitalApi.GetNearByHospitals nearByHospitals=hospitalApiService.getNearByHospitals(radius,lat,lon);
nearByHospitals.getLastStatusCode();
return nearByHospitals.execute().getItems();
} catch (IOException e) {
//return e.getMessage();
return null;
}
}
}
Process dialog shows no trace whatsoever of its existence, while all other things work fine.Map is rendered with all markers etc.
You are calling .get() on the AsyncTask. This will cause the AsyncTask to run synchronously instead of asynchronously. This causes the UI thread to be blocked and as such the progress dialogue will not show.
You need to call execute() on the AsyncTask instead. This will cause the AsyncTask to run in a separate thread and not block the UI. Note that the onPreExecute and onPostExecute methods will be run on the same thread you called execute from
Using get() means it will freezes your main ui thread untill the Async task is finished. Adding a progress dialog will run on main Ui thread. And as you are using get() it will not allow any changes to main ui. So remove get() you will get progress dialog works.
Related
I'm new to android studio and in learning phase.
I'm able to get current user location and store it in Parse server. Now i have also stored the Geo location of some super markets in Parse database in a class "Hospitals". Now i'm trying to retrieve the Geo location of specific hospital and trying to compare the distance between current user and the hospital.
When i'm trying to fetch the value from Parse database, it is calculated in background, so i'm unable to use it in different function to get the difference in distance as the return value will be null.
Below code is used:
class RemoteDataTask extends AsyncTask<Void, Void, Void> {
ProgressDialog mProgressDialog;
private Context context;
List<ParseUser> object = new ArrayList<>();
final ParseGeoPoint[] parlourUser = new ParseGeoPoint[1];
RemoteDataTask(Context context) {
Log.i("info", "Enetred RemoteDataTask");
this.context = context;
}
#Override protected Void doInBackground(Void... params) {
Log.i("info", "Entered doInbackground");
ParseQuery<ParseUser> query = ParseUser.getQuery();
try{
Log.i("info", "value is " + query.get("aifg14PNKz"));
object.set(1, query.get("aifg14PNKz"));
Log.i("info", "Object value" + object.get(1));
parlourUser[0] = object.get(0).getParseGeoPoint("Location");
Log.i("info", "Location of parlour user " + parlourUser[0]);
} catch (ParseException e) {
e.printStackTrace();
}
}
First of all, this is a very wrong practice to make a function which depends on the background thread.
query.getInBackground
It will work on the background thread and
After Background Query completed, this callback method communicates with UI thread
new GetCallback<ParseObject>() {
#Override
public void done(ParseObject object, ParseException e) {
}
Your function will not work if query.getInBackground take some time.
Solution to your problem in 2 steps
1) You can call this in AsyncTask and handle the Query in background
// RemoteDataTask AsyncTask
ProgressDialog mProgressDialog;
private class RemoteDataTask extends AsyncTask<Void, Void, Void> {
private Context context;
List<ParseObject> object;
final ParseGeoPoint[] hospitalUser = new ParseGeoPoint[1];
RemoteDataTask(Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
// Create a progressdialog
mProgressDialog = new ProgressDialog(context);
// Set progressdialog title
mProgressDialog.setTitle("Title...");
// Set progressdialog message
mProgressDialog.setMessage("Loading...");
mProgressDialog.setIndeterminate(false);
// Show progressdialog
mProgressDialog.show();
}
#Override
protected Void doInBackground(Void... params) {
// Create the array
try {
ParseQuery<ParseObject> query = ParseQuery.getQuery("Lakme");
object = query.find();
if (ob != null) {
hospitalUser[0] = object.getParseGeoPoint("Location");
//calculate distance
}
} catch (ParseException e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
// Close the progressdialog
mProgressDialog.dismiss();
// You can use "hospitalUser" Object here.
}
}
Call it from where you call this method
new RemoteDataTask(YourActivity.this).execute();
2) Calculate Distance once got a result from query
if it does not involve UI thread then you can calculate it in
"doInBackground" or
if it does involve UI thread you should use it in "onPostExecute" or
if you want to get notified after AsyncTask completed then you can
implement interface like this.
I want to check if a user is registered or not in a database, and if it is get the information of the user.
Normally, when I retrieve the information from the server, I put in the Json a variable saying if the user exists or not. Then in onPostExecute(Void result) i treat the Json, so i don't need the AsyncTask to return any value.
Before I was calling the AsyncTask as follows:
task=new isCollectorRegistered();
task.execute();
But now i'm trying a different approach. I want my asynktask to just return a boolean where i called the AsyncTask.
the AsyncTask looks as follows:
public class isCollectorRegistered extends AsyncTask<Void, Void, Void> {
private static final String TAG_SUCCESS = "success";
int TAG_SUCCESS1;
private static final String TAG_COLLECTOR = "collector";
public String collector;
JSONArray USER = null;
JSONObject jObj = null;
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
// Checks on the server if collector is registered
try {
jObj = ServerUtilities.UserRegistered(context, collector);
return null;
} finally {
return null;
}
}
#Override
protected void onPostExecute(Void result) {
try {
String success = jObj.getString(TAG_SUCCESS);
Log.d(TAG_COLLECTOR, "Final Info: " + success);
//This if sees if user correct
if (Objects.equals(success, "1")){
//GOOD! THE COLLECTOR EXISTS!!
}
} catch (JSONException e) {
e.printStackTrace();
Log.d(TAG_COLLECTOR, "JSON parsing didn't work");
}
}
}
I have checked several posts, but I still havent found out the way to retrieve the boolean where I call the Asynktask, something like this :
task=new isCollectorRegistered();
task.execute();
boolean UserRegistered = task.result();
What would be the right approach? Any help would be appreciated
To use AsyncTask you must subclass it. AsyncTask uses generics and varargs. The parameters are the following AsyncTask <TypeOfVarArgParams , ProgressValue , ResultValue> .
An AsyncTask is started via the execute() method.
The execute() method calls the doInBackground() and the onPostExecute() method.
TypeOfVarArgParams is passed into the doInBackground() method as input, ProgressValue is used for progress information and ResultValue must be returned from doInBackground() method and is passed to onPostExecute() as a parameter.
In your case you are passing Void to your AsyncTask : isCollectorRegistered extends AsyncTask<Void, Void, Void> so you can't get your result from the thread.
please read this tutorial to a deep understand of the AsyncTask in Android
I think the following is exactly what you were looking for, Alvaro...NOTE: I tweaked your code to make it more sensible, but I tried to stick to as much of your original code as possible...
public class RegisterCollector extends AsyncTask<String, Void, Boolean> {
private static final String TAG_SUCCESS = "success";
private static final String TAG_COLLECTOR = "collector";
int TAG_SUCCESS1;
String[] strArray;
JSONArray USER = null;
JSONObject jObj = null;
public String collector;
private AppCompatActivity mAct; // Just incase you need an Activity Context inside your AsyncTask...
private ProgressDialog progDial;
// Pass data to the AsyncTask class via constructor -> HACK!!
// This is a HACK because you are apparently only suppose to pass data to AsyncTask via the 'execute()' method.
public RegisterCollector (AppCompatActivity mAct, String[] strArray) {
this.mAct = mAct;
this.strArray = strArray;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
// AHAH!! - So we do need that Activity Context after all...*TISK* *TISK* # Google **sigh**.
progDial = ProgressDialog.show(mAct, "Please wait...", "Fetching the strawberries & cream", true, false);
}
#Override
protected Boolean doInBackground(String... params) {
// Checks on the server if collector is registered
try {
jObj = ServerUtilities.UserRegistered(context, collector);
return true; // return whatever Boolean you require here.
} finally {
return false; // return whatever Boolean you require here.
}
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
progDial.dismiss();
try {
String success = jObj.getString(TAG_SUCCESS);
Log.d(TAG_COLLECTOR, "Final Info: " + success);
// This 'if' block checks if the user is correct...
if (Objects.equals(success, "1")){
//GOOD! THE COLLECTOR EXISTS!!
}
// You can then also use the Boolean result here if you need to...
if (result) {
// GOOD! THE COLLECTOR EXISTS!!
} else {
// Oh my --> We need to try again!! :(
}
} catch (JSONException e) {
e.printStackTrace();
Log.d(TAG_COLLECTOR, "JSON parsing didn't work");
Toast.makeText(mAct, "JSON parsing FAILED - Please try again.", Toast.LENGTH_LONG).show();
}
}
}
...then if you want to use the generated Boolean data outside the AsyncTask class try the following:.
RegisterCollector regisColctr = new RegisterCollector((AppCompatActivity) this, String[] myStrArry);
AsyncTask<String, Void, Boolean> exeRegisColctr = regisColctr.execute("");
Boolean isColctrRegistered = false;
try {
isColctrRegistered = exeRegisColctr.get(); // This is how you FINALLY 'get' the Boolean data outside the AsyncTask...-> VERY IMPORTANT!!
} catch (InterruptedException in) {
in.printStackTrace();
} catch (ExecutionException ex) {
ex.printStackTrace();
}
if (isColctrRegistered) {
// Do whatever tasks you need to do here based on the positive (i.e. 'true') AsyncTask Bool result...
} else {
// Do whatever tasks you need to do here based on the negative (i.e. 'false') AsyncTask Bool result...
}
There you go - I think this is what you were looking for (originally). I always use this approach whenever I need Async data externally, and it has yet to fail me....
I've got a simple login screen. If you click "Login", a progress bar should appear while we wait for the AsyncTask in the background to check the login credentials.
If I run the code without the AsyncTask in the background, my progress bar appears immediately. However, if I use the AsyncTask, which I set up after I make my progress bar appear, the app freezes at the exact moment I click on "Login". Then it waits until the AsyncTask has got its result (get() command) and only then it unfreezes, making my progress bar useless.
Is this a commonly known issue? How do you solve it?
This is how where I set up the AsyncTask, after I show the progress bar.
showProgress(true, "Logging in ...");
mAuthTask = new InternetConnection();
String arguments = "email="+mEmail+"&pwd="+mPassword;
boolean k = mAuthTask.makeConnection("ADDRESS", arguments, getBaseContext());
String f = mAuthTask.getResult();
And this is my AsyncTask. downloadUrl() sets up an HttpURLConnection. This works, I tested it.
private DownloadData data = new DownloadData();
public boolean makeConnection(String url, String arguments, Context context) {
if(isWifi(context) || isMobile(context)) {
argsString = arguments;
data.execute(url);
return true;
} else {
return false; //No network available.
}
}
public String getResult() {
try {
return data.get();
} catch (InterruptedException e) {
return "Error while retrieving data.";
} catch (ExecutionException e) {
return "Error while retrieving data.";
}
}
private class DownloadData extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... url) {
try {
return downloadUrl(url[0]);
} catch (IOException e) {
return "Unable to retrieve data.";
}
}
Do it like:
#Override
protected void onPreExecute() {
Asycdialog.setMessage("Working");
Asycdialog.getWindow().setGravity(Gravity.CENTER_VERTICAL);
Asycdialog.getWindow().setGravity(Gravity.CENTER_HORIZONTAL);
Asycdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
Asycdialog.setCancelable(false);
//Dialog Show
Asycdialog.show();
super.onPreExecute();
And then in onPostExecute:
protected void onPostExecute(String result) {
// hide the dialog
Asycdialog.dismiss();
super.onPostExecute(result);
}
To use the same Async task from Different classes:
class MainActivity{
new MyTask().execute();
}
class DifferentActivity {
new MyTask().execute();//a new instance
}
class MyTask extends AsyncTask{
public MyTask(Context context){
}//Pass in context.
}
Pass the context to the constructor, if you want a consistent Progress dialog.
TO publish the progress from doInBackground you can use the following:
publishProgress(progress);
Asycdialog.setMax(lines);
Asycdialog.incrementProgressBy(1);
Where progress is a string, lines are the max number of items.
You should not call get() it blocks the ui waiting for the result to be returned making asynctask no more asynchronous.
You have
private DownloadData data = new DownloadData();
and you have
data.get(); // this why it freezes
and
private class DownloadData extends AsyncTask<String, Void, String>
http://developer.android.com/reference/android/os/AsyncTask.html#get()
You only need
data.execute(url);
And if your asynctask is an inner class of activity class you can return result in doInbackground and update ui in onPostExecute. If not you can use interface as a callback to the activity to return the result.
your issue is related to the fact that you are calling getResult from the UI Thread. getResult calls data.get() that is a blocking operation. That's why you are getting a freeze. Your UI Thread is waiting for get() to complete and it is unable to draw everything else
In my app I performing loading data from web and then displaying it to user. Before loading data app shows progress dialog. I have problem if user locks phone in the middle of loading operation, or server is overloaded and can't respond in time my application freezes, because it doesn't dismiss progress dialog, or in some cases it crashes because lack on needed data.
If some error happened while loading data I want show some dialog to user to let him know about error and ask him should application repeat last request. I tried to use AlertDialog for it, but I haven't succeed.
Here is code of one activity (There is no progress dialog here, but it demonstrates how I loading data):
#EActivity(R.layout.layout_splash)
#RoboGuice
public class SplashScreenActivity extends Activity {
#Inject
private AvtopoiskParserImpl parser;
#Bean
BrandsAndRegionsHolder brandsAndRegionsHolder;
#ViewById(R.id.splash_progress)
ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
}
#Background
protected void loadData() {
publishProgress(10);
LinkedHashMap<String, Integer> brands = null;
try {
brands = parser.getBrands();
} catch (IOException e) {
Log.e(e.getMessage());
}
publishProgress(50);
LinkedHashMap<String, Integer> regions = null;
try {
regions = parser.getRegions();
} catch (IOException e) {
Log.e(e.getMessage());
}
publishProgress(70);
populateData(brands, regions);
}
#UiThread
protected void populateData(LinkedHashMap<String, Integer> brands, LinkedHashMap<String, Integer> regions) {
Intent intent = new Intent(SplashScreenActivity.this, SearchActivity_.class);
brandsAndRegionsHolder.brandsMap = brands;
brandsAndRegionsHolder.regionsMap = regions;
publishProgress(100);
startActivity(intent);
finish();
}
#UiThread
void publishProgress(int progress) {
progressBar.setProgress(progress);
}
}
parser.getBrands() and parser.getRegions() are loading data from the web.
I want to do something like this:
boolean repeatRequest = true;
while (repeatRequest) {
try {
brands = parser.getBrands();
repeatRequest = false;
} catch (IOException e) {
Log.e(e.getMessage());
repeatRequest = showErrorDialog();
}
}
But I didn't manage to do so because this code executes in background thread, but dialog should be shown in UI thread.
I believe that it should be standard approach of doing so, but didn't manage to find it.
Any ides how can I implement this?
The best way is to use AsyncTask.
private class LoadDataTask extends AsyncTask<Void, Integer, Object> {
private ProgressDialog mProgress;
protected Object doInBackground(Void... params) {
// This method runs in background
Object result = null;
try {
result = parser.parse();
} catch (Exception e) {
result = e.getMessage();
}
return result;
}
protected void onProgressUpdate(Integer... progress) {
// This method runs in UI thread
mProgress.setProgress(progress[0]);
}
protected void onPreExecute() {
// This method runs in UI thread
mProgress = new ProgressDialog(context);
mProgress.show();
}
protected void onPostExecute(Object result) {
// This method runs in UI thread
mProgress.dismiss();
if (result instance of String) {
// Here you can launch AlertDialog with error message and proposal to retry
showErrorDialog((String) result);
} else {
populateData(result);
}
}
}
I have a difficulty with my database connection. I have a DataManager which creates the Database connection etc. I also created an Application class which creates this DataManager so the UI Thread can get the data. Now the problem is that there is a background thread which also needs access to the database, and I am not exactly sure how to do this (currently I get an error when trying to create a new DataManager in the AsyncActivity because the database was not closed (and this is correct, as the UIThread has it open).
So I thought that (and this might not be the right way to do it as it is a different thread) I would get access to the Application and use the same DataManager on a new session. But how can I get access to the Application from an 'ordinary' class without Context or Activity (I have done it before I think, but cannot remember).
The code of the background task is:
public class SyncTask extends AsyncTask<String, Integer, String> {
private static final String TAG = "Sync";
/** application context. */
private Context context;
private ProgressDialog dialog;
public SyncTask(Context aContext) {
//this.activity = activity;
this.context = aContext;
dialog = new ProgressDialog(context);
}
protected void onPreExecute() {
this.dialog.setMessage("Loading...");
this.dialog.setMax(100);
this.dialog.setProgress(0);
this.dialog.show();
}
#Override
protected void onPostExecute(final String errMessage) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (errMessage == null) {
Toast.makeText(context, "Update completed", Toast.LENGTH_LONG).show();
} else {
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("Error...");
alertDialog.setMessage(errMessage);
alertDialog.show();
}
}
protected void onProgressUpdate(Integer... progress) {
Log.d(TAG, "Progress Update: " + progress[0].toString());
super.onProgressUpdate(progress[0]);
dialog.setProgress(progress[0]);
}
protected String doInBackground(final String... args) {
try{
publishProgress( new Float(50).intValue());
iDomsAndroidApp app = ((iDomsAndroidApp) ?? cannot remember ??);
DataManager manager = app.getDataManager();
manager.updateData();
return null;
} catch (Exception e){
Log.e(TAG, "error", e);
return e.getMessage();
}
}
}
You can use getApplicationContext() method of any Context instance you have and cast return to YouApplication. It is absolutelly eligible.
You should use a ContentProvider for accessing the database, it is thread safe and the preferred way to handle your data.
http://developer.android.com/guide/topics/providers/content-providers.html