上周四面试的时候,面试官提到了进程保活的问题,所以现在就来踩一下坑,准备多尝试一下,学点新东西。正好最近也在写这个聊天项目的另一个衍生的项目,推送平台,也是对保活有很大的要求的,打好铺垫。
测试机型:小米5(android7.0)、魅族metal(android5.0.1)
先分析待解决问题:锁屏之后,被自动杀掉进程,导致无法收到后续消息。
目标:锁屏之后不被杀掉。
思路1:
算是一个歪点子,新添加一个Activity,然后监听Screen状态,监听到锁屏事件后,弹出该Activity到屏幕顶端,从而将该进程的等级从后台进程升级到前台进程达到最高权限避免被杀。
方案:写一个baseActivity,从而全局监听锁屏事件,在任何一个Activity都要监听到锁屏事件。然后不能被用户发现弹出了一个新的Activity,所以要动点歪脑筋,将该Activity设成透明然后在将Window的大小设置成1个像素点,就能完全避免被用户发现了。
当然是通过广播来实现监听了,所以先完成我们的BroadCastReceiver
public class ScreenObserver {
private Context mContext;
private ScreenBroadcastReceiver mScreenReceiver;
private ScreenStateListener mScreenStateListener;
public ScreenObserver(Context context) {
mContext = context;
mScreenReceiver = new ScreenBroadcastReceiver();
}
public void startObserver(ScreenStateListener listener) {
mScreenStateListener = listener;
registerListener();
}
public void shutdownObserver() {
unregisterListener();
}
private void registerListener() {
if (mContext != null) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
mContext.registerReceiver(mScreenReceiver, filter);
}
}
private void unregisterListener() {
if (mContext != null)
mContext.unregisterReceiver(mScreenReceiver);
}
private class ScreenBroadcastReceiver extends BroadcastReceiver {
private String action = null;
@Override
public void onReceive(Context context, Intent intent) {
action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
mScreenStateListener.onScreenOn();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
mScreenStateListener.onScreenOff();
} else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁
mScreenStateListener.onUserPresent();
}
}
}
public interface ScreenStateListener {// 返回给调用者屏幕状态信息
void onScreenOn();
void onScreenOff();
void onUserPresent();
}
}
其核心就是
private class ScreenBroadcastReceiver extends BroadcastReceiver {
private String action = null;
@Override
public void onReceive(Context context, Intent intent) {
action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
mScreenStateListener.onScreenOn();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
mScreenStateListener.onScreenOff();
} else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁
mScreenStateListener.onUserPresent();
}
}
}
这几行就是核心了,监听了锁屏、亮屏、解锁三个事件。
下面看一下BaseActivity
public class BaseActivity extends Activity {
private static final String TAG = "BaseActivity";
ScreenObserver observer;
private Context context=this;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
LogUtil.log(TAG,"oncreate");
super.onCreate(savedInstanceState);
localBroadcastManager=LocalBroadcastManager.getInstance(this);
observer=new ScreenObserver(this);
observer.startObserver(new ScreenObserver.ScreenStateListener() {
@Override
public void onScreenOn() {
LogUtil.log(TAG,"ON!!!!!!!!!!!!!!!!!!!!!!!");
}
@Override
public void onScreenOff() {
LogUtil.log(TAG,"OFF!!!!!!!!!!!!!!!!!!!!!!!");
KeepAliveActivity.Start(context);
}
@Override
public void onUserPresent() {
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
observer.shutdownObserver();
}
}
也是很简单,就是实例化我们刚刚定义的ScreeObserver,然后回调接口,完成操作:打开那个猥琐的透明的保活Activity。
下面就看一下这个猥琐的Activity吧
public class KeepAliveActivity extends Activity{
private static final String TAG = "KeepAliveActivity";
ScreenObserver observer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LogUtil.log(TAG,"onCreate");
Window window=getWindow();
window.setGravity(Gravity.LEFT|Gravity.TOP);
WindowManager.LayoutParams params=window.getAttributes();
params.x=0;
params.y=0;
params.height=1;
params.width=1;
window.setAttributes(params);
observer=new ScreenObserver(this);
observer.startObserver(new ScreenObserver.ScreenStateListener() {
@Override
public void onScreenOn() {
LogUtil.log(TAG,"ON!!!!!!!!!!!!!!!!!!!!!!!");
}
@Override
public void onScreenOff() {
LogUtil.log(TAG,"OFF!!!!!!!!!!!!!!!!!!!!!!!");
}
@Override
public void onUserPresent() {
LogUtil.log(TAG,"USERPRESENT!!!!!!!!!!!!!!!!!!!!!!!"+this.getClass().getName());
stopme();
}
});
}
public static void Start(Context context){
Intent intent=new Intent(context,KeepAliveActivity.class);
context.startActivity(intent);
}
public void stopme(){
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
observer.shutdownObserver();
}
}
核心:
Window window=getWindow();
window.setGravity(Gravity.LEFT|Gravity.TOP);
WindowManager.LayoutParams params=window.getAttributes();
params.x=0;
params.y=0;
params.height=1;
params.width=1;
window.setAttributes(params);
设置成了1个像素点,然后左上角的一个window。那透明属性在哪呢?
答:在xml
<style name="KeepALiveActivity">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
就这样。就完成了思路1的全部代码。
成果:
- 成功监听,能够自动打开和finish 保活Activity。
- 运用此方法前锁屏后两台手机都会在1min中内杀掉进程,改造后,我睡了个午觉回来后还没有被杀掉,就算是成功了吧。
感想:
- 有时候思路要发散一些,比如像这样,不能思维固化,我们是保活,而不是拉活,不能上来就想着死后拉活。