效果图
适用应用场景
适用应用场景:应用没有完全退出的情况下在后台运行时,如果用户已经关闭了手机屏幕,如果我们的APP收到推送消息,则打开手机屏幕,弹框显示消息的方式来提醒用户。
实现思路
在收到自己服务器的推送消息时,发送一条广播,在接收到指定广播之后在广播的onReceive()中判断当前屏幕是否处于关闭状态,如果处于关闭状态,则显示这个弹窗消息,反之,则不需要显示
实现步骤
1.Service中发送广播
由于写这个demo,没有引入第三方推送,我就用Service来代替,大致就是说,开启一个后台服务,在退出APP后,任然在运行,在退出5s后,发送一条广播消息,通知广播接收器有新的消息需要提示用户。
**
* 模拟推送,在退出APP后的一段时间发送消息
*/
/**
* 模仿推送,发消息
*/
private void sendMessage() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent intent = new Intent();
intent.setAction("com.zx.lockscreenmsgdemo.LockScreenMsgReceiver");
sendBroadcast(intent); //发送广播
}
}).start();
}
}
2.在广播中启动锁屏弹窗
我们设置的是锁屏下才弹窗的,非锁屏下就不适合弹出这个窗口了。注册一个广播接收器,在接收到指定广播之后判断是否需要弹窗。
**
* 监听锁屏消息的广播接收器
*/
public class LockScreenMsgReceiver extends BroadcastReceiver {
private static final String TAG = "LockScreenMsgReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive:收到了锁屏消息 ");
String action = intent.getAction();
if (action.equals("com.zx.lockscreenmsgdemo.LockScreenMsgReceiver")) {
//管理锁屏的一个服务
KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
String text = km.inKeyguardRestrictedInputMode() ? "锁屏了" : "屏幕亮着的";
Log.i(TAG, "text: " + text);
if (km.inKeyguardRestrictedInputMode()) {
Log.i(TAG, "onReceive:锁屏了 ");
//判断是否锁屏
Intent alarmIntent = new Intent(context, MessageActivity.class);
//在广播中启动Activity的context可能不是Activity对象,所以需要添加NEW_TASK的标志,否则启动时可能会报错。
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(alarmIntent); //启动显示锁屏消息的activity
}
}
}
}
注意:
- KeyguardManager类,用来管理锁屏的,4.1之后该类的API新增了一个isKeyguardLocked()的方法判断是否锁屏,但在4.1之前,我们只能用inKeyguardRestrictedInputMode()方法,如果为true,即为锁屏状态。
- 在广播中启动Activity的context可能不是Activity对象,有可能是Service或者其他BroadcastReceiver,所以需要添加NEW_TASK的标志,否则启动时会报错。
3.Activity来显示弹窗消息
QQ的弹窗一开始我以为是悬浮View,用WindowManager去添加,后来尝试,查阅资料之后才发现是一个Activity,只不过它的背景用的就是你手机的这个背景而已。
public class MessageActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("tag", "onCreate:启动了消息内容的activity ");
//四个标志位顾名思义,分别是锁屏状态下显示,解锁,保持屏幕长亮,打开屏幕。这样当Activity启动的时候,它会解锁并亮屏显示。
Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED //锁屏状态下显示
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD //解锁
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON //保持屏幕长亮
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); //打开屏幕
// Drawable wallPaper = WallpaperManager.getInstance( this).getDrawable();
// win.setBackgroundDrawable(wallPaper);
setContentView(R.layout.activity_message);
}
}
之所以能够在锁屏状态下显示消息就是因为窗体对象的这个添加标志位的这个方法起了作用。四个标志位的作用,分别是锁屏状态下显示,解锁,保持屏幕长亮,打开屏幕。这样当Activity启动的时候,它会解锁并亮屏显示。保持屏幕长亮这个标志位是可选的。
设置了背景为壁纸的背景,所以显示的是桌面的背景。如果背景设为默认的白色,则导致弹窗后面是一片白色,看起来很丑。如果背景设置为透明,则弹窗后面会显示出解锁后的界面(即使有锁屏密码,也是会显示解锁后的界面的),一样很影响视觉效果。
当显示完消息,点击消息内容的时候,需要先先解锁系统自带锁屏服务,才能进入我们对应的消息界面。
//先解锁系统自带锁屏服务,放在锁屏界面里面
KeyguardManager keyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
keyguardManager.newKeyguardLock("").disableKeyguard(); //解锁
//点击进入消息对应的页面
mContext.startActivity(new Intent(mContext, DetailsActivity.class));
finish();
然后在AndroidManifest.xml文件当中,对该activity的声明需要加上以下属性:
<activity
android:name=".activity.MessageActivity"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:taskAffinity="" />
需要的权限
<!-- 解锁屏幕需要的权限 -->
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<!-- 申请电源锁需要的权限 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />