在从服务调用的 asyncTask 中显示 alertDialog 时出错?
having error when showing alertDialog in asyncTask that is called from service?
我有一个服务做一些工作,最后我执行我的异步任务(它的名字是背景)。
在 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.run(locationService.java:588)
at java.util.Timer$TimerImpl.run(Timer.java:284)
这是我的异步任务class:
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 Service{Context 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.access0(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)
您正在 doInBackground()
中创建在工作线程中运行的对话框,请根据您的要求尝试在 onPreExecute
或 onPostExecute
中创建它。
我认为错误是因为您想要从服务 启动 ui 而不是在主线程上 运行 并且服务没有runOnUiThread 的实现周围的工作是在任何 activity 上使用 LocalBroadcastManager 和 broadcastreceiver 并从示例 activity 下面的示例
打开对话框
为您服务
Intent intent = new Intent("showDialog");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
在任意 activity 片段
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);
根据 AsynkTask 文档(阅读 线程规则 部分)
- 必须在 UI 线程上加载 AsyncTask class。这是从 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 class 实例 class 并从那里调用 AsyncTask。
Runnable runnable = new Runnable() {
@Override
public void run() {
backGround=new BackGround(context);
backGround.execute(String.valueOf(send_json));
}
};
如果您希望停止对 运行nable 的重复调用,只需调用 mHandler.removeCallbacks(runnable);
更新:-
我们无法显示带有服务上下文的正常警报对话框,因此以下是两种可能的解决方案。
解决方案 1:-
只有当它是系统警报对话框时,我们才能显示来自服务的对话框。因此,将 TYPE_SYSTEM_ALERT window 布局参数设置为 Dialog 如下:
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
但是,它需要 SYSTEM_ALERT_WINDOW 许可。所以,不要忘记在清单文件中添加此权限。
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
解决方案 2 :-
尝试启动 Activity 并将 Activity 的主题设置为 Theme.Dialog。
ApiDemo
项目中有演示。查看 this link 如何将主题应用到 activity 或应用程序。
仅供参考
A Service
运行s 在其托管进程的主线程中——该服务不创建自己的线程,也不在单独的线程中 运行过程(除非您在清单中另外指定)。 IntentService
运行 工作线程(非 UI 线程)。
您可以从任何线程调用 mHandler.postDelayed
,因为一旦我们创建处理程序实例,它就会与它在其中创建的线程相关联。在我们的例子中,它是 UIThread,因为我们在 onStartCommand
方法中创建它。
如有任何疑问,请随时提出。
希望这对您有所帮助并消除您的疑虑。
我有一个服务做一些工作,最后我执行我的异步任务(它的名字是背景)。 在 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.run(locationService.java:588)
at java.util.Timer$TimerImpl.run(Timer.java:284)
这是我的异步任务class:
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 Service{Context 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.access0(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)
您正在 doInBackground()
中创建在工作线程中运行的对话框,请根据您的要求尝试在 onPreExecute
或 onPostExecute
中创建它。
我认为错误是因为您想要从服务 启动 ui 而不是在主线程上 运行 并且服务没有runOnUiThread 的实现周围的工作是在任何 activity 上使用 LocalBroadcastManager 和 broadcastreceiver 并从示例 activity 下面的示例
打开对话框为您服务
Intent intent = new Intent("showDialog");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
在任意 activity 片段
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);
根据 AsynkTask 文档(阅读 线程规则 部分)
- 必须在 UI 线程上加载 AsyncTask class。这是从 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 class 实例 class 并从那里调用 AsyncTask。
Runnable runnable = new Runnable() {
@Override
public void run() {
backGround=new BackGround(context);
backGround.execute(String.valueOf(send_json));
}
};
如果您希望停止对 运行nable 的重复调用,只需调用 mHandler.removeCallbacks(runnable);
更新:- 我们无法显示带有服务上下文的正常警报对话框,因此以下是两种可能的解决方案。
解决方案 1:-
只有当它是系统警报对话框时,我们才能显示来自服务的对话框。因此,将 TYPE_SYSTEM_ALERT window 布局参数设置为 Dialog 如下:
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
但是,它需要 SYSTEM_ALERT_WINDOW 许可。所以,不要忘记在清单文件中添加此权限。
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
解决方案 2 :-
尝试启动 Activity 并将 Activity 的主题设置为 Theme.Dialog。
ApiDemo
项目中有演示。查看 this link 如何将主题应用到 activity 或应用程序。
仅供参考
A
Service
运行s 在其托管进程的主线程中——该服务不创建自己的线程,也不在单独的线程中 运行过程(除非您在清单中另外指定)。IntentService
运行 工作线程(非 UI 线程)。您可以从任何线程调用
mHandler.postDelayed
,因为一旦我们创建处理程序实例,它就会与它在其中创建的线程相关联。在我们的例子中,它是 UIThread,因为我们在onStartCommand
方法中创建它。
如有任何疑问,请随时提出。
希望这对您有所帮助并消除您的疑虑。