Android network thread and view/gui updates - android

I have a catch 22 here. If I use a AsyncTask to run my network activity I can't update my user interface from that thread.
MainActivity.onCreate(...){
myAsyncTask.execute();
//E/AndroidRuntime(1177): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
}
The Network activity will be continuous and needs to happen on a different thread. So, I turned to super.runOnUiThread to fix the error above and because it accepts Runnable as its parameter. Unfortunatly, the Javadocs are not clear in that I don't know if super.runOnUiThread is going to make a Thread or just call run directly. Apparently it does not make a Thread, because I get this exception: android.os.NetworkOnMainThreadException
Given that I have a one-screen App that need a connection. What is the simplest way to make this work?

If I use a AsyncTask to run my network activity I can't update my user interface from that thread
That is why AsyncTask has onPostExecute(). Put your UI update logic in there (or onProgressUpdate(), if you wish to update the UI as the background work is going on).

As CommonsWare said you can use onProgressUpdate() to update your UI .Here is an example, I used it to make a cool splash screen.
https://www.dropbox.com/s/cyz7112k4m1booh/1.png
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
bar=(ProgressBar) findViewById(R.id.progressBar);
new PrefetchData().execute();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.splash, menu);
return true;
}
private class PrefetchData extends AsyncTask<String,String,String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
// before making http calls
}
#Override
protected String doInBackground(String... arg0) {
Random ran=new Random();
int count;
int total=0;
try {
while(total <= 100){
count=ran.nextInt(30);
total+=count;
Thread.sleep(1000);
if (total >= 100) publishProgress(""+100);
//here publishProgress() will invoke onProgressUpdate() automatically .
else publishProgress(""+(int) total);
}
}catch (InterruptedException e) {
e.printStackTrace();
Log.e("Error:",e.getMessage());
}
return null;
}
protected void onProgressUpdate(String... progress) {
bar.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Intent i = new Intent(SplashActivity.this, MainActivity.class);
startActivity(i);
finish();
}
}

Related

Abort Jsoup request [duplicate]

