安卓在Service中弹出Dialog

来源:互联网 发布:淘宝客服幽默常用语 编辑:程序博客网 时间:2024/05/21 19:05

这是前几天在面试的时候被问到的一个问题,首先当时本人并没有仔细研究过这个问题,答得一塌糊涂,所以下来就仔细研究了一下:

先给出结论:

API 18 及以下可以使用将Dialog设置为 WindowManager.LayoutParams.TYPE_SYSTEM_ALERT ,来解决这个问题;API 19 以上就不行了,我的实现方式是将一个Activity的THEME设置为Theme.AppCompat.Dialog.Alert,曲线救国。效果如下:

效果图1

一、API 18 的实现方式:

  1. 添加权限 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  2. 直接贴出 Activity 和 Service 部分代码

MainActivity

public class MainActivity extends Activity {    @BindView(R.id.start_service)    Button startService;    @BindView(R.id.stop_service)    Button stopService;    @BindView(R.id.bind_service)    Button bindService;    @BindView(R.id.unbind_service)    Button unbindService;    private Intent intent;    private MyService.MyBinder myBinder;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            myBinder = (MyService.MyBinder) iBinder;            myBinder.startDownload();        }        @Override        public void onServiceDisconnected(ComponentName componentName) {        }    };    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        intent = new Intent(this, MyService.class);    }    @OnClick({R.id.start_service, R.id.stop_service, R.id.bind_service, R.id.unbind_service})    public void onViewClicked(View view) {        switch (view.getId()) {            case R.id.start_service:                startService(intent);                break;            case R.id.stop_service:                stopService(intent);                break;            case R.id.bind_service:                bindService(intent, connection, BIND_AUTO_CREATE);                break;            case R.id.unbind_service:                unbindService(connection);                break;        }    }}
上面的startDownload() 方法执行的时 showDialog(),名字咩改,见谅

MyService

