sama sama - 3 months ago 39
Android Question

having error when showing alertDialog in asyncTask that is called from service?

I have a service that doing some work and at the end I execute my asynctask(its name is background).
In asyncTask,In onPostExecute() I want to show an alertDialog.
but when i debug my app, error accures in my service at the line that i execute my asyncTask. this line:

backGround=new BackGround(context);
backGround.execute(String.valueOf(send_json))


I know that the error accures because of the context that i send to asyncTask.
I apply getApplicationContext(); & getBaseContext();& service's context,too; but error doesn't disapear.
I apply this code in the mainActivity with its context and no error occures,so I am sure this error is because of the context that i send it from my service to asyncTask's constructor.

So, what can I do?
I appreciate any help.

Edited:this is the error.

09-08 18:39:35.253 20251-20813/ir.blog.trafik E/AndroidRuntime: FATAL EXCEPTION: Timer-0
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:197)
at android.os.Handler.<init>(Handler.java:111)
at android.app.Dialog.<init>(Dialog.java:111)
at android.app.AlertDialog.<init>(AlertDialog.java:114)
at android.app.AlertDialog$Builder.create(AlertDialog.java:931)
at ir.blog.trafik.BackGround.onPreExecute(BackGround.java:88)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:586)
at android.os.AsyncTask.execute(AsyncTask.java:534)
at ir.blog.trafik.locationService.json_maker(locationService.java:538)
at ir.blog.trafik.locationService$1.run(locationService.java:588)
at java.util.Timer$TimerImpl.run(Timer.java:284)


this is my asyncTask class:

public class BackGround extends AsyncTask < String , Void , String > {
Context context;
AlertDialog alertDialog;
public BackGround(Context context){
this.context=context;
}

@Override
protected String doInBackground(String... params){
String location_url ="http://192.168.1.90/server_connection.php";
try{
URL url1=new URL(location_url);
HttpURLConnection httpURLConnection = (HttpURLConnection)url1.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
OutputStream stream_upload=httpURLConnection.getOutputStream();
BufferedWriter buffer_writer=new BufferedWriter(new OutputStreamWriter(stream_upload,"UTF-8"));
// String PostData= URLEncoder.encode()

buffer_writer.write(String.valueOf(params));


buffer_writer.flush();
buffer_writer.close();
stream_upload.close();





InputStream stream_dawnload=httpURLConnection.getInputStream();
BufferedReader bufferreader=new BufferedReader(new InputStreamReader(stream_dawnload,"iso-8859-1"));
String result="";
String line;
while ((line = bufferreader.readLine()) != null) {
result += line;}
bufferreader.close();
stream_upload.flush();
stream_dawnload.close();
httpURLConnection.disconnect();

return result;



}catch (Exception e){
e.printStackTrace();
}

return null;


}

@Override
protected void onPreExecute() {
alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("Login status");
}

@Override
protected void onPostExecute(String result) {

alertDialog.setMessage(result);
alertDialog.show();
}


this is my service's code:

public class locationService extends Service{Context context;BackGround backGround ;


@Override
public void onCreate() {
context =this;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

Toast.makeText(context,"service started",Toast.LENGTH_LONG).show();


doSomeThingRepeatedly(context);


return Service.START_FLAG_REDELIVERY;



}


and this is my doSomeThingReapetedly() method that i called it in onStartcommand()

private void doSomeThingRepeatedly(final Context context) {
timer.scheduleAtFixedRate(new TimerTask() {

@Override
public void run() {
......
backGround=new BackGround(context);
backGround.execute(String.valueOf(send_json));
}


}, 0, UPDATE_INTERVAL);




After adding "Pravin Divraniya's" solution, it works fine but an error occurs in asyncTask after debuging onPostExecute() and intering to doInBackground(), that error is:

FATAL EXCEPTION: main
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:804)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:288)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:73)
at android.app.Dialog.show(Dialog.java:287)
at ir.blog.trafik.BackGround.onPostExecute(BackGround.java:103)
at ir.blog.trafik.BackGround.onPostExecute(BackGround.java:22)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5493)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
at dalvik.system.NativeStart.main(Native Method)

Answer

As per AsynkTask documentation (Read Threading rules section)

  1. The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
  2. The task instance must be created on the UI thread.
  3. execute(Params...) must be invoked on the UI thread.
  4. Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
  5. The task can be executed only once (an exception will be thrown if a second execution is attempted.)

If you want to do repeated operation you can do it using Handler. Create Handler in onStartCommand (so, from the UI thread). Then use that Handler to trigger AsyncTask.

    private Handler mHandler = null;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        mHandler = new Handler();
        mHandler.postDelayed(runnable,1000);
//Here 1000 is delayMillis,  you can set your delay here.
    }

create Runnable class instance inside your service class and call AsyncTask from there.

Runnable runnable = new Runnable() {
        @Override
        public void run() {
            backGround=new BackGround(context);
            backGround.execute(String.valueOf(send_json));
        }
    };

If you wish to stop repeted calls to runnable, simply call mHandler.removeCallbacks(runnable);

Just for Information

  1. A Service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify in manifest otherwise). IntentService runs on worker thread(non UIThread).

  2. You can call mHandler.postDelayed from any thread, because once we are create handler instance it is associated with the Thread in which it create. in our case it is UIThread as we are creating it in onStartCommand method.

Feel free to ask any query.

Hope this will help you and clear your doubts.