I use an async task to upload an image and get some results.
While uploading the image I see a progress dialog, written in onPreExecute() method like this:
protected void onPreExecute() {
uploadingDialog = new ProgressDialog(MyActivity.this);
uploadingDialog.setMessage("uploading");
uploadingDialog.setCancelable(true);
uploadingDialog.show();
}
Ok when I press the back button, obviously the dialog disappears because of the setCancelable(true).
But (obviously) the async task doesn't stop.
So how can I fix this? I want to cancel both dialog and async task when I press the back button. Any ideas?
From SDK:
Cancelling a task
A task can be cancelled at any time by invoking cancel(boolean).
Invoking this method will cause subsequent calls to isCancelled()
to return true.
After invoking this method, onCancelled(Object), instead of
onPostExecute(Object) will be invoked after doInBackground(Object[]) returns.
To ensure that a task is cancelled as quickly as possible,
you should always check the return value of isCancelled() periodically from
doInBackground(Object[]), if possible (inside a loop for instance.)
So your code is right for dialog listener:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
myTask.cancel(true);
//finish();
}
});
Now, as I have mentioned earlier from SDK, you have to check whether the task is cancelled or not, for that you have to check isCancelled() inside the onPreExecute() method.
For example:
if (isCancelled())
break;
else
{
// do your work here
}
FOUND THE SOLUTION:
I added an action listener before uploadingDialog.show() like this:
uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
public void onCancel(DialogInterface dialog) {
myTask.cancel(true);
//finish();
}
});
That way when I press the back button, the above OnCancelListener cancels both dialog and task. Also you can add finish() if you want to finish the whole activity on back pressed. Remember to declare your async task as a variable like this:
MyAsyncTask myTask=null;
and execute your async task like this:
myTask = new MyAsyncTask();
myTask.execute();
I spent a while figuring this out, all I wanted was a simple example of how to do it, so I thought I'd post how I did it. This is some code that updates a library and has a progress dialog showing how many books have been updated and cancels when a user dismisses the dialog:
private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
private ProgressDialog dialog = new ProgressDialog(Library.this);
private int total = Library.instance.appState.getAvailableText().length;
private int count = 0;
//Used as handler to cancel task if back button is pressed
private AsyncTask<Void, Integer, Boolean> updateTask = null;
#Override
protected void onPreExecute(){
updateTask = this;
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setOnDismissListener(new OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
updateTask.cancel(true);
}
});
dialog.setMessage("Updating Library...");
dialog.setMax(total);
dialog.show();
}
#Override
protected Boolean doInBackground(Void... arg0) {
for (int i = 0; i < appState.getAvailableText().length;i++){
if(isCancelled()){
break;
}
//Do your updating stuff here
}
}
#Override
protected void onProgressUpdate(Integer... progress){
count += progress[0];
dialog.setProgress(count);
}
#Override
protected void onPostExecute(Boolean finished){
dialog.dismiss();
if (finished)
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
else
DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
}
}
create some member variables in your activity like
YourAsyncTask mTask;
Dialog mDialog;
use these for your dialog and task;
in onPause() simply call
if(mTask!=null) mTask.cancel();
if(mDialog!=null) mDialog.dismiss();
I would like to improve the code. When you canel the aSyncTask the onCancelled() (callback method of aSyncTask) gets automatically called, and there you can hide your progressBarDialog.
You can include this code as well:
public class information extends AsyncTask<String, String, String>
{
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... arg0) {
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
this.cancel(true);
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onCancelled() {
Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
dialog.hide(); /*hide the progressbar dialog here...*/
super.onCancelled();
}
}
Most of the time that I use AsyncTask my business logic is on a separated business class instead of being on the UI. In that case, I couldn't have a loop at doInBackground(). An example would be a synchronization process that consumes services and persist data one after another.
I end up handing on my task to the business object so it can handle cancelation. My setup is like this:
public abstract class MyActivity extends Activity {
private Task mTask;
private Business mBusiness;
public void startTask() {
if (mTask != null) {
mTask.cancel(true);
}
mTask = new mTask();
mTask.execute();
}
}
protected class Task extends AsyncTask<Void, Void, Boolean> {
#Override
protected void onCancelled() {
super.onCancelled();
mTask.cancel(true);
// ask if user wants to try again
}
#Override
protected Boolean doInBackground(Void... params) {
return mBusiness.synchronize(this);
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
mTask = null;
if (result) {
// done!
}
else {
// ask if user wants to try again
}
}
}
public class Business {
public boolean synchronize(AsyncTask<?, ?, ?> task) {
boolean response = false;
response = loadStuff(task);
if (response)
response = loadMoreStuff(task);
return response;
}
private boolean loadStuff(AsyncTask<?, ?, ?> task) {
if (task != null && task.isCancelled()) return false;
// load stuff
return true;
}
}
I had a similar problem - essentially I was getting a NPE in an async task after the user had destroyed the activity. After researching the problem on Stack Overflow, I adopted the following solution:
volatile boolean running;
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
running=true;
...
}
public void onDestroy() {
super.onDestroy();
running=false;
...
}
Then, I check "if running" periodically in my async code. I have stress tested this and I am now unable to "break" my activity. This works perfectly and has the advantage of being simpler than some of the solutions I have seen on SO.
You can just ask for cancellation but not really terminate it. See this answer.
How to cancel AsyncTask
Full answer is here - Android AsyncTask Example
AsyncTask provides a better cancellation strategy, to terminate currently running task.
cancel(boolean mayInterruptIfitRunning)
myTask.cancel(false)- It makes isCancelled returns true. Helps to cancel the task.
myTask.cancel(true) – It also makes isCancelled() returns true, interrupt the background thread and relieves resources .
It is considered as an arrogant way, If there is any thread.sleep() method performing in background thread, cancel(true) will interrupt background thread at that time. But cancel(false) will wait for it and cancel task when that method completes.
If you invoke cancel() and doInBackground() hasn’t begun execute yet. onCancelled() will invoke.
After invoking cancel(…) you should check value returned by isCancelled() on doInbackground() periodically. just like shown below.
protected Object doInBackground(Params… params) {
while (condition)
{
...
if (isCancelled())
break;
}
return null;
}

AsycTask thread inside doInBackground runs after PostExecution has started

I am using AsyncTask and there are threads running inside the doInBackground method, isn't the purpose of AsyncTask is to let all the code finish executing inside the doInBackground and THEN go to PostExecute? Then why is it that some of my threads are running after the code block in PostExecution has started?
What should I do to solve this problem?
public class myActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
...
}
#Override
public void onClick(View view) {
new myTask().execute();
}
class myTask extends AsyncTask<Void, Void, Void> {
Boolean success;
#Override
protected void onPreExecute() {
success = true
}
#Override
protected Void doInBackground(Void... params) {
try {
Callback callback = new Callback() {
public void successCallback(String name, Object response) {
}
public void errorCallback(String name, error) {
success = false;}
};
} catch (Exception e) {
success = false;
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
if (success == true){
// do something
}
}
}
}
It is inside the error callback, that I want the success field to change to false. But the error callback runs after the postExecute method.
I think that you misunderstand the threading model.
AsyncTask does indeed execute doInBackground first, on a background thread, and then executes onPostExecute on the foreground thread, passing it the result from doInBackground.
However, from the wording of your question, it sounds like you are starting new threads from doInBackground. I'm sure that doInBackground does indeed complete before onPostExecute starts, but there is nothing that would cause the AsyncTask to wait for your additional threads to complete as well.
Edit:
It looks like you could skip the AayncTask altogether. The reason that you have callbacks is probably that method is already asynchronous
But note that you are only creating the callback, never using it. Perhaps you just left that part out?

Android: Combination of AsyncTask and get()

