在后台服务中使用AsyncTask添加Android进度对话框,遇到致命异常。

7
  1. 我正在从计划服务中调用一个异步任务,每10分钟运行一次。

  2. 在运行服务时,进度对话框OnpreExecute中获取异常。

错误

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:594)

at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:259)

at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)

at android.app.Dialog.show(Dialog.java:286)

编辑1: 闹钟管理器,每5分钟调用服务

/*Alarm manager Service for From Server*/
private void setServerFetch() {
    // for  to Server to GPS PING
    Intent myIntent1 = new Intent(LoginPage.this, AlarmService.class);
    pendingintent1 = PendingIntent.getService(LoginPage.this, 1111, myIntent1, 0);
    AlarmManager alarmManager5 = (AlarmManager) getSystemService(ALARM_SERVICE);
    Calendar calendar1 = Calendar.getInstance();
    calendar1.setTimeInMillis(System.currentTimeMillis());
    calendar1.add(Calendar.SECOND, 1);
    alarmManager5.set(AlarmManager.RTC_WAKEUP, calendar1.getTimeInMillis(), pendingintent1);
    alarmManager5.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar1.getTimeInMillis(), 300 * 1000, pendingintent1);

}

从Service OnStart调用AsyncTask

 @Override
    public void onStart(Intent intent, int startId)
    {
        super.onStart(intent, startId);


        try
        {

                Asynctask_Incident task=new Asynctask_Incident();
                task=new();

        }
        catch (Exception e)
        {
            e.printStackTrace();
            Log.i("PING", "EXCEPTION in reading Data from Web Async task ONstart.!");

        }

    }

异步任务类 onStart 方法

public class Asynctask_Incident extends AsyncTask<String, Void, Void>
    {


        @Override
        protected void onPreExecute()
        {
            super.onPreExecute();

            runOnUiThread(new Runnable() {

                @Override
                public void run() {

                    if (!pDialog.isShowing())
                    {
                        pDialog = new ProgressDialog(appContext);
                        pDialog.setCanceledOnTouchOutside(false);
                        pDialog.setCancelable(false);
                        pDialog.setMessage("Please Wait Updating Data From...");
                        pDialog.show();
                    }
                }
            });


        }

        @Override
        protected Void doInBackground(String... params)
        {
            try {
                getAPICall();

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

                if (pDialog.isShowing()) {
                    pDialog.dismiss();
                }

            }

            return null;
        }
        @Override
        protected void onPostExecute(Void aVoid)
        {

            super.onPostExecute(aVoid);

            if (pDialog.isShowing()) {
                pDialog.dismiss();
            }


        }


    }

帮我解决这个问题。


你是从Activity还是其他类开始启动服务的? - Abdulhamid Dhaiban
@AbdulhamidDhaiban - 在活动中使用闹钟管理器启动服务。 - Kumar
我尝试了这个链接:https://github.com/nickfox/Update-Android-UI-from-a-Service - Kumar
有两种情况: 1)如果您想从后台服务运行定期任务,即使应用程序已关闭,也不应显示进度对话框。这是糟糕的用户体验,并且违反 Android 政策,在应用程序外显示对话框。 2)如果您想运行进度对话框,则应打开您的应用程序。然后,您可以启动一个定期处理程序来启动 AsyncTask。请告诉我哪种情况适用于您,以便为您提供详细的代码。 - Abdulhamid Dhaiban
请查看我更新的答案中的scenario2。 - Abdulhamid Dhaiban
显示剩余2条评论
6个回答

3

初始化你的ProgressDialog

OnPreExecute();

runOnUiThread(new Runnable() {

                @Override
                public void run() {

                    if (pDialog == null)
                    {
                       pDialog = new ProgressDialog(appContext);
                        pDialog.setCanceledOnTouchOutside(false);
                        pDialog.setCancelable(false);
                        pDialog.setMessage("Please Wait Updating Data From...");

                    }

                   pDialog.show();
                }
            });

OnPostExecute();

 pDialog.dismiss();

等一下,我正在尝试。我已经在Oncreate中初始化了Prgressdialog。 - Kumar
仍然出现相同的错误 :( 请帮帮我。 - Kumar
我在这一行尝试使用pDialog.show(),但出现了异常:android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application。 - Kumar

3

实际上,您无法从服务中启动进度对话框,因为它需要活动上下文而不是应用程序上下文,这在您的情况下可能为空。

更多信息请查看:链接1链接2链接3

如果您想基于服务操作触发进度对话框,可以使用观察者设计模式,请查看此处

更新: 如果您的应用正在运行,可以使用Handler并每5分钟运行一次。

这里是一个完整的示例:

public class TestActivity extends AppCompatActivity {

private Handler handler;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);

    handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            //
            new Asynctask_Incident(TestActivity.this).execute("url");
            handler.postDelayed(this, 5 * DateUtils.MINUTE_IN_MILLIS);
        }
    }, 0);
}

public class Asynctask_Incident extends AsyncTask<String, Void, Void> {


    ProgressDialog pDialog;
    Context appContext;

    public Asynctask_Incident(Context ctx) {
        appContext = ctx;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();

            pDialog = new ProgressDialog(appContext);
            pDialog.setCanceledOnTouchOutside(false);
            pDialog.setCancelable(false);
            pDialog.setMessage("Please Wait Updating Data From...");
            pDialog.show();


    }

