Android 关于后台杀死App之后改变服务器状态的一些尝试

Posted 夜的那种黑丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 关于后台杀死App之后改变服务器状态的一些尝试相关的知识,希望对你有一定的参考价值。

前言:

  如题,我的需求是:我需要在App在后台运行(未退出),调出最近运行记录,杀死App服务时,程序能够向服务器发送一条指令,以此达到我想要的目的。

  android方面刚刚才开始玩,我一开始想的是可不可以在Activity中监听到,比如onDestroy()方法,但是打Log看了之后是没有的。度娘是万能的,百度一波后,我在逼乎上找到了另一个思路,那就是创建一个Server,很多人的博客中也都指出了,App在后台被杀死时,Service的onTaskRemoved()方法是可以监听到的。

Service的onTaskRemoved()监听App在后台被杀死:

  首先,一个Service类是必要的,其中onTaskRemoved()中的Http请求就是我需要跟服务器的交互

 1 package com.example.demo02;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.content.res.Configuration;
 6 import android.os.IBinder;
 7 import android.support.annotation.Nullable;
 8 import android.util.Log;
 9 
10 import com.example.http.UserHttpClientUtil;
11 
12 public class SimpleService extends Service {
13     private static final String TAG = "SimpleService";
14 
15     /**
16      * 绑定服务时才会调用
17      * 必须要实现的方法
18      * @param intent
19      * @return
20      */
21     @Nullable
22     @Override
23     public IBinder onBind(Intent intent) {
24         Log.d(TAG, "onBind: ");
25         return null;
26     }
27 
28     /**
29      * 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。
30      * 如果服务已在运行,则不会调用此方法。该方法只被调用一次
31      */
32     @Override
33     public void onCreate() {
34         super.onCreate();
35     }
36 
37     /**
38      * 每次通过startService()方法启动Service时都会被回调。
39      * @param intent
40      * @param flags
41      * @param startId
42      * @return
43      */
44     @Override
45     public int onStartCommand(Intent intent, int flags, int startId) {
46         return START_STICKY;
47     }
48 
49     /**
50      * 服务销毁时的回调
51      */
52     @Override
53     public void onDestroy() {
54         super.onDestroy();
55     }
56 
57     @Override
58     public void onStart(Intent intent, int startId) {
59         super.onStart(intent, startId);
60     }
61 
62     @Override
63     public void onConfigurationChanged(Configuration newConfig) {
64         super.onConfigurationChanged(newConfig);
65     }
66 
67     @Override
68     public void onLowMemory() {
69         super.onLowMemory();
70     }
71 
72     @Override
73     public void onTrimMemory(int level) {
74         super.onTrimMemory(level);
75     }
76 
77     @Override
78     public boolean onUnbind(Intent intent) {
79         return super.onUnbind(intent);
80     }
81 
82     @Override
83     public void onRebind(Intent intent) {
84         super.onRebind(intent);
85         Log.d(TAG, "onRebind: ");
86     }
87 
88     @Override
89     public void onTaskRemoved(Intent rootIntent) {
90         super.onTaskRemoved(rootIntent);
91         new Thread(new Runnable() {
92             @Override
93             public void run() {
94                 UserHttpClientUtil.exitCurrentAccount(LoginActivity.userInfoMapContextCache.get("userNo"));
95             }
96         }).start();
97     }
98 
99 }

  然后,在Activity中启动它

1 intent = new Intent(this, SimpleService.class);
2 getApplicationContext().startService(intent);

  AndroidManifest.xml中

1 <service
2    android:name=".SimpleService"
3    android:enabled="true"
4    android:exported="true">
5    <intent-filter>
6       <action android:name="com.example.demo02.AndroidApplication.intentService" />
7    </intent-filter>
8 </service>

  但是,我在测试的发现这种监听好像并不稳定,有时是可以监听到的,有时又监听不到,这肯定是不行的。(老式的Android是长按Home间调出最近运行记录,但是新式的Android并不是这样了,我不知道是不是这方面的原因)后来我又尝试重写Application,在Application中启动Service

 1 package com.example.demo02;
 2 
 3 import android.app.Application;
 4 import android.content.res.Configuration;
 5 import android.util.Log;
 6 
 7 import com.example.common.DefaultExceptionHandler;
 8 import com.example.common.MyLifecycleHandler;
 9 import com.example.http.UserHttpClientUtil;
10 
11 import static com.example.demo02.LoginActivity.userInfoMapContextCache;
12 
13 public class AndroidApplication extends Application {
14     private static AndroidApplication instance;
15     private static final String TAG = "AndroidApplication";
16     @Override
17     public void onCreate() {
18         super.onCreate();
19         instance = this;
20         Intent intentService = new Intent(this, SimpleService.class);
21         getApplicationContext().startService(intentService);
22     }
23 
24     public static AndroidApplication getInstance(){
25         return instance;
26     }
27 }

  但是结果仍然是一样的

  执念:我始终认为这一种方法是可行的,可能是我哪一方面写的有问题,如果有大神看出,望指正,不胜感激。

  这种方法暂时是走不通了,但是问题总是要解决的。经过一番思考,想出了一个上不得台面的方法:我其实需要的是在App在后台被杀死的情况下(非程序崩溃),改变一下用户的状态,那么我可不可以在程序中监听App处于前台还是后台,当处于前台时,每进入一个页面,我都更新一下状态为在线(这是为了无论从哪个页面进入后台,App再次进入前台时,状态都能够更新,这个可以在ActivityLifecycleCallbacks的onActivityResumed()方法中实现),当App位于后台运行时,我就更新状态为离线(我使用了Application中的onTrimMemory()方法来实现)。

