前两天公司给了一个新需求,就是要求移动端内部有个开关,定时访问后台,当后台返回数据为true时展示对话框,但是不让用户知道。本来跟大佬说加个推送更方便快捷简单,还减少服务器压力。。。但是老板不允许,本着老板就是上帝的态度,那就做呗。
既然不然用户感知那就用个Service吧,后台运行就行了。说搞就搞,先写一个服务
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
既然要定时访问数据那就再来个计时线程吧,然后十秒访问一次
Handler handler=new Handler();
Runnable runnable=new Runnable() {
@Override
public void run() {
handler.postDelayed(runnable,10000);
}
};
收到消息后需要弹出对话框,那肯定需要广播了,那就写一个广播吧,收到广播后弹出对话框
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle("我是Title");
dialog.setMessage("我是测试的消息内容");
dialog.setNegativeButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "点击了确定", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
});
dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "点击了取消", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
});
dialog.show();
}
}
然后在在服务里写一个请求方法,我这里做测试就不写请求了,就写个方法模拟一下,在计时线程中调用该方法,并传递i,用于测试判断是否获取到了值
private void getServer(int i) {
Intent intent=new Intent(this,MyReceiver.class);
intent.putExtra("test",i);
sendBroadcast(intent);
}
然后在广播了增加判断,当i==20的时候弹出对话框
点击运行,你会发现项目崩溃了,看日志会出现这个异常
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
报错异常是因为对话框的创建是依附与Activity界面存在的,我们在广播中传递给Dialog的Context并没有界面,而是一个广播的上下文,并不存在界面,所以报了这个错误,要怎么解决呢?
其实也很简单,我们只需要在AndroidManifest.xml文件里加入申请悬浮窗权限,然后在dialog.show之前加入这句代码
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
//alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
//google规定O以上需要改成这个 TYPE_APPLICATION_OVERLAY
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
这样就可以正常显示了。
如果你不想这么麻烦,也可以使用一个本地广播直接在Activity里创建一个内部类,然后在使用,不过使用有点不一样,发送消息要这样,LocalBroadcastManager,使用这个安全性更高,也便于管理
//发送广播
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
Intent intent = new Intent("com.example.testbradcasereceiver.myreceiver");
intent.putExtra("test", i);
localBroadcastManager.sendBroadcast(intent);
然后在activity里注册广播和添加广播接收器(代码一样我就不重复写了)
//注册广播
myReceiver = new MyReceiver();
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.testbradcasereceiver.myreceiver");
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(myReceiver, intentFilter);
效果是一样的,只是这个你在按Home键时这个是不可见的,而悬浮窗那种回到桌面也是可见的。两中方式各有优缺点,看需求吧
最后在说一句,为了避免溢出请注意注销,,,我这里都没写哦