    @Override
    protected Void doInBackground(String... params) {
        try {
            getAPICall();

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

            if (pDialog.isShowing()) {
                pDialog.dismiss();
            }

        }

        return null;
    }

    private void getAPICall() {

        //5 seconds delay for test, you can put your code here
        try {
            Thread.sleep(5 * DateUtils.SECOND_IN_MILLIS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onPostExecute(Void aVoid) {

        super.onPostExecute(aVoid);

        if (pDialog.isShowing()) {
            pDialog.dismiss();
        }


    }


}

}

请检查我的更新问题。使用Observer添加任何代码片段以及我的问题。 - Kumar
谢谢您的回复,我的服务在哪里?我的 Web 服务方法放置在服务 Onstart 中。 - Kumar
1
@Kumer,这段代码替换了一个服务。你不需要使用Service和AlarmManager。因为Service的主要目的是在应用程序关闭时运行。但是当您的应用程序正在运行时,您可以使用handler来重复执行您的AsyncTask。 - Abdulhamid Dhaiban
非常感谢朋友:),它达到了我预期的效果。 - Kumar
还有一个疑问,如果另一个Activity页面在此页面之上打开,它是否会运行每5分钟处理程序? - Kumar
另外,您也可以在其他活动中获取处理程序: 请查看此网址:[链接](http://www.vogella.com/tutorials/AndroidBackgroundProcessing/article.html#handler) - Abdulhamid Dhaiban

1
您不需要在onPreExecute方法中启动对话框线程,因为此方法始终在UI线程中调用。通过调用线程,您会延迟它。因此,doInBackground可能会在创建对话框之前发生。
另外,您不应该在doInBackground方法中调用任何修改UI的内容,因为此方法在工作线程中运行。任何UI调用必须在主线程中进行。onPostExecute由主线程调用。因此,请将与对话框相关的调用放置在其中,但不要在doInBackground中使用。
这些在doInBackground中的行需要被删除。
if (pDialog.isShowing()) {
                        pDialog.dismiss();
                    }

嗨,朋友,我从DoinBackground中删除了PDialog,还在方法调用之前删除了异步任务,我在Retrofit调用之前添加了Progressdialog,但是出现了错误 - android.view.WindowManager$BadTokenException:无法添加窗口--令牌null不是应用程序的。请帮助我修复,否则请给我一些代码片段以及我的代码。 - Kumar
1
检查 Abdulhamid Dhaiban 的答案。你不能在服务中显示对话框,必须要有一个屏幕上的活动。在你的服务中,当你想要显示对话框时,你需要先启动一个活动,然后在该活动中显示对话框。 - Ray

1

当上下文不活动时,会出现异常Exception:android.vi‌​ew.WindowManager$BadT‌​okenException: Unable to add window -- token null is not for an application。可能有其他原因导致此异常,但上下文是主要原因。此外,如果以前显示的对话框没有被解除,可能会发生异常。

请尝试使用以下代码:

runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if(appContext != null) {

                    // if dialog is already showing, hide it
                    if(pDialog != null && pDialog.isShowing()) {
                        pDialog.dismiss();
                    }

                    if (pDialog == null) {
                        pDialog = new ProgressDialog(appContext);
                        pDialog.setCanceledOnTouchOutside(false);
                        pDialog.setCancelable(false);
                        pDialog.setMessage("Please Wait Updating Data From...");
                    }
                    pDialog.show();

                } else {
                    Log.e("Error","Context is Null");
               }
            }
        });

可以添加一个额外的检查: http://dimitar.me/android-displaying-dialogs-from-background-threads/


仍然出现相同的异常... :( 未修复 - Kumar
当后台服务与异步任务同时运行时,是否有可能显示进度条? - Kumar

1

1) 您不需要在Runnable内设置ProgressDialogonPreExecute()onPostExecute()中的任何内容已经在UI线程上运行。 只有doInBackground()在非UI线程上运行。

2) 将AsyncTask类放入MainActivity中,在MainActivity中调用它,而不是从您的Service中调用。 从MainActivity中调用您的AsyncTask,例如:

new MyAsyncTask(MainActivity.this).execute("");

3) 最后,将这个构造函数放到你的AsyncTask类中:

public MyAsyncTask(Context context) {
        appContext = context;
    }

如何将我的服务(由闹钟管理器每5分钟启动)连接到主活动异步任务?请添加任何代码片段。 - Kumar
1
我不确定我理解了吗?看起来你想让AsyncTask任务在Service启动时开始?我对Alarm Manager不是很熟悉,但我相信有一种方法可以使用AlarmManager设置AsyncTask按计划启动,然后您可以在onPostExecute中启动Service。如果这是您想要实现的内容,请搜索“AsyncTask AlarmManager Service”,会出现一些解决方案。 - ShadowGod
好的,我在尝试中,如果有疑问会及时告诉您。 - Kumar

0

看起来你的上下文没有正确的资源集。 确保你正在使用正确的上下文。

Context context = this;
ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.show();

其中 "this" 指的是 AppCompatActivity 或 Activity 的上下文


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