从IntentService调用AsyncTask的问题

5

我创建了 IntentService 类并正在执行 asyncTask,但当调用 onPreExecute() 时,在这行代码 pDialog.show(); 报出异常。

AsyncHandlerService 类 ---

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();
}

更新:

我在广播接收器中调用了具有android.intent.action.PACKAGE_REPLACED意图过滤器的IntentService,该过滤器在Android清单中定义。 代码如下 ---

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);
    }
}

错误日志:

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)
5个回答

13

首先,IntentService 已经使用了后台线程,您不需要另一个后台线程。在 onHandleIntent() 中执行需要在后台完成的工作。

其次,一个Service不能显示Dialog。相反,通过事件总线(例如LocalBroadcastManager,greenrobot的EventBus,Square的Otto)向应用程序的UI层发送消息,让UI层知道工作已经完成。如果UI层没有处理事件,您的服务可以提醒用户有关已完成的工作的信息,例如通过展示一个Notification来实现。


我展示对话框的目的是为了在服务执行后台工作时阻止用户进行任何操作,因为该工作正在更新应用程序包信息(调用意图PACKAGE_REPLACED),每当应用程序从Google Play更新时。完成此工作后,用户可以使用此最新的包信息创建某些记录。因此,“通知”在这里不起作用。 - My God
1
@VedPrakash:除非使用自定义ROM,否则无法“阻止用户执行任何操作”。仅因为数百个应用程序中的一个正在执行某些操作而弹出对话框,对用户非常不友好。如果用户恰好打开了您的应用程序,并且您确定由于您正在进行的操作而无法使用您的应用程序,请在您的应用程序UI中处理它。 - CommonsWare
@VedPrakash:我不确定。我的猜测是,在用户有机会打开更新后的应用程序之前,广播将会发送出去。根据定义,在更新过程中,应用程序本身不会在后台运行,因为您的进程必须被终止才能进行更新。启动一个新进程来发送广播给您的应用程序。 - CommonsWare
@VedPrakash:在更新过程中应该停止您的进程。 - CommonsWare
Intent Service + AsyncTask + httpClient 失败了?httpClient 不能在非异步进程中工作。需要将同一功能同时变为同步和异步吗? - e-info128
显示剩余6条评论

0

从Intent服务中调用Asynctask不是一个好的做法。如果您需要从IntentService中启动其他线程,请考虑使用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);

0

如果出于某种原因,您真的 非常非常 非常 想要使用 AsyncTask(例如,您已经设置了框架以使用 AsyncTask 来调用某些Web API),则始终可以使用 wait/notify,例如:

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();
        }
    }
}

这个看起来很丑 :(


0

0

将AsyncTask放在IntentService子类甚至是JobIntentService子类中使用,这不是一个好的实践。在JobIntentServices的情况下,它也会导致崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接