Contents
  1. 1. 自定义BaseActivity类
    1. 1.1. 关键技术点:
    2. 1.2. 判断程序是否出于前台运行中
  2. 2. 后台时钟服务ClockService类
  3. 3. AndroidManifest文件

我们在使用应用程序的时候,会遇到这样的需求:当程序被切换到后台运行(按Home键)一段比较长的时间,重现召唤到前台的时候,可能需要重新登录或者重新显示广告页面,表示我们的应用已经长时间处于非活动状态,相当于重新进行了一次重启应用的过程。
本篇Blog就来介绍下如果自定义一个BaseActivity,程序的所有Activity均继承自这个自定义的BaseActivity,当应用切换到后台时,启动一个本地服务,记录应用切换到后台的时间。

自定义BaseActivity类

我们直接来看自定义的BaseActivity类的源代码。

注意:
  1. 源代码中省略了我的程序的packet以及所有的import头文件,这里没有特殊的import类,用Android Studio自动生成的就OK。
  2. 目前的BaseActivity类中仅完成了启动后台时间的额外功能,其余的一些通用的功能也可以在这里完成。
  3. 每个函数中都打上了Log,我们可以清楚地看到每个Activity从创建到消亡的每个阶段。LogUtil是我自定义的类,其实功能和Android自带的Log类一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* File Description: 自定义的Activity基类
* 本类重载了Activity类,可以自定义自己的一些动作,例如在Activity的每个阶段输出log;隐藏标题栏等等
* Created by KAKA on 16/2/9 19:11
* 一个Activity从生成到显示依次经过 onCreate --> onStart --> onResume
* 当用户点击BACK结束程序时依次经过 onPause --> onStop --> onDestroy
* 当程序从前台切换到后台时依次经过 onPause --> onStop
* 当程序从后台切换到前台时依次经过 onStart --> onResume
*
* 模块化设计的功能逻辑:
* 1. 切换到后台启动{@link ClockService}服务:
* {@link BaseActivity#time_interval_} 私有变量 统计程序切换到后台的时间
* {@link BaseActivity#broadcastReceiver} 私有变量 接收从时钟服务的回调函数
* {@link BaseActivity#updateUI(Intent)} 函数 时钟服务在UI进程的响应动作
*
*/
public abstract class BaseActivity extends Activity {
/**
* 记录当前应用距离上一次切换到后台的时间
*/
private long time_interval_;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LogUtils.i(getActivityName(), "onCreate");
}

@Override
public void onStart() {
super.onStart();
LogUtils.i(getActivityName(),"onStart");
}

/**
* Description: 重写Activity切回前台时动作
* Created by KAKA on 16/2/11 16:55
* 1. 判断程序时候处于后台,因为可能涉及不同Activity之间的切换,此时程序并未处于后台
* 2. 如果超时,则启动Splash页面
* 3. 注销接收广播,并结束后台服务
*/
@Override
public void onResume() {
super.onResume();
LogUtils.i(getActivityName(),"onResume");

if (!((CommonVariables) getApplication()).getIsActive()) {
((CommonVariables) getApplication()).setIsActive(true);
// 判断时间是否超过10秒,如果超过则显示splash页面
if (time_interval_ > 10L) {
// TODO 设置超时的阈值
LogUtils.i("显示Splash","因为超时了");
// CommonUtils.LaunchActivity(BaseActivity.this,SplashActivity.class);
}
unregisterReceiver(broadcastReceiver);
stopService(new Intent(BaseActivity.this,ClockService.class));
}
}

@Override
public void onPause() {
super.onPause();
LogUtils.i(getActivityName(),"onPause");
}

/**
* Description: 重写Activity stop时的动作
* Created by KAKA on 16/2/11 16:57
* 1. 判断程序时候已经处于后台运行中,通过{@link CommonUtils#isAppOnForeground(Context)}方法
* 2. 如果程序处于后台运行则开启本地服务,并开启广播接收
* 3. 设置全局变量标识程序处于后台运行
*/
@Override
public void onStop() {
super.onStop();
LogUtils.i(getActivityName(),"onStop");
// 如果当前处于程序后台,则开启本地service
if (!CommonUtils.isAppOnForeground(getApplicationContext())) {
Intent intent = new Intent(BaseActivity.this, ClockService.class);
startService(intent);
registerReceiver(broadcastReceiver, new IntentFilter(ClockService.BROADCAST_ACTION));
// 设置全局变量记录程序进入后台
((CommonVariables) getApplication()).setIsActive(false);
}
}

@Override
public void onDestroy() {
super.onDestroy();
LogUtils.i(getActivityName(),"onDestroy");
}

/**
* Description: 获取当前的activity
* Created by KAKA on 16/2/9 19:15
*/
public abstract String getActivityName();

/**
* Description: 后台时钟服务控制UI的入口
* Created by KAKA on 16/2/9 19:47
*/
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateUI(intent);
}
};

private void updateUI(Intent intent) {
time_interval_ = intent.getIntExtra("time", 0);
LogUtils.d("Hello", "Time " + time_interval_);
}
}

