我们在使用应用程序的时候,会遇到这样的需求:当程序被切换到后台运行(按Home键)一段比较长的时间,重现召唤到前台的时候,可能需要重新登录或者重新显示广告页面,表示我们的应用已经长时间处于非活动状态,相当于重新进行了一次重启应用的过程。 本篇Blog就来介绍下如果自定义一个BaseActivity,程序的所有Activity均继承自这个自定义的BaseActivity,当应用切换到后台时,启动一个本地服务,记录应用切换到后台的时间。
自定义BaseActivity类 我们直接来看自定义的BaseActivity类的源代码。
注意:
源代码中省略了我的程序的packet以及所有的import头文件,这里没有特殊的import类,用Android Studio自动生成的就OK。
目前的BaseActivity类中仅完成了启动后台时间的额外功能,其余的一些通用的功能也可以在这里完成。
每个函数中都打上了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 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" ); } @Override public void onResume () { super .onResume(); LogUtils.i(getActivityName(),"onResume" ); if (!((CommonVariables) getApplication()).getIsActive()) { ((CommonVariables) getApplication()).setIsActive(true ); if (time_interval_ > 10L ) { LogUtils.i("显示Splash" ,"因为超时了" ); } unregisterReceiver(broadcastReceiver); stopService(new Intent(BaseActivity.this ,ClockService.class )) ; } } @Override public void onPause () { super .onPause(); LogUtils.i(getActivityName(),"onPause" ); } @Override public void onStop () { super .onStop(); LogUtils.i(getActivityName(),"onStop" ); 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" ); } public abstract String getActivityName () ; 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_); } }
关键技术点:
getActivityName 方法是一个接口函数,需要在子类中实现,这个函数其实很简单,主要是返回当前Activity的名称,用于log的显示而已。
onStop 方法中启动本地服务,此处需要用到两个关键函数,CommonUtils.isAppOnForeground()
用来判断程序是否处于前台运行;setIsActive
用来设置和获取全局变量。
onResume 方法中判断程序从后台切回到前台后,启动后台服务,也是用到了上面两个方法。
我们来详细看一下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 public static boolean isAppOnForeground (Context context) { boolean result = false ; 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) { 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;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 ); } private Runnable sendUpdatesToUI = new Runnable() { public void run () { DisplayLoggingInfo(); handler.postDelayed(this , 1000 ); } }; 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) { return null ; } }
注意 :import进来的包是 android.os.Handle
AndroidManifest文件 最后,记得在AndroidManifest
文件中把我们定义的服务类添加进取: 在<application>
标签中添加子标签<service>
:
1 2 <!-- 定义服务--> <service android:name=".Utils.ClockService"/>
基本到这里,该介绍的关键点都讲到了。之后的程序中,所有的Activity都继承自我们自定义的BaseActivity
类,这样程序无论在任何一个页面点击Home键切换到后台,超过我们设定的阈值后,重新切回前台就可以执行我们设定的重新登录的动作,无论是重新登录还是显示广告页面等等。 最后在说一点Tips: 为了大家阅读方便,很多应该当地定义的常量值我都用数字直接写了,例如超时的阈值10秒;每1000豪秒发送的广播间隔等。规范的写法应该是在一个单独的常量类中讲这些值设定好。