1. 程式人生 > >Android 關於後臺殺死App之後改變服務器狀態的一些嘗試

Android 關於後臺殺死App之後改變服務器狀態的一些嘗試

round 創建 color 賬號 llb 當前 troy lifecycle nvi

前言:

  如題,我的需求是:我需要在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之後改變服務器狀態的一些嘗試