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

前言:

  如題,個人需求是:我須要在App在後臺運行(未退出),調出最近運行記錄,殺死App服務時,程序可以向服務器發送一條指令,以此達到我想要的目的。java

  Android方面剛剛纔開始玩,我一開始想的是可不能夠在Activity中監聽到,好比onDestroy()方法,可是打Log看了以後是沒有的。度娘是萬能的,百度一波後,我在逼乎上找到了另外一個思路,那就是建立一個Server,不少人的博客中也都指出了,App在後臺被殺死時,Service的onTaskRemoved()方法是能夠監聽到的。android

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中啓動它app

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

  AndroidManifest.xml中ide

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 }

  可是結果仍然是同樣的this

  執念:我始終認爲這一種方法是可行的,多是我哪一方面寫的有問題,若是有大神看出,望指正,不勝感激。spa

  這種方法暫時是走不通了,可是問題老是要解決的。通過一番思考,想出了一個上不得檯面的方法:我其實須要的是在App在後臺被殺死的狀況下(非程序崩潰),改變一下用戶的狀態,那麼我可不能夠在程序中監聽App處於前臺仍是後臺,當處於前臺時,每進入一個頁面,我都更新一下狀態爲在線(這是爲了不管從哪一個頁面進入後臺,App再次進入前臺時,狀態都可以更新,這個能夠在ActivityLifecycleCallbacks的onActivityResumed()方法中實現),當App位於後臺運行時,我就更新狀態爲離線(我使用了Application中的onTrimMemory()方法來實現)。設計

一條小路:

  首先,須要判斷一個App處於前臺仍是後臺code

 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的兄弟,都沒有獲得想要的答案,若是有大神知曉,望告知,不勝感激!!!

相關文章
相關標籤/搜索