安卓在Service中弹出Dialog

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

先给出结论:

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: main
java.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


源码在这里

文章就到这里,希望能对你有所帮助,笔者才疏学浅,不足之处请各位大牛不吝赐教!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值