Android监听应用进程被系统清理后向服务器发送退出请求

  • Post author:
  • Post category:其他


最近改bug的时候看到我们的软件在本地如果不点击退出登陆,直接用任务管理器杀死的话,在服务器端依旧保持着在线状态,直到Token过期(我们的Token四个小时才过期。。。我也不懂为什么要这么设计)。但是既然存在这样的问题,那么就想解决方案就好了。

####方案计划:

监听进程是否还在存活,如果已经被清理了,就向服务器发送退出请求。也就是:

if(APP.isDead()){


LoginOut();

}

计划是有了,接下来就应该思考如何去编码实现。

####编码实现:

第一步:监听进程这种任务肯定是要放在Service中利用定时器+ActivityManager来解决。代码片段:

	private boolean isBackgroundRunning() {
		activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        if (activityManager == null) {
            return false;
        }
        List<ActivityManager.RunningTaskInfo> processList = activityManager.getRunningTasks(100);
        for (ActivityManager.RunningTaskInfo info : processList) {
	        //在这里只能用baseActivity,今天看到有些人写的是用topActivity,不是不行
		    //问题就在于 如果程序中调用了相机之类的系统应用,那么此时topActivity的包名与当前进程包名不匹配
		    //就会出现错误监听的情况
            if (info.baseActivity.getPackageName().startsWith(PackageName)) {
                return true;
            }
        }
        return false;
      }

第二步:在启动Service的时候启动定时器,也就是在OnCreate中定义好定时器,并执行。代码片段:

	@Override
    public void onCreate() {
	    //每5秒向handler发送一次消息 而不是直接在TimerTask中进行判断
        timerMail.schedule(new TimerTask() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        }, 15000, 5000);
        super.onCreate();
    }
    
	private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
	        try {
		        //没有过多的操作,简单粗暴 通俗易懂
                if(!isBackgroundRunning()){
                    logout();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    });   

第三步:当请求发送成功之后,调用onDestroy方法,销毁当前服务。代码片段:

	@Override
    public void onDestroy() {
	    //将定时器取消
        if (timerMail!= null){
            timerMail.cancel();
        }
        //Handler中提供了去清除Message和Runnable的方法,不再占用多余的队列空间
        mHandler.removeCallbacksAndMessages(null);  
        super.onDestroy();
    }

第四步:大功告成,开始测试。贴上全部代码:

public class CheckService extends Service {
    private static final String PackageName = "com.****.****";
    private final Timer timerMail = new Timer();
    private LogUtils mLogUtils = LogUtils.getInstance();
    private ActivityManager activityManager=null;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        mLogUtils.e("   开启检查服务" , 2);
        timerMail.schedule(new TimerTask() {
            @Override
            public void run() {
                mLogUtils.e("   发送消息", 2);
                Message message = new Message();
                message.what = 1;
                handlerMail.sendMessage(message);
            }
        }, 15000, 5000);
        super.onCreate();
    }

    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            try {
                if(!isBackgroundRunning()){
	                mLogUtils.e("   退出操作", 2);
                    logout();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    });   
    
    private boolean isBackgroundRunning() {
        activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        if (activityManager == null) {
            return false;
        }
        List<ActivityManager.RunningTaskInfo> processList = activityManager.getRunningTasks(100);
        for (ActivityManager.RunningTaskInfo info : processList) {
            if (info.baseActivity.getPackageName().startsWith(PackageName)) {
                return true;
            }
        }
        return false;
    }
    
    /* (non-Javadoc)
     * @see android.app.Service#onDestroy()
     */
    @Override
    public void onDestroy() {
        if (timerMail!= null){
            timerMail.cancel();
        }
        mLogUtils.e("   销毁服务", 2);
        super.onDestroy();
    }
	
	public void logout() {
		***
	}
}

####思考问题:

这里打断点是没有用的,所以我们只能利用打印log文件的方法去检查程序运行的状态。

这里写图片描述

这就是仅仅运行了一次之后打印出来的Log,我明明只是在登陆成功后调用了一次StartService方法,为什么OnCreate会被执行两次?

于是,继续测试,这次在打印log的时候在打印出当前进程ID,再来看一下:

	//获取进程ID
	int mProcessId = android.os.Process.myPid()

这里写图片描述

####结论猜想:

当应用进程被系统直接清理之后,当前服务也会被一并清理,但是Handler的Message Queue中存在着还没有被来的及处理的Message,于是重新打开一个进程服务,继续处理消息时,发现应用程序已经不在运行了,此时发送退出请求,清理数据,销毁当前Service并将之前的Service在当前进程中一并销毁。

####失败声明:

这个方法在华为的某些手机上是失败的。。。当进程被清理的时候,不存在第二次启动。其他机型上没有做过测试,当然我们的软件是在我们给客户提供的机子上运行的,所以没有做过多的机型适配。(我自己用的锤子科技的M1,测试成功)。



版权声明:本文为m0_37852369原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。