关键技术点:

  1. getActivityName方法是一个接口函数,需要在子类中实现,这个函数其实很简单,主要是返回当前Activity的名称,用于log的显示而已。
  2. onStop方法中启动本地服务,此处需要用到两个关键函数,CommonUtils.isAppOnForeground()用来判断程序是否处于前台运行;setIsActive用来设置和获取全局变量。
  3. onResume方法中判断程序从后台切回到前台后,启动后台服务,也是用到了上面两个方法。
  4. 我们来详细看一下CommonUtils.isAppOnForeground()的实现方法。而全局变量的设置和获取我们稍后在下一篇Blog中予以完整而详细的介绍。

判断程序是否出于前台运行中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* Description: 判断程序是否在前台中
* Created by KAKA on 16/2/6 22:18
*/
public static boolean isAppOnForeground(Context context) {
boolean result = false;
// Returns a list of application processes that are running on the device
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context
.ACTIVITY_SERVICE);
String packageName = context.getPackageName();

List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
if (appProcesses == null) {
result = false;
} else {
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName) && appProcess.importance ==
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
result = true;
break;
}
}
}

if (result){
LogUtils.i("当前程序","前台运行中");
} else {
LogUtils.i("当前程序","切换到后台");
}

return result;
}

后台时钟服务ClockService类

本类主要用于启动和管理应用的服务,直接上源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.os.Handler;


/**
* File Description: 本地服务类,用于计算当前程序进入后台的时间
*
* Created by KAKA on 16/2/6 18:09
*
*/
public class ClockService extends Service {
private Intent intent;
public static final String BROADCAST_ACTION = "com.touziqu.sealily.newinvestmentgo.MainPage" +
".MainActivity";
private Handler handler = new Handler();
private long initial_time;
long timeInMilliseconds = 0L;

@Override
public void onCreate() {
super.onCreate();
initial_time = SystemClock.uptimeMillis();
intent = new Intent(BROADCAST_ACTION);
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 1000); // 1 second
}

private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
DisplayLoggingInfo();
handler.postDelayed(this, 1000); // 1 seconds
}
};

private void DisplayLoggingInfo() {
timeInMilliseconds = SystemClock.uptimeMillis() - initial_time;
int timer = (int) timeInMilliseconds / 1000;
intent.putExtra("time", timer);
sendBroadcast(intent);
}

@Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(sendUpdatesToUI);
}

@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}

}

注意:import进来的包是 android.os.Handle

AndroidManifest文件

最后,记得在AndroidManifest文件中把我们定义的服务类添加进取:
<application>标签中添加子标签<service>

1
2
<!-- 定义服务-->
<service android:name=".Utils.ClockService"/>

基本到这里,该介绍的关键点都讲到了。之后的程序中,所有的Activity都继承自我们自定义的BaseActivity类,这样程序无论在任何一个页面点击Home键切换到后台,超过我们设定的阈值后,重新切回前台就可以执行我们设定的重新登录的动作,无论是重新登录还是显示广告页面等等。
最后在说一点Tips:
为了大家阅读方便,很多应该当地定义的常量值我都用数字直接写了,例如超时的阈值10秒;每1000豪秒发送的广播间隔等。规范的写法应该是在一个单独的常量类中讲这些值设定好。

Contents
  1. 1. 自定义BaseActivity类
    1. 1.1. 关键技术点:
    2. 1.2. 判断程序是否出于前台运行中
  2. 2. 后台时钟服务ClockService类
  3. 3. AndroidManifest文件