I've got a main Activity, an extra class for my fragment, and inside this fragment is an AsyncTask, which gathers data from various android library (Wifi SSID, BSSID, etc). When I start my app the app shows a blank screen, without any UI. Then after about 2 seconds, the whole data is being shown. I actually want to display my TextViews as "Not connected to a wifi network" in the background, while showing a ProgressDialog until the data is being displayed. I've got the ProgressDialog in my MainActivity, and calling it in my AsyncTask onProgressUpdate
MainActivity.progressDialog = ProgressDialog.show(MainActivity.c,
"ProgressDialog Title",
"ProgressDialog Body");
I'm updating my TextViews in the doInBackground methode (via another methode outside the Fragment)
((Activity) getActivity()).runOnUiThread(new Runnable() {
Would be too big a comment so i'll just put it here.
Sounds like you are using both fragment and AsyncTask in an incorrect way. You should never do anything UI relevant in doInBackground.
Here is an example of what you could do.
I assume the following scenario:
You have a main activity
You have a fragment containing TextViews
You wish to populate the TextViews after loading some data using AsyncTask with a progressDialog
The approach would be to:
Add the fragment in onCreate of your activity (if the fragment is not defined in the layout, then it will automatically be added).
Create the AsyncTask in your fragment like this:
private class LoadData extends AsyncTask<Void, Void, List<String>> {
ProgressDialog progressDialog;
//declare other objects as per your need
#Override
protected void onPreExecute()
{
// getActivity() is available in fragments and returns the activity to which it is attached
progressDialog= new ProgressDialog(getActivity());
progressDialog.setTitle("ProgressDialog Title");
progressDialog.setMessage("ProgressDialog Body");
progressDialog.setIndeterminate(true)
progressDialog.setCancelable(false)
progressDialog.show();
//do initialization of required objects objects here
};
#Override
protected Void doInBackground(Void... params)
{
List<String> results = new ArrayList<String>();
//do loading operation here
//add each of the texts you want to show in results
return results;
}
// onPostExecute runs on UI thread
#Override
protected void onPostExecute(List<String> results )
{
progressDialog.dismiss();
// iterate results and add the text to your TextViews
super.onPostExecute(result);
};
}
Start the AsyncTask in onCreate of your fragment:
#Override
public void onCreate(Bundle savedInstanceState) {
new LoadData().execute();
super.onCreate(savedInstanceState);
}
This way you avoid calling directly back to your activity, which really should not be necessary in your scenario (unless I have misunderstood).
Otherwise please post all the relevant code and layouts.
This line:
I'm updating my TextViews in the doInBackground methode
points to your problem. You need to use the AsyncTask method onProgressUpdate() to publish to the UI thread. You do not call onProgressUpdate() directly, instead you call publishProgress().
Interestingly, I answered a similar question yesterday here: android AsyncTask in foreach
and it includes an example.
Here's what you need to do.
(1) From the place you run the code that gathers data, you should first display the progress dialog. Something like this:
busy = new ProgressDialog (this);
busy.setMessage (getString (R.string.busy));
busy.setIndeterminate (true);
busy.setCancelable (false);
busy.show();
(2) Then you start your data gathering. This must be done in a separate thread (or Runnable). Do something like this:
Thread thread = new Thread ()
{
#Override
public void run()
{
try
{
... gather data ...
Message msg = handler.obtainMessage();
msg.what = LOADING_COMPLETE;
msg.obj = null;
handler.sendMessage (msg);
}
catch (Exception e)
{
Message msg = handler.obtainMessage();
msg.what = LOADING_FAILED;
msg.obj = e.getMessage(); // maybe pass this along to show to the user
handler.sendMessage (msg);
}
// get rid of the progress dialog
busy.dismiss();
busy = null;
}
}
(3) Add a handler to the activity to receive notification when data gathering is complete:
Handler handler = new Handler()
{
#Override
public void handleMessage (Message msg)
{
if (msg.what == LOADING_COMPLETE)
loadingComplete ();
else if (msg.what == LOADING_FAILED)
loadingFailed ((String)msg.obj);
}
};
(4) Implement the handlers:
private void loadingComplete ()
{
...
}
private void loadingFailed (String errorMessage)
{
...
}
That's the essentials.
Related
In an effort to learn Android I am writing a small app. The first thing I am trying to do is login via a remote API.
I would like to show a "loading" dialog when the call is being made (in case he user in using mobile internet). Researching this has shown two possible methods.
One is to use a ProgressDialog and a private class that extends Thread, the other is using a private class that extends AsyncTask.
Which is best/more appropriate for this task?
I have tried using the ProgressDialog version but am struggling. I have put the function making the http request in the extended Thread run() method, but am unsure on how to pass the response data (JSON) back into my activity.
Any and all help gratefully received.
The best way possible is to use an AsyncTask with a ProgressDialog. You should extend AsyncTask and implement all the methods you need:
onPreExecute() - here you initialize your ProgressDialog and show() it
doInBackground() - here you do your work
onPostExecute() - here you call dismiss() on ProgressDialog to hide it
(optional) onProgressUpdate() - here you can change the progress of your ProgressDialog if it's determinate
There is a get() method in AsyncTask class that lets you retrieve the result of the work. Also you can implement an interface between the AsyncTask and calling Activity to return the result. Hope this helps.
Efforts come with rewards :) Egor is right, AsyncTask is the best way to do it. But
You have to know that Activity is working on the UI thread and threads not. So the only way to share things is via handler. Here an example:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progress = (ProgressBar) findViewById(R.id.progressBar1);
handler= new Handler();
}
public void startProgress(View view) {
// Do something long
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 10; i++) {
final int value = i;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
progress.setProgress(value);
}
});
}
}
};
new Thread(runnable).start();
}
The goal:
Using Google App Engine server and Android client, I'm trying to put on the Google map at the Android client Users overlays. Every 30 seconds I'm polling the server and getting Vector that contains users and adding it to the map.
Current status:
I'm dong all that using in one new thread, So after running the app I got:
weird behaviors(delayed overlays, multiple overlays) and after that crushed with ConcurrentModificationException.
After reading a bit i figured out that I need to work with AsyncTask.
Correct me if I'm wrong,But I understand that everything done in the Activity at at onCreate is "running" in UIhread so I need to put the "Logic" (All the Network handling) in doInBackground and all the UI Handling like putting overlays on the map in onPostExecute.
My Question are:
1) In the current status I'm doing:
new Thread()
{
#Override
public void run()
{
super.run();
while(true)
{
SystemClock.sleep(30000);
Vector responseFromServer = getUsersVectorFromServer();
putNewOnlineUserOnTheMap();
}
}
}.start();
What is the right way to convert this To AsyncTask?
Do I poll the server still using new thread in the doInBackground or there is right way to do this?
2) Is there a specific list of what counts as UI to put in onPostExecute or any concepts list?
In my case I guess that in need to put putNewOnlineUserOnTheMap() in onPostExecute.
Thanks.
Something similar to the following:
class UpdateTask extends AsyncTask<Void, Vector, Void>{
#Override
protected Void doInBackground(Void... params) {
// this is running in a background thread.
while (!isCancelled()) {
SystemClock.sleep(30000);
Vector responseFromServer = getUsersVectorFromServer();
// send the result back to the UI thread
// onProgressUpdate will be called then
publishProgress(responseFromServer);
}
return null;
}
#Override
protected void onProgressUpdate(Vector... values) {
// this is executed on the UI thread where we can safely touch UI stuff
putNewOnlineUserOnTheMap(values[0]);
}
}
You can't use the result of the task since the task is finished then. But you can use the progress publishing mechanism to get periodic results. If you use it like that and do the modification on the UI thread you should not get ConcurrentModificationException because you do the modifications on the one thread that can safely modify the UI.
One thing to note here: create new instances of your Vector in the background thread and then use it to update the UI. But don't touch the same object afterwards in the backgroundthread. That way you don't need any synchronization since after the background thread sends it away it is only the UI thread that touches it. (and you could use a simple ArrayList instead of a Vector)
AsyncTask uses generics and varargs.The parameters that are passed to the asyntask are . TypeOfVariableArgumentsParameters is passed into the doInBackground(), ProgressParam is used for progress information and ResultParam must be returned from doInBackground() and is passed to onPostExecute() as parameter.
example:--
protected class ParsingTask extends AsyncTask> {
private ProgressDialog loadingDialog = new ProgressDialog(JsonParserActivity.this);
protected void onPreExecute() {
loadingDialog.setMessage("loading app store..");
loadingDialog.show();
}
#Override
protected ArrayList<Items> doInBackground( Context... params ) {
// do ur process here.
return result;
}
if (!this.isCancelled()) {
}
return result;
}
#Override
protected void onProgressUpdate(String... s) {
super.onProgressUpdate(s);
Toast.makeText(getApplicationContext(), s[0], Toast.LENGTH_SHORT).show();
}
#Override
protected void onPostExecute( ArrayList<Items> response ) {
//if u r dealing with list view and adapters set the adapter here at the onPostExecute()
loadingDialog.dismiss();
}
#Override
protected void onCancelled() {
super.onCancelled();
Toast.makeText(getApplicationContext(), "The operation was cancelled", 1).show();
}
}
You can use AsyncTask like below. Hope this will help you..
Class YourClass{
void YourClass(){
NetworkTask nT = new NetworkTasK();
nT.execute();
}
}
protected class NetworkTask extends AsyncTask<Void, String, Boolean>
{
#Override
protected Boolean doInBackground(Void... params)
{
try
{
String response;
while(keepreceiving)
{
response = in.readLine();//Prog Counter stops here until getting i/p.
if(response != null)
yourFunctionForResponse(response);
}
}
catch (Exception ex)
{
}
return null;
}
private void yourFunctionForResponse(String response){
//things to do....
}
}
You may also try runOnUiThread(Runnable action) along with this to implement your work.
I have a ListActivity which launches another Activity based on the list selection. This second Activity needs to load a fair bit of data from the internet and as such there is a noticeable delay between when the user clicks on an item and when the Activity displays.
This is a problem because I currently have no way to indicate to the user that their click is being processed (even just changing the colour of the selected list item would be sufficient but I can't find a good way to do that). Ideally I'd be able to display an indeterminate ProgressDialog while the second Activity is loading.
I've tried a few different approaches for this but nothing seems to work as desired.
I've tried the following:
Retrieving the serializable data (not all of it but some part) in an AsyncTask in the first Activity and passing it as an extra to the second. This didn't really work well as a ProgressDialog I created in onPreExecute() didn't display immediately (it seems delayed by the processing done in doInBackground() for some reason.)
Here is the code for that:
AsyncTask<String, Void, String> read = new AsyncTask<String, Void, String>() {
Dialog progress;
#Override
protected void onPreExecute() {
progress = ProgressDialog.show(SearchActivity.this,
"Loading data", "Please wait...");
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
DatasetReader reader = new DatasetReader();
reader.setFundID(params[0]);
reader.addDatsets(FundProfile.datasets);
reader.populate();
return reader.toString();
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progress.dismiss();
}
};
read.execute(selectedItem.getUniqueID());
try {
action = new Intent(SearchActivity.this, FundProfile.class);
action.putExtra("data", read.get());
} catch(Exception ex) {
ex.printStackTrace();
}
In the second Activity's onCreate() method (this does not work at all):
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarVisibility(true);
Here is the onCreate() method for the second approach:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setTitleColor(Color.WHITE);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarVisibility(true);
try {
setContentView(R.layout.fund_profile);
// init some data
setProgressBarVisibility(false);
} catch(Exception ex) {
FundProfile.this.finish();
}
}
If you have long operations you should not be doing them in onCreate in any case as this will freeze the UI (whether or not the activity is displayed). The UI set by onCreate will not appear and the UI will be unresponsive until after the onCreate call finishes.
It seems you can start your second activity and display a progress bar (or requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);), then start an ASyncTask which will be responsible for updating your UI once data has been retrieved.
Adam,
It sounds like you are looking for the Indeterminate Progress bar: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/ProgressBar2.html
You can display this while you are loading your second Activity then set the visibility to false once the second Activity has loaded its data.
Move creating the Intent -- and really anything you need to do after the AsyncTask completes -- into onPostExecute:
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progress.dismiss();
Intent action = new Intent(SearchActivity.this, FundProfile.class);
action.putExtra("data", result);
// ... do more here
}
The problem is that AsyncTask.get() blocks until the task is completed. So in the code above, the UI thread is blocked and the ProgressDialog is never given a chance to appear until the task completes.
I'm using following code to fill a custom ListPreference dialog. Since the fill procedure takes a lot of time i want to show a progress dialog during the fill procedure.
My problem is that filler.execute() does not block onPrepareDialogBuilder and functions goes till the end before values are filled causing an exception... Any idea?
#Override
protected void onPrepareDialogBuilder(Builder builder) {
// Load data
if (this.getEntries()==null) {
FillerTask filler = new FillerTask();
filler.execute();
}
Log.d(TAG, "Filler finished");
super.onPrepareDialogBuilder(builder);
}
Here is Filltertask code, basically he looks for every activity with a MAIN Intent filling a list:
private class FillerTask extends AsyncTask<Void, Void, String[][]> {
private ProgressDialog dialog;
#Override
protected void onPreExecute() {
Log.d(TAG, "Dismiss dialog");
dialog = ProgressDialog.show(MyListPreference.this.getContext(), "", "Doing stuff...", true);
}
#Override
protected String[][] doInBackground(Void... params) {
return fill();
}
public String[][] fill() {
Log.d(TAG, "Fill started");
CREATE LISTS...
// Done
Log.d(TAG, "Fill done");
String[][] result = new String[][] {entryNames, entryValues};
return result;
}
#Override
protected void onPostExecute(String[][] result) {
Log.d(TAG, "Post execute");
MyListPreference.this.setEntries(result[0]);
MyListPreference.this.setEntryValues(result[1]);
dialog.dismiss();
}
}
My problem is that filler.execute() does not block onPrepareDialogBuilder and functions goes till the end before values are filled causing an exception... Any idea?
That is the entire point behind an AsyncTask. The "Async" in AsyncTask means asynchronous.
Use your AsyncTask to get your data. Then, in onPostExecute(), display the dialog.
Found the solution, best way to do this is override the onClick method and let the AsyncTask postExecute call the "super()", so click is not passed until content is loaded and during load progress bar is correctly displayed.
asyntask doesn't lock main thread, it just drops a message to message queue of main thread
I have an application that is performing HTTP Requests (specifically calling the FogBugz API) when the user clicks certain buttons. Right now, I am just creating a service when the application starts, and then calling different methods in that service to complete requests. However, when I do this, there is the usual hang in the UI thread. I have looked at AsyncTask, but am not sure it will do what I want to accomplish. Because I need to instantly parse the XML that the HTTP Request returns, I need to have a process that is able to return this data to the UI thread. Will ASyncTask be able to accomplish this, or is there some other way?
public static InputStream makeRequest(String httpRequest)
{
In a separate thread, run HTTP Request, get back and process, return inputstream
}
This method is called by several others to perform HttpRequests. Once the inputstream is returned, the other methods parse for specific information.
The simplest way to do it would just be to do something like
//Body of your click handler
Thread thread = new Thread(new Runnable(){
#Override
public void run(){
//code to do the HTTP request
}
});
thread.start();
That will cause the code inside the run() method to execute in a new thread of execution. You can look into an async task if you like although I've personally never used it. This is a quick and simple way to get things done.
With regards to passing information back, I would use a Handler object which effectively allows you to set up a message queue for a given thread and pass messages to it which cause the execution of specific code. The reason you need to do this is because Android will not let any thread other than the UI thread update the actual UI.
Does that address your question? I know my first pass didn't fully address all of your issues.
Edit Basically, what you do is define a handler object in your Activity like
private Handler handler_ = new Handler(){
#Override
public void handleMessage(Message msg){
}
};
You also create static int constants that help tell the handler what to do. You basically use those to allow for several different types of messages to be passed to one instance of a handler. If there is only going to be one message that is passed back, then you don't have to worry about that.
For example
private static final int UPDATE_UI = 1;
To send a message to the handler you call
handler_.sendMessage(Message.obtain(handler_, UPDATE_UI, inputStreamInstance));
From the handler:
private Handler handler_ = new Handler(){
#Override
public void handleMessage(Message msg){
switch(msg.what){
case UPDATE_UI:
InputStream is = (InputStream)msg.obj;
//do what you need to with the InputStream
break;
}
}
};
Alternatively, where the inputStreamInstance is added to the Message object, you can pass any object you like so if you wanted to parse it into some kind of container object or something like that, you could do that as well. Just cast it back to that object from within the handler.
Try using AsyncTask. Goto this Link for more:
private class SyncIncoData extends AsyncTask<String, String, String> {
ProgressBar pb;
ProgressBar pbar;
#Override
protected String doInBackground(String... urls) {
for (int i = 0; i <= 100; i++) {
publishProgress(i);
}
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
pb = (ProgressBar) findViewById(R.id.progressBarsync4);
pb.setVisibility(View.VISIBLE);
}
#Override
protected void onPostExecute(String result) {
pb = (ProgressBar) findViewById(R.id.progressBarsync4);
pb.setVisibility(View.INVISIBLE);
}
#Override
protected void onProgressUpdate(String... values) {
pbar = (ProgressBar) findViewById(R.id.progressBarpbar);
pbar.setProgress(Integer.parseInt(values[0]));
super.onProgressUpdate(values);
}
}
Write the program for http request inside doinbackgroud()