public class MyService extends Service {    private static final String TAG = "MyService";    private MyBinder binder = new MyBinder();    private Handler handler = new Handler();    @Override    public void onCreate() {        super.onCreate();        Log.d(TAG, "onCreate executed");        Log.e(TAG, "MyService thread is " + Thread.currentThread().getId());    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.d(TAG, "onStartCommand executed");        showDialog();        return super.onStartCommand(intent, flags, startId);    }    @Override    public boolean onUnbind(Intent intent) {        Log.d(TAG, "onUnBind executed");        return super.onUnbind(intent);    }    @Override    public IBinder onBind(Intent intent) {        Log.d(TAG, "onBind executed");        return binder;    }    @Override    public void onDestroy() {        Log.d(TAG, "onDestroy executed");        super.onDestroy();    }    public class MyBinder extends Binder {        public void startDownload() {            showDialog();            Log.d(TAG, "requestNet");        }    }    private void showDialog() {// 注释1此处需要注意(详见下文)//AlertDialog.Builder builder = new AlertDialog.Builder(this);        AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.AppTheme));        builder.setTitle("提示"); //设置标题        builder.setMessage("是否确认退出?"); //设置内容        builder.setIcon(R.mipmap.ic_launcher);//设置图标,图片id即可        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { //设置确定按钮            @Override            public void onClick(DialogInterface dialog, int which) {                dialog.dismiss(); //关闭dialog                Toast.makeText(MyService.this, "确认" + which, Toast.LENGTH_SHORT).show();            }        });        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { //设置取消按钮            @Override            public void onClick(DialogInterface dialog, int which) {                dialog.dismiss();                Toast.makeText(MyService.this, "取消" + which, Toast.LENGTH_SHORT).show();            }        });        builder.setNeutralButton("忽略", new DialogInterface.OnClickListener() {//设置忽略按钮            @Override            public void onClick(DialogInterface dialog, int which) {                dialog.dismiss();                Toast.makeText(MyService.this, "忽略" + which, Toast.LENGTH_SHORT).show();            }        });        //参数都设置完成了,创建并显示出来        final AlertDialog dialog = builder.create();// 注释2 把该dialog类型设置为系统的弹窗        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);        dialog.show();// 注释3 并不需要使用多线程 + handler//        new Thread(new Runnable() {//            @Override//            public void run() {//                handler.post(new Runnable() {//                    @Override//                    public void run() {//                        Toast.makeText(MyService.this, "dialog.show()", Toast.LENGTH_SHORT).show();//                        dialog.show();//                    }//                });//            }//        }).start();    }}
注释1:此处如果直接写成注释中的代码,报出如下错误,提示需要给Activity设置一个Theme,Google了一下,网上有很多这样的错误,但是跟笔者的情况都不一样,
笔者按照提示给MainActivity加了Theme,发现并没有卵用,后来给Dialog添加了Theme才有效。笔者此处并没有想通为什么必须添加这个Theme,希望有大牛解答。
08-10 05:51:34.187 29397-29397/chang.xiaobing.servicedialogtest E/AndroidRuntime: FATAL EXCEPTION: mainjava.lang.RuntimeException: Unable to start service chang.xiaobing.servicedialogtest.service.MyService@536d1ff0 with Intent {cmp=chang.xiaobing.servicedialogtest/.service.MyService }: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2507)at android.app.ActivityThread.access$1900(ActivityThread.java:130)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1292)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loop(Looper.java:137)at android.app.ActivityThread.main(ActivityThread.java:4745)at java.lang.reflect.Method.invokeNative(Native Method)at java.lang.reflect.Method.invoke(Method.java:511)at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)at dalvik.system.NativeStart.main(Native Method)Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.at android.support.v7.app.AppCompatDelegateImplV9.createSubDecor(AppCompatDelegateImplV9.java:356)at android.support.v7.app.AppCompatDelegateImplV9.ensureSubDecor(AppCompatDelegateImplV9.java:325)at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:286)at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:83)at android.support.v7.app.AlertController.installContent(AlertController.java:225)at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:257)at android.app.Dialog.dispatchOnCreate(Dialog.java:351)at android.app.Dialog.show(Dialog.java:256)at chang.xiaobing.servicedialogtest.service.MyService.showDialog(MyService.java:108)at chang.xiaobing.servicedialogtest.service.MyService.onStartCommand(MyService.java:41)at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2490)at android.app.ActivityThread.access$1900(ActivityThread.java:130) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1292) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method)                                                                                 
20170810135202错误信息
注释2:此处除了可以设置为
TYPE_SYSTEM_ALERT,还可以设置为 TYPE_TOAST,只不过设置为 TYPE_TOAST 之后不会响应点击事件,而且关不掉。


注释3:笔者在网上查资料时发现很多文章中写到在Service中 show Dialog 和 show Toast 时需要使用多线程+handler,经测试并不需要(见上面注释3的代码部分)
布局文件会在下方给出

下面是曲线救国方案:

思路:直接弹出一个Dialog样式的Activity,不多说直接上代码

1.在values/styles.xml中添加一个style(根据需求自定义)

<style name="dialogstyle">        <!--设置dialog的背景-->        <item name="android:windowBackground">@android:color/transparent</item>        <!--设置Dialog的windowFrame框为无-->        <item name="android:windowFrame">@null</item>        <!--设置无标题-->        <item name="android:windowNoTitle">true</item>        <!--是否浮现在activity之上-->        <item name="android:windowIsFloating">true</item>        <!--是否半透明-->        <item name="android:windowIsTranslucent">true</item>        <!--设置窗口内容不覆盖-->        <item name="android:windowContentOverlay">@null</item>        <!--设置动画,在这里使用让它继承系统的Animation.Dialog-->        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>        <!--背景是否模糊显示-->        <item name="android:backgroundDimEnabled">true</item>    </style>
2. 在清单文件中添加一个DialogActivity,并且为其设置样式

<activity            android:name=".DialogActivity"            android:theme="@style/dialogstyle"></activity>
3. 将showDialog() 方法给成下面的:
private void showDialog() {        Intent intent = new Intent(this, DialogActivity.class);// 此处必须设置flag        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        startActivity(intent);    }
4. 然后可以自定义布局,处理点击事件,效果如下(写的并不是很美观,可根据需求自定义)
效果图2


源码在这里

文章就到这里,希望能对你有所帮助,笔者才疏学浅,不足之处请各位大牛不吝赐教!

原创粉丝点击