Problems in Calling AsyncTask from IntentService

2019-04-08 21:40发布

问题:

I have created IntentService class and performing asyncTask but getting exception when onPreExecute() is called at this code line pDialog.show();

AsyncHandlerService Class ---

public class AsyncHandlerService extends IntentService{
ProgressDialog pDialog;
HttpPost post;
HttpResponse response;
Context ctx;

public AsyncHandlerService() {
    super("AsyncHandlerService");
    ctx = this;
}

@Override
protected void onHandleIntent(Intent intent) {
    new LoadDeviceInfo().execute();   
}


class LoadDeviceInfo extends AsyncTask<String, String, String> {

@Override
protected void onPreExecute() {
    super.onPreExecute();
    pDialog = new ProgressDialog(ctx);
    pDialog.setMessage("Updating device info...");
    pDialog.setIndeterminate(false);
    pDialog.setCancelable(false);
    pDialog.show(); //Exception here..
}

protected String doInBackground(String... args) {
}

protected void onPostExecute(String file_url) {
    pDialog.dismiss();
}

UPDATE:

I am calling the IntentService in the broadcast receiver that has the intent filter of android.intent.action.PACKAGE_REPLACED defined in android manifest. The code ---

public class OnUpgradeBroadcastReceiver extends BroadcastReceiver {
Context activity;
@Override
    public void onReceive(final Context context, final Intent intent) {
         activity = context;
         Intent msgIntent = new Intent(activity, AsyncHandlerService.class);
            activity.startService(msgIntent);
    }
}

Error Log:

com.testapp.main fatal error : Unable to add window -- 
token null is not for an application
android.view.WindowManager$BadTokenException: Unable to add window -- 
token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:588)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:326)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
at android.view.WindowManagerImpl$CompatModeWrapper.
addView(WindowManagerImpl.java:149)
at android.app.Dialog.show(Dialog.java:277)
at com.testapp.main.AsyncHandlerService$LoadDeviceInfo.
onPreExecute(AsyncHandlerService.java:62)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:586)
at android.os.AsyncTask.execute(AsyncTask.java:534)

回答1:

First, IntentService already uses a background thread. You do not need another background thread. Do the work that needs to be done in the background in onHandleIntent().

Second, a Service cannot display a Dialog. Instead, let the UI layer of your app know that the work was done via a message on an event bus (e.g., LocalBroadcastManager, greenrobot's EventBus, Square's Otto). If the UI layer does not handle the event, your service can raise a Notification or otherwise let the user know about the work that was done, if that is needed.



回答2:

Service isn't a UI thread.
Since you try to display a ProgressDialog from a service context, it can't be completed.
Try this solution:
https://stackoverflow.com/a/4369755/1405268



回答3:

If for whatever reason you really really really want to use an AsyncTask (e.g. you've set up your framework to use AsyncTask to make calls to some web api) you can always use wait/notify such as:

public class GetCacheIntentService extends DebuggableIntentService implements ApiAsyncTask.Callback {
    private static final String ACTION_GET_CACHE = "action.GET_CACHE";

    private static final String EXTRA_INT_START = "extras.START";
    private static final String EXTRA_INT_LIMIT = "extras.LIMIT";

    private static final int API_GET_CACHE = 0;

    private final Object mApiCallLock = new Object();
    private GetCacheResponse getCacheResponse;

    public GetCacheIntentService() {
        super("GetCacheIntentService");
        setIntentRedelivery(true);
    }

    public static void startServiceActionGetCache(Context context, int start, int limit) {
        Intent intent = new Intent(context, GetCacheIntentService.class);
        intent.setAction(ACTION_GET_CACHE);
        intent.putExtra(EXTRA_INT_START, start);
        intent.putExtra(EXTRA_INT_LIMIT, limit);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent == null) {
            return;
        }

        String action = intent.getAction();

        if (ACTION_GET_CACHE.equals(action)) {
            int start = intent.getIntExtra(EXTRA_INT_START, 0);
            int limit = intent.getIntExtra(EXTRA_INT_LIMIT, 100);
            getCache(start, limit);
        }
    }

    private void getCache(int start, int limit) {
        GetCacheTask task = new GetCacheTask(this, API_GET_CACHE);
        task.setStart(start);
        task.setLimit(limit);
        task.execute();

        synchronized (mApiCallLock) {
            try {
                mApiCallLock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            }
        }

        processResponse(mGetCacheResponse);
    }

    public void processResponse(GetCacheResponse response) {
           // do something
    }

    @Override
    public void onRequestFailed(int id, ApiResponse apiResponse) {
        synchronized (mApiCallLock) {
            switch (id) {
                case API_GET_CACHE:
                    break;
            }
            mApiCallLock.notify();
        }
    }

    @Override
    public void onRequestSuccess(int id, ApiResponse response) {
        synchronized (mApiCallLock) {
            switch (id) {
                case API_GET_CACHE:
                    mGetCacheResponse = (GetCacheResponse) response;
                    break;
            }
            mApiCallLock.notify();
        }
    }
}

this is quite ugly though :(



回答4:

Not a good practise to call Asynctask from an Intent service. If you need to do spin other thread from IntentService consider using Executor.

ExecutorService es = Executors.newFixedThreadPool(5);
es.execute(new Runnable() {
                @Override
                public void run() {

                }
            });

es.execute(new Runnable() {
                @Override
                public void run() {
                }
            });
es.shutdown();
es.awaitTermination(1, TimeUnit.HOURS);