在从服务调用的 asyncTask 中显示 alertDialog 时出错?

Posted

技术标签:

【中文标题】在从服务调用的 asyncTask 中显示 alertDialog 时出错?【英文标题】:having error when showing alertDialog in asyncTask that is called from service? 【发布时间】:2017-01-16 12:16:18 【问题描述】:

我有一个服务在做一些工作,最后我执行了我的异步任务(它的名字是背景)。 在 asyncTask 中,在 onPostExecute() 中我想显示一个 alertDialog。 但是当我调试我的应用程序时,在我执行我的 asyncTask 的那一行我的服务中会出现错误。这一行:

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

我知道错误是由于我发送到 asyncTask 的上下文而产生的。 我申请 getApplicationContext(); & getBaseContext();& 服务的上下文;但错误并没有消失。 我将此代码应用到 mainActivity 及其上下文中,并且没有发生错误,所以我确信这个错误是因为我将它从我的服务发送到 asyncTask 的构造函数的上下文。

那么,我该怎么办? 感谢您的帮助。

已编辑:这是错误。

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)

这是我的 asyncTask 类:

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

这是我的服务代码:

 public class locationService extends ServiceContext 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;




这是我在 onStartcommand() 中调用的 doSomeThingReapetedly() 方法

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

添加“Pravin Divraniya's”解决方案后,它工作正常,但在调试 onPostExecute() 和 intering to doInBackground() 后 asyncTask 出现错误,该错误是:

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)

【问题讨论】:

你能发布错误信息吗?? 同时发布您的代码。 @thunder413 我编辑了,我添加了我的错误消息。 @PravinDivraniya 哪个代码?asyncTask? 是的,也是您遇到错误的那一行。 【参考方案1】:

根据AsynkTask 文档(阅读线程规则部分)

    必须在 UI 线程上加载 AsyncTask 类。这从 JELLY_BEAN 开始自动完成。 必须在 UI 线程上创建任务实例。 execute(Params...) 必须在 UI 线程上调用。 不要手动调用 onPreExecute()、onPostExecute(Result)、doInBackground(Params...)、onProgressUpdate(Progress...)。 该任务只能执行一次(如果尝试第二次执行将引发异常。)

如果你想重复操作,你可以使用Handler。在onStartCommand 中创建处理程序(因此,从 UI 线程)。然后使用那个Handler来触发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.
    

在您的服务类中创建 Runnable 类实例并从那里调用 AsyncTask。

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

如果您希望停止对 runnable 的重复调用,只需调用 mHandler.removeCallbacks(runnable);

更新:- 我们无法显示带有服务上下文的正常警报对话框,因此以下是两种可能的解决方案。

解决方案 1:-

只有当它是系统警报对话框时,我们才能显示来自服务的对话框。因此,将 TYPE_SYSTEM_ALERT 窗口布局参数设置为 Dialog 如下:

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

但是,它需要 SYSTEM_ALERT_WINDOW 权限。所以,不要忘记在 Manifest 文件中添加这个许可。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

解决方案 2:-

尝试启动一个 Activity 并将 Activity 的 Theme 设置为 Theme.Dialog。 ApiDemo 项目中有一个演示。查看this 链接,了解如何将主题应用于活动或应用程序。

仅供参考

    Service 在其宿主进程的主线程中运行——该服务不会创建自己的线程,也不会在单独的进程中运行(除非您在清单中另外指定)。 IntentService 在工作线程(非 UIThread)上运行。

    您可以从任何线程调用mHandler.postDelayed,因为一旦我们创建了处理程序实例,它就会与它创建的线程相关联。在我们的例子中,它是 UIThread,因为我们在 onStartCommand 方法中创建它。

如有任何疑问,请随时提出。

希望这能帮助您并消除您的疑虑。

【讨论】:

谢谢你,它就像一个魅力:)。重要的是“import android.os.Handler”,但添加此代码后发生错误: 我把它添加到我的问题中请检查一下,谢谢。 我也很感谢您添加的其他信息,它们非常有用。 更新您的答案后,我应用了它并且它工作了,但现在应用程序只工作一次,并且在调试窗口中写道:应用程序正在运行并且在 logCat 中它说:I/Choreographer:跳过 51 帧!应用程序可能在其主线程上做了太多工作。为什么会发生?现在有什么解决方案?你能帮忙吗?请。 您可能在主 UI 线程中执行了一些繁重的操作(网络或一些繁重的计算等),这就是出现此警告的原因。【参考方案2】:

您正在创建在工作线程中运行的doInBackground() 中的对话框,请尝试根据您的要求在onPreExecuteonPostExecute 中创建它。

【讨论】:

【参考方案3】:

我认为错误来自这样一个事实,即您想从一个服务女巫启动一个 ui 并没有在主线程上运行,并且 Service 没有 runOnUiThread 的实现,工作方法是使用 LocalBroadcastManager 和一个广播接收器在任何活动上并从示例示例下方的活动中打开对话框

为您服务

Intent intent = new Intent("showDialog");
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

在片段上的任何活动中

private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() 
    @Override
    public void onReceive(Context context, Intent intent) 
        String action = intent.getAction();
        if(action.equals("showDialog")) 
            // Show your dialog here
        
    
;

别忘了注册接收者

// Register receiver onStart/onCreate
    LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,new IntentFilter("showDialog"));
    // Unregister onDestroy
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);

【讨论】:

是的,我有一个服务,它有一个功能,其中我有一个计时器,而且我知道当一个 timerTask 被声明时,一个新线程准备好了;我使用我的代码:backGround=new BackGround(context); timerTask 中的 backGround.execute(String.valueOf(send_json)) ......但我不明白你说的答案:“在 frgament 上的任何活动中”,我应该在哪里添加你说的这段代码? 我编辑了我的问题并添加了我的服务类。请检查一下,谢谢。

以上是关于在从服务调用的 asyncTask 中显示 alertDialog 时出错?的主要内容,如果未能解决你的问题,请参考以下文章

在从 Internet 下载数据期间,在 AsyncTask 的 onProgressUpdate 中显示确定的 ProgressBar

Android 我应该使用 AsyncTask 还是 IntentService 进行 REST API 调用? [复制]

从 DialogFragment 调用的 AsyncTask 中的 ProgressDialog 未显示

使用 AsyncTask 并将值返回给 UI 主线程

AsyncTask 不断重复 onResume

从 Activity 调用时,另一个 AsyncTask 类中的 ProgressDialog 未显示