• 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的兄弟,都没有得到想要的答案,如果有大神知晓,望告知,不胜感激!!!

  • 相关阅读:
    集合介绍,创建,添加,删除。
    字典简介、操作、内置函数、练习题
    git教程——简单总结
    前端性能优化总结
    小米2018春招实习笔试题总结
    浏览器缓存控制 以及 在url框中回车、F5 和 Ctrl + F5的区别
    携程2018春招实习前端开发笔试题分享
    不同方式实现两列布局
    移动端开发-viewport与媒体查询
    华为2018春招前端开发实习生笔试题分享
  • 原文地址:https://www.cnblogs.com/fx-blog/p/8312571.html
Copyright © 2020-2023  润新知