从 IntentService 调用 AsyncTask 的问题

Posted

技术标签:

【中文标题】从 IntentService 调用 AsyncTask 的问题【英文标题】:Problems in Calling AsyncTask from IntentService 【发布时间】:2014-07-19 02:06:56 【问题描述】:

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

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

更新:

我在广播接收器中调用IntentService,它在android manifest 中定义了android.intent.action.PACKAGE_REPLACED 的意图过滤器。代码---

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)

【问题讨论】:

【参考方案1】:

首先,IntentService 已经使用了后台线程。你不需要另一个后台线程。在onHandleIntent()后台做需要做的工作。

第二,a Service cannot display a Dialog。相反,让应用程序的 UI 层知道工作是通过事件总线上的消息完成的(例如,LocalBroadcastManager、greenrobot 的 EventBus、Square 的 Otto)。如果 UI 层不处理该事件,您的服务可以提出 Notification 或以其他方式让用户知道已完成的工作(如果需要)。

【讨论】:

检查我的更新。我将asyncTaskbroadcast receiver 委托给service。所以 UI 层没有处理这个事件。那我应该使用通知还是有其他好方法? @VedPrakash:“我应该使用通知”——如果您的应用程序不在前台,是的。正如我在回答中指出的那样,您可以使用事件总线尝试告诉前台 UI 工作已完成,如果您的 UI 不在前台,则显示 Notification。以下是我引用的三个事件总线实现中每一个的相同基本示例应用程序的三个实现:github.com/commonsguy/cw-omnibus/tree/master/EventBus 我显示对话框的目的是在服务执行其后台工作时阻止用户执行任何操作,因为该工作正在更新应用程序包信息(调用意图 PACKAGE_REPLACED)每当应用程序从谷歌播放。这项工作完成后,用户可以使用这个最新的包信息来创建某些记录。所以Notification 在这里不起作用。 @VedPrakash:您不能“阻止用户做任何事情”,除非通过您自己的自定义 ROM。而弹出一个对话框,仅仅因为数百个应用程序中的一个恰好在做某事,对用户来说是非常敌对的。 如果用户碰巧打开了你的应用程序,并且如果你确定你的应用程序由于你正在做的事情而在那个时刻无法使用,处理那个在您应用的 UI 中 好的。因此,如果用户从 Google Play 更新了应用程序,然后打开应用程序,那么上面的 OnUpgradeBroadcastReceiver 会立即被调用,或者当应用程序在更新后处于后台时它甚至会被调用。【参考方案2】:

服务不是 UI 线程。 由于您尝试从服务上下文显示ProgressDialog,因此无法完成。 试试这个解决方案:https://***.com/a/4369755/1405268

【讨论】:

【参考方案3】:

如果出于某种原因您真的真的 真的想要使用 AsyncTask(例如,您已将框架设置为使用 AsyncTask 来调用某些 web api)您可以随时使用等待/通知,例如:

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

虽然这很丑:(

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

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

【讨论】:

以上是关于从 IntentService 调用 AsyncTask 的问题的主要内容,如果未能解决你的问题,请参考以下文章

Android PushNotification IntentService 并调用 AsyncTask 函数从服务器数据库获取数据并更新 sqlite 数据库

未从 IntentService 调用 onLocationChanged

从片段调用 Android AsyncTask 没有调用其成员 - doInbackground、onpreexecute、onpostexecute

Android:intentservice,如何中止或跳过handleintent队列中的任务

使用 IntentService 进行位置监听,但在 onHandleIntent 之后立即调用 onDestroy

ResultReceiver.send 只能从同一个库组中调用