In my android app, I have to load the data on first run through online server. But after splash screen, the loading bar should be displayed with black background. But when I am doing, it shows the content behind the progress bar. what i want is just to display the progress bar (with black background) until the data gets downloaded.
Here is my code of mainactivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
task myTask = new task();
myTask.execute();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//other code
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
//getMenuInflater().inflate(R.menu.main, menu);
return true;
}
class task extends AsyncTask<String, Integer, String> {
private ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog=new ProgressDialog(MainActivity.this, android.R.style.Theme_Translucent_NoTitleBar);
progressDialog.setMessage("Loading...");
progressDialog.show();
}
#Override
protected String doInBackground(String... params) {
try {
Thread.sleep(20000);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(progressDialog!=null)
progressDialog.dismiss();
}
}
}
Well, while copy-pasting some of the brackets may be gone wrong.
But be assured the code is running fine, The problem comes that background is loaded alongwith the loading screen. I got to know about get() method to stop the process being loaded. But this also stopped the view of loading bar.
Please help. Or any alternative way suggestions?
Remove setContentView() from onCreate() and place it in onPostExecute() at the end.

ProgressDialog within AsyncTask not updated

I am having a problem with ProgressDialog UI being frozen when I start the action in the AsyncTask.
My problem is somewhat different than the bunch of other similar question because the my background task consists of two parts:
- first part (loadDB()) is related to the database access
- second part (buildTree()) is related to building the ListView contents and is started with runOnUiThread call
The progress dialog is correctly updated during the 1st part of the task, but not during the 2dn part.
I tried moving the buildTree part in the AsyncTask's onPostExecute but it doesn't help, this part of the code still causes the progress to freeze temporarily until this (sometimes quite lengthy) part of the work is done. I can not recode the buildTree part from scratch because it is based on external code I use.
Any tips on how to resolve this? Is there a method to force updating some dialog on screen?
The code goes here:
public class TreePane extends Activity {
private ProgressDialog progDialog = null;
public void onCreate(Bundle savedInstanceState) {
// first setup UI here
...
//now do the lengthy operation
new LoaderTask().execute();
}
protected class LoaderTask extends AsyncTask<Void, Integer, Void>
{
protected void onPreExecute() {
progDialog = new ProgressDialog(TreePane.this);
progDialog.setMessage("Loading data...");
progDialog.show();
}
protected void onPostExecute(final Void unused) {
if (progDialog.isShowing()) {
progDialog.dismiss();
}
}
protected void onProgressUpdate(Integer... progress) {
//progDialog.setProgress(progress[0]);
}
protected Void doInBackground(final Void... unused)
{
//this part does not block progress, that's OK
loadDB();
publishProgress(0);
//long UI thread operation here, blocks progress!!!!
runOnUiThread(new Runnable() {
public void run() {
buildTree();
}
});
return null;
}
}
public void buildTree()
{
//build list view within for loop
int nCnt = getCountHere();
for(int =0; i<nCnt; i++)
{
progDialog.setProgress(0);
//add tree item here
}
}
}
Don't run your whole buildTree() method inside the UI thread.
Instead, run only the changes you want to make to the UI in the UI thread:
protected Void doInBackground(final Void... unused)
{
//this part does not block progress, that's OK
loadDB();
publishProgress(0);
buildTree();
return null;
}
And then:
public void buildTree()
{
//build list view within for loop
int nCnt = getCountHere();
for(int =0; i<nCnt; i++)
{
progDialog.setProgress(0);
runOnUiThread(new Runnable() {
public void run() {
// update your UI here and return
}
});
// now you can update progress
publishProgress(i);
}
}
You should call AsyncTask's publishProgress method and not the progDialog.setProgress(0); as you call.
Also the buildTree shouln't run on the UI thread since it will block it.
run the logic from the doInBackground method.
note that you don't actually build the ListView, rather you should build it's data model.
look here
something like this:
protected Void doInBackground(final Void... unused)
{
//this part does not block progress, that's OK
loadDB();
publishProgress(0);
buildTree();
}
public void buildTree()
{
//build list view within for loop
int nCnt = getCountHere();
for(int =0; i<nCnt; i++)
{
publishProgress(i); //for exmple...
//add tree item here
}
}

ProgressBar display with some delay in on click of option menu

I am facing the issue with displaying progressbar onItem selected in option menu.
My code is here:
case R.id.mnuLogout:
showDialog(Constants.PROGRESS_DIALOG);
closeOptionsMenu();
if(MyApp.IsLoggedOut())
handler.sendEmptyMessage(Constants.LOGOUT);
else
handler.sendEmptyMessage(Constants.ERROR_MSG);
Progressbar is displayed after completion of IsLogged method.
You're calling get() right after the AsyncTask as executed and lose asynchronous behavior because this method waits until task is finished. You should add all the code in try/catch block to AsyncTask.onPostExecute() method and also dismiss the dialog from this method.
void doLogout() {
new LogoutTask().execute();
}
void dispatchLogoutFinished() {
dismissDialog(Constants.PROGRESS_DIALOG);
if (MyApp.IsLoggedOut()) {
// do something
} else {
// do something else
}
}
private class LogoutTask extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
TheActivity.this.showDialog(Constants.PROGRESS_DIALOG);
}
protected Void doInBackground(Void... params) {
return null;
}
protected void onPostExecute(Long result) {
TheActivity.this.dispatchLogoutFinished();
}
}
And I don't think you need to send messages to the handler. The dispatchLogoutFinished() is executed on the UI thread, so there's no need for synchronization.

Categories

Resources