Reputation: 674
I have a problem with AsyncTasks. I have this in onCreate()
new ProgressTask().execute();
And this is my Task:
private class ProgressTask extends AsyncTask<String, Integer, Boolean> {
private ProgressDialog dialog;
public ProgressTask() {
//dialog = new ProgressDialog(getBaseContext());
}
/** progress dialog to show user that the backup is processing. */
/** application context. */
private Context context;
protected void onPreExecute() {
dialog = new ProgressDialog(getBaseContext());
dialog.setTitle("Loading connection...");
dialog.setMessage("Loading...");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMax(100);
dialog.setProgress(0);
dialog.show();
}
@Override
protected void onPostExecute(final Boolean success) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (success) {
Toast.makeText(context, "OK", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Error", Toast.LENGTH_LONG).show();
}
}
protected Boolean doInBackground(final String... args) {
try{
//Get device ID
publishProgress(0);
loading.setMessage("Getting device ID...");
try {
getDeviceId();
} catch (Exception ex) {
ErrorMessage("Couldn't get device ID");
ErrorAlert += "Error 101: Couldn't get device ID\n";
}
Thread.sleep(2000);
publishProgress(20);
//Open connection
loading.setMessage("Opening connection...");
try {
openConnection();
} catch (IOException ex) {
ErrorMessage("Couldn't open connection");
ErrorAlert += "Error 102: Couldn't open connection\n";
}
Thread.sleep(2000);
publishProgress(40);
//Testing connection
loading.setMessage("Testing connection...");
if (!mmSocket.isConnected()) {
ErrorMessage("Test failed!");
ErrorAlert += "Error 103: Test failed!\n";
}
Thread.sleep(2000);
publishProgress(60);
//Calibrate sensors
loading.setMessage("Calibrating sensors...");
try {
sendCommand("c");
} catch (IOException ex) {
ErrorMessage("Couldn't calibrate sensors");
ErrorAlert += "Error 104: Couldn't calibrate sensors\n";
}
Thread.sleep(2000);
publishProgress(80);
//Finish
loading.setMessage("Finishing...");
Thread.sleep(1000);
publishProgress(90);
//Clear
loading.setMessage("Clearing some stuff...");
Thread.sleep(1000);
publishProgress(100);
return true;
} catch (Exception e){
return false;
}
}
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values[0]);
dialog.setProgress(values[0]);
}
}
But when I launch the activity, the app stops :(
This is the output:
03-06 12:32:35.902 10648-10648/com.jules_citronic.racecarcontrol E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.jules_citronic.racecarcontrol, PID: 10648
Theme: themes:{default=overlay:theme.lonecm12.kikkosart.com.lonecm12, iconPack:theme.lonecm12.kikkosart.com.lonecm12, fontPkg:theme.lonecm12.kikkosart.com.lonecm12, com.android.systemui=overlay:theme.lonecm12.kikkosart.com.lonecm12, com.android.systemui.navbar=overlay:theme.lonecm12.kikkosart.com.lonecm12}
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.jules_citronic.racecarcontrol/com.jules_citronic.racecarcontrol.control}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2450)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2520)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1363)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5466)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:571)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:310)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:86)
at android.app.Dialog.show(Dialog.java:319)
at com.jules_citronic.racecarcontrol.control$ProgressTask.onPreExecute(control.java:109)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:604)
at android.os.AsyncTask.execute(AsyncTask.java:551)
at com.jules_citronic.racecarcontrol.control.onCreate(control.java:84)
at android.app.Activity.performCreate(Activity.java:6251)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2403)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2520)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1363)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5466)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
I'm not so good with Threads and stuff so I hope that someone can help me.
EDIT: I changed getBaseContext to context and placed "context = control.this". by the way, control is the name of my activity/class. Now it launched the activity but never shows a ProgressDialog and doesn't connect to Bluetooth. It gives me a toast with "Error".
EDIT: The exception exports this:
03-06 13:09:04.858 15370-15535/com.jules_citronic.racecarcontrol E/MYAPP: exception
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at android.widget.Toast$TN.<init>(Toast.java:347)
at android.widget.Toast.<init>(Toast.java:103)
at android.widget.Toast.makeText(Toast.java:261)
at com.jules_citronic.racecarcontrol.control.ErrorMessage(control.java:301)
at com.jules_citronic.racecarcontrol.control$ProgressTask.doInBackground(control.java:135)
at com.jules_citronic.racecarcontrol.control$ProgressTask.doInBackground(control.java:90)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
EDIT: It worked :). This is the final class:
private class ProgressTask extends AsyncTask<String, Integer, Boolean> {
private ProgressDialog dialog;
private Context context;
public ProgressTask(Context context) {
this.context = context;
//dialog = new ProgressDialog(getBaseContext());
}
/** progress dialog to show user that the backup is processing. */
/** application context. */
protected void onPreExecute() {
dialog = new ProgressDialog(context);
dialog.setTitle("Loading connection...");
dialog.setMessage("Loading...");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMax(100);
dialog.setProgress(0);
dialog.show();
}
@Override
protected void onPostExecute(final Boolean success) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (success) {
Toast.makeText(context, "OK", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Error", Toast.LENGTH_LONG).show();
}
}
protected Boolean doInBackground(final String... args) {
try {
Looper.prepare();
} catch (Exception ex) {
Log.e("MYAPP", "Exception in looper " + ex.getMessage());
}
try{
//Get device ID
publishProgress(0);
try {
getDeviceId();
} catch (Exception ex) {
ErrorMessage("Couldn't get device ID");
ErrorAlert += "Error 101: Couldn't get device ID\n";
}
Thread.sleep(2000);
publishProgress(20);
//Open connection
try {
openConnection();
} catch (IOException ex) {
ErrorMessage("Couldn't open connection");
ErrorAlert += "Error 102: Couldn't open connection\n";
}
Thread.sleep(2000);
publishProgress(40);
//Testing connection
if (!mmSocket.isConnected()) {
ErrorMessage("Test failed!");
ErrorAlert += "Error 103: Test failed!\n";
}
Thread.sleep(2000);
publishProgress(60);
//Calibrate sensors
try {
sendCommand("c");
} catch (IOException ex) {
ErrorMessage("Couldn't calibrate sensors");
ErrorAlert += "Error 104: Couldn't calibrate sensors\n";
}
Thread.sleep(2000);
publishProgress(80);
//Finish
Thread.sleep(1000);
publishProgress(90);
//Clear
Thread.sleep(1000);
publishProgress(100);
return true;
} catch (Exception e){
Log.e("MYAPP", "exception", e);
return false;
}
}
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values[0]);
dialog.setProgress(values[0]);
if (values[0] == 0){
dialog.setMessage("Getting device ID...");
}
if (values[0] == 20){
dialog.setMessage("Opening connection...");
}
if (values[0] == 40){
dialog.setMessage("Testing connection...");
}
if (values[0] == 60){
dialog.setMessage("Calibrating sensors...");
}
if (values[0] == 80){
dialog.setMessage("Finishing...");
}
if (values[0] == 90){
dialog.setMessage("Clearing some stuff...");
}
}
}
Upvotes: 1
Views: 3367
Reputation: 5940
add
try {
Looper.prepare();
} catch (Exception ex) {
Log.e(tag, "Exception in looper " + ex.getMessage());
}
these lines in doInBackground
method. These lines should be at start of doInBackground
method. I faced same exception then i tried these lines.
OR
Remove these lines from onPreExecute
method and declare in constructor.
dialog = new ProgressDialog(getBaseContext());
dialog.setTitle("Loading connection...");
dialog.setMessage("Loading...");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMax(100);
dialog.setProgress(0);
And in onPreExecute
method, use only dialog.show()
method.
Hope it'll help you.
Refer this answer for more details
Upvotes: 0
Reputation: 41510
The posted exception indeed happens because your context is a base context and not an activity. However, I think this whole setup is not really correct.
The main problem with long-running tasks is that they may outlive the activity if it gets recreated for some reason (the most common is changing the device orientation). In that case you may end up calling methods of the activity which is already gone, and that will lead to exceptions and unexpected behavior.
I have previously left another answer which outlines how you can deal with this. In general, you can create a loader which handles the background work and sends back the objects representing the loaded data or the current state. The loader framework can automatically attach and detach to the activity so you always work with the active instance of it and avoid the problems.
Upvotes: 0
Reputation: 669
Change constructor and onPreExecute()
method of your AsyncTask like this:
public ProgressTask(Context c) {
//dialog = new ProgressDialog(getBaseContext());
context = c;
}
/** progress dialog to show user that the backup is processing. */
/** application context. */
private Context context;
protected void onPreExecute() {
dialog = new ProgressDialog(this.context);
dialog.setTitle("Loading connection...");
dialog.setMessage("Loading...");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMax(100);
dialog.setProgress(0);
dialog.show();
}
Then, try to create AsyncTask instance in your activity :
ProgressTask task = new ProgressTask(YourActivity.this);
task.execute();
Upvotes: 0
Reputation: 179
Since AsyncTask
does not inherit from Context, it has no way of accessing the current context.
You have to pass the current context as a parameter to the constructor and set it there.
private Context context;
public ProgressTask(Context context) {
this.context = context;
}
And then pass context from new ProgressTask(context).execute();
Upvotes: 1