一条小路:

  首先,需要判断一个App处于前台还是后台

 1 package com.example.common;
 2 
 3 import android.app.Activity;
 4 import android.app.Application;
 5 import android.content.Context;
 6 import android.content.Intent;
 7 import android.os.Bundle;
 8 import android.os.Handler;
 9 import android.os.Message;
10 import android.widget.Toast;
11 
12 import com.example.demo02.LoginActivity;
13 import com.example.http.UserHttpClientUtil;
14 
15 import java.util.HashMap;
16 import java.util.Map;
17 
18 import static com.example.demo02.LoginActivity.userInfoMapContextCache;
19 
20 /**
21  * 判断一个App处于前台还是后台
22  */
23 public class MyLifecycleHandler implements Application.ActivityLifecycleCallbacks{
24 
25     private static int resumed;
26     private static int paused;
27     private static int started;
28     private static int stopped;
29 
30     @Override
31     public void onActivityCreated(Activity activity, Bundle bundle) {
32 
33     }
34 
35     @Override
36     public void onActivityStarted(Activity activity) {
37         ++started;
38     }
39 
40     private Map<String, String> UpdateCurrentAccountMap = new HashMap<>();
41     private Context context;
42     @Override
43     public void onActivityResumed(Activity activity) {
44         ++resumed;
45         context = activity.getApplicationContext();
46         new Thread(new Runnable() {
47             @Override
48             public void run() {
49                 if (userInfoMapContextCache.get("userNo") != null && userInfoMapContextCache.get("userNo") != "") {
50                     UpdateCurrentAccountMap = UserHttpClientUtil.UpdateCurrentAccount(userInfoMapContextCache.get("userNo"));
51                     loginHandler.sendEmptyMessage(0);
52                 }
53             }
54         }).start();
55     }
56 
57     private Handler loginHandler = new Handler(){
58         @Override
59         public void handleMessage(Message msg) {
60             if (!UpdateCurrentAccountMap.get("lastLoginTime").equals(userInfoMapContextCache.get("lastLoginTime"))) {
61                 userInfoMapContextCache.clear();
62                 Intent intent = new Intent(context, LoginActivity.class);
63                 intent.putExtra("isAccountReset", "true");
64                 context.startActivity(intent);
65                 Toast.makeText(context, "当前账号已经在其他地方登陆,请重新登陆!", Toast.LENGTH_SHORT).show();
66             }
67         }
68     };
69 
70     @Override
71     public void onActivityPaused(Activity activity) {
72         ++paused;
73     }
74 
75     @Override
76     public void onActivityStopped(Activity activity) {
77         ++stopped;
78     }
79 
80     @Override
81     public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
82 
83     }
84 
85     @Override
86     public void onActivityDestroyed(Activity activity) {
87 
88     }
89 
90     public static boolean isApplicationVisible() {
91         return started > stopped;
92     }
93 
94     public static boolean isApplicationInForeground() {
95         // 当所有 Activity 的状态中处于 resumed 的大于 paused 状态的,即可认为有Activity处于前台状态中
96         return resumed > paused;
97     }
98 }

  然后,重写Application

 1 package com.example.demo02;
 2 
 3 import android.app.Application;
 4 import android.content.res.Configuration;
 5 import android.util.Log;
 6 
 7 import com.example.common.DefaultExceptionHandler;
 8 import com.example.common.MyLifecycleHandler;
 9 import com.example.http.UserHttpClientUtil;
10 
11 import static com.example.demo02.LoginActivity.userInfoMapContextCache;
12 
13 public class AndroidApplication extends Application {
14     private static AndroidApplication instance;
15     private static final String TAG = "AndroidApplication";
16     @Override
17     public void onCreate() {
18         super.onCreate();
19         instance = this;
20         registerActivityLifecycleCallbacks(new MyLifecycleHandler());
21     }
22 
23     public static AndroidApplication getInstance(){
24         return instance;
25     }
26 
27     @Override
28     public void onTrimMemory(int level) {
29         super.onTrimMemory(level);
30         if (!MyLifecycleHandler.isApplicationInForeground()) {
31             new Thread(new Runnable() {
32                 @Override
33                 public void run() {
34                     if (userInfoMapContextCache.get("userNo") != null && userInfoMapContextCache.get("userNo") != "") {
35                         UserHttpClientUtil.exitCurrentAccount(userInfoMapContextCache.get("userNo"));
36                     }
37                 }
38             }).start();
39         }
40     }
41 
42     @Override
43     public void onLowMemory() {
44         super.onLowMemory();
45         Log.d(TAG, "onLowMemory: ");
46     }
47 
48     @Override
49     public void onTerminate() {
50         super.onTerminate();
51         Log.d(TAG, "onTerminate: ");
52     }
53 
54     @Override
55     public void onConfigurationChanged(Configuration newConfig) {
56         super.onConfigurationChanged(newConfig);
57         Log.d(TAG, "onConfigurationChanged: ");
58     }
59 }

  不要忘记将你重写的Application在AndroidManifest中说明

<application
        android:name=".AndroidApplication"

  但是这种方法有一个坏处,就是在调用系统相机或者相册时,App也是出于后台的,这跟当初的设计理念不符

最后:

  我感觉Android应该是有监听到App在后台被杀死的方法的,我问了老板和一些搞Android的兄弟,都没有得到想要的答案,如果有大神知晓,望告知,不胜感激!!!

以上是关于Android 关于后台杀死App之后改变服务器状态的一些尝试的主要内容,如果未能解决你的问题,请参考以下文章

每当 App 在 android pie 中被杀死时,服务也会被杀死

Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制

Android:当应用程序被杀死时保持服务运行

android 模拟应用因内存不足被后台杀死命令

后台服务在android中被杀死

应用程序被杀死时Android后台服务正在重新启动