重点内容刚写博客,不是很清楚怎么写,大家见谅,有不清楚的直接qq我,1092417123,欢迎大家一起交流技术
https://github.com/aixiaozi/Fast_Fragment
代码在提交的防止应用被强杀里面,对应用被强杀的情况做了一些封装,防止应用崩溃
导致应用被强杀的原因有很多,比如:按了 home 键、系统内存不足等等
此外,Android 6.0新特性中,在电源管理中新加了两个特性,导致挂在后台的应用更容易被回收,这就要求我们要注意状态的保存和恢复
应用待机 (App standby)
检测:不充电,且没有直接或间接启动该应用
退出:应用激活,或设备充电时休眠(Doze)
检测:不充电,且设备静止且灭屏一段时间
特性:让系统进入一个休眠状态,周期性的进入一个窗口恢复正常工作,然后进入更长时间的休眠状态
eg:我们在一个类中设置一个 static 变量, HomeActivity 中初始化这个 static 变量,在其它页面(ProfileActivity)调用,这时候应用被强杀,再点击应用直接回到 ProfileActivity,此时由于 这个 static 尚未 初始化,所以会导致空指针,应用崩溃。
Android 机制中 app 被强杀后,app中所有的变量都被清空了,但是唯有Activity的栈信息依然保存着
被强杀后不会重新走WelcomeActivity,而是直接返回ProfileActivity,重新走ProfileActivity 的 onCreate()流程,只是这些Activity重新初始化,包括Application,所以此时引用的变量可能为空,就导致了空指针
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
mProfileLabel = (TextView) findViewById(R.id.mProfileLabel);
mProfileLabel.setText(CustomApplication.mTestNullPointers.toString());
}
解决方法一:单例(如果为空,就new)
当对象为空,就 new 出来,再去拿对象里的东西,可以解决但不建议这么做
为什么会出现空指针? -> 应用被强杀 -> 直接让它走应用的流程
if(isForceKilled){
startActivity(new Intent(this, WelcomeActivity.class));
}
在ProfileActivity 中判断是否被强杀,如果是强杀,重新走应用的流程(实际代码中在 BaseActivity 中去判断),BaseActivity 相关代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
switch (AppStatusTracker.getInstance().getAppStatus()) {
case ConstantValues.STATUS_FORCE_KILLED:
protectApp();
break;
case ConstantValues.STATUS_KICK_OUT:
kickOut();
break;
case ConstantValues.STATUS_LOGOUT:
case ConstantValues.STATUS_OFFLINE:
case ConstantValues.STATUS_ONLINE:
setUpContentView();
setUpView();
setUpData(savedInstanceState);
break;
}
}
protected void protectApp() {
Intent intent = new Intent(this, HomeActivity.class);
intent.putExtra(ConstantValues.KEY_HOME_ACTION, ConstantValues.ACTION_RESTART_APP);
startActivity(intent);
}
由此,跳转到 HomeworActivity,再在 HomeActivity 里 再进行判断 是否被强杀,HomeAcitivity 相关代码如下(onNewIntent 是因为HomeAcitivty 启动模式为 singleTask):
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
int action = intent.getIntExtra(ConstantValues.KEY_HOME_ACTION, ConstantValues.ACTION_BACK_TO_HOME);
switch (action) {
case ConstantValues.ACTION_KICK_OUT:
break;
case ConstantValues.ACTION_LOGOUT:
break;
case ConstantValues.ACTION_RESTART_APP:
protectApp();
break;
case ConstantValues.ACTION_BACK_TO_HOME:
break;
}
}
@Override
protected void protectApp() {
startActivity(new Intent(this, WelcomeActivity.class));
finish();
}
注意:此时重走应用流程,比如说 跳转到 WelcomeActivity 界面,再点返回会回到ProfileActivity,而此时我们需要的是直接退出应用程序
对应用的 栈信息分析:首先是 WelcomeActivity,然后自己 finish,然后是 LoginActivity,成功之后 到 HomeActivity,然后点返回键应该回到 桌面。
HomeActivity 启动模式设置为 singleTask,如果现在在ProfileActivity,现在要回到 HomeActivity ,singleTask 就会 直接用栈底的 HomeActivity ,ProfileActivity 和 XxxActivity 会被直接 remove 掉,从栈里清除,
应用被强杀,会在BaseAcitivity 中判断,如果是,跳转至HomeActivity,在HomeActivity(singleTask ) 的 onNewIntent 中进行标注,再跳转至 welcomeActivty,整个栈信息就清空了,开始重走整个应用流程
在ProfileActivity 中,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (CustomApplication.mAppStatus == -1) {//被强杀
Intent intent = new Intent(this, HomeActivity.class);
intent.putExtra("action", "force_kill");
startActivity(intent);
} else {
setUpData();
}
}
在HomeActivity中,多一个onNewIntent
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String action = intent.getStringExtra("action");
if ("force_kill".equals(action)) {
protectApp();
}
}
@Override
protected void protectApp() {
startActivity(new Intent(this, WelcomeActivity.class));
finish();
}
错误示例:
注意:保留的只有栈信息,实例是没有的,此时还要走 onCreat(),如果在 onCreate() 中调用 static 变量 还是有可能导致空指针,办法:可以在这里也判断一下是否被强杀就可以了
可以这样想:
初始值是 -1 ,代表着 isForceKilled(),它就是被强杀掉的,状态可以在 WelcomeActivity 中 onCreate() 中对这个状态进行修改
在CustomApplication中定义一个常量
public static int mAppStatus = -1;
当应用走到 WelcomeActivity 进行修改
public class WelcomeActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
CustomApplication.mAppStatus = 0;
super.onCreate(savedInstanceState);
}
...
}
在BaseActivity 中进行判断:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (CustomApplication.mAppStatus == -1) {
protectApp();
} else {
setUpData();
}
}
...
}
public class ConstantValues {
public static final int STATUS_FORCE_KILLED = -1;//被强杀
public static final int STATUS_LOGOUT = 0;//注销
public static final int STATUS_OFFLINE = 1;//未登录
public static final int STATUS_ONLINE = 2;//登陆
public static final int STATUS_KICK_OUT = 3;//token失效,或账号在另一台设备登陆,被挤下线
public static final String KEY_HOME_ACTION = "key_home_action";
public static final int ACTION_BACK_TO_HOME = 0;
public static final int ACTION_RESTART_APP = 1;
public static final int ACTION_LOGOUT = 2;
public static final int ACTION_KICK_OUT = 3;
}
public class AppStatusTracker implements Application.ActivityLifecycleCallbacks{
private boolean isForground;
private int activeCount;//计数器,所有的Activity都执行了stop方法,那么count=0,app在后台执行
private static final long MAX_INTERVAL = 5 * 60* 1000;
private long timestamp;//在后台呆的时间够长,再切换到前台的时间差,超过最大值显示图形解锁
private boolean isScreenOff;//图形解锁的特殊需求,屏幕锁住之后再亮起需要图形解锁
private static AppStatusTracker tracker;
private Application application;
private DaemonReceiver receiver;
private int mAppStatus = ConstantValues.STATUS_FORCE_KILLED;
private AppStatusTracker(Application application) {
this.application = application;
application.registerActivityLifecycleCallbacks(this);//必须注册才能接受到该方法的回调
}
public static void init(Application application) {
tracker = new AppStatusTracker(application);
}
public static AppStatusTracker getInstance() {
return tracker;
}
public void setAppStatus(int status) {
this.mAppStatus = status;
if (status == ConstantValues.STATUS_ONLINE){
if (receiver == null){
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
receiver = new DaemonReceiver();
application.registerReceiver(receiver,filter);
}else if (receiver != null){
application.unregisterReceiver(receiver);
receiver = null;
}
}
}
public int getAppStatus() {
return this.mAppStatus;
}
public boolean isForground() {
return isForground;
}
private void onScreenOff(boolean isScreenOff) {
this.isScreenOff = isScreenOff;
}
public boolean checkIfShowGesture(){
if (mAppStatus == ConstantValues.STATUS_OFFLINE){
if (isScreenOff){
return false;
}
if (timestamp != 0l && System.currentTimeMillis() - timestamp > MAX_INTERVAL) {
return true;
}
}
return false;
}
@Override
public void onActivityStopped(Activity activity) {
L.e(activity.toString()+" onActivityStopped");
activeCount --;
if (activeCount == 0){
isForground = false;//在后台执行
timestamp = System.currentTimeMillis();
}
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
L.e(activity.toString()+" onActivityCreated");
}
@Override
public void onActivityStarted(Activity activity) {
L.e(activity.toString()+" onActivityStarted");
activeCount ++;
}
//界面要显示时调用此方法,onCreate -- onResume 之间不该写大量代码,
// 防止初始化时间过长会导致黑屏,
//一旦此方法回调,就可以确定当前应用是在前台执行
@Override
public void onActivityResumed(Activity activity) {
L.e(activity.toString()+" onActivityResumed");
//Activity 跳转过程中,onPause -> onResume 需要时间,这过程中算前台?后台?
isForground = true;
timestamp = 0l;
isScreenOff = false;
}
@Override
public void onActivityPaused(Activity activity) {
L.e(activity.toString()+" onActivityPaused");
}
@Override
public void onActivityDestroyed(Activity activity) {
L.e(activity.toString()+" onActivityDestroyed");
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
private class DaemonReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
L.d("onReceive:" + action);
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
AppStatusTracker.getInstance().onScreenOff(true);
}
}
}
}
public abstract class BaseActivity extends AppCompatActivity implements Toolbar.OnMenuItemClickListener {
protected Toolbar toolbar;
protected TextView toolbar_title;
public static final int MODE_BACK = 0;
public static final int MODE_DRAWER = 1;
public static final int MODE_NONE = 2;
public static final int MODE_HOME = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
switch (AppStatusTracker.getInstance().getAppStatus()) {
case ConstantValues.STATUS_FORCE_KILLED:
protectApp();
break;
case ConstantValues.STATUS_KICK_OUT:
kickOut();
break;
case ConstantValues.STATUS_LOGOUT:
case ConstantValues.STATUS_OFFLINE:
case ConstantValues.STATUS_ONLINE:
setUpContentView();
setUpView();
setUpData();
break;
}
}
protected abstract void setUpContentView();
protected abstract void setUpView();
protected abstract void setUpData();
protected void protectApp() {
Intent intent = new Intent(this, HomeActivity.class);
intent.putExtra(ConstantValues.KEY_HOME_ACTION, ConstantValues.ACTION_RESTART_APP);
startActivity(intent);
}
protected void kickOut() {
// TODO show dialog to confirm
Intent intent = new Intent(this, HomeActivity.class);
intent.putExtra(ConstantValues.KEY_HOME_ACTION, ConstantValues.ACTION_KICK_OUT);
startActivity(intent);
}
@Override
public void setContentView(int layoutResID) {
setContentView(layoutResID, -1, -1, MODE_BACK);
}
public void setContentView(int layoutResID, int titleResId) {
setContentView(layoutResID, titleResId, -1, MODE_BACK);
}
public void setContentView(int layoutResID, int titleResId, int mode) {
setContentView(layoutResID, titleResId, -1, mode);
}
public void setContentView(int layoutResID, int titleResId, int menuId, int mode) {
super.setContentView(layoutResID);
setUpToolbar(titleResId, menuId, mode);
}
protected void setUpToolbar(int titleResId, int menuId, int mode) {
if (mode != MODE_NONE) {
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle("");
toolbar_title = (TextView) findViewById(R.id.toolbar_title);
if (mode == MODE_BACK) {
toolbar.setNavigationIcon(R.drawable.ic_toolbar_back);
}
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onNavigationBtnClicked();
}
});
setUpTitle(titleResId);
setUpMenu(menuId);
}
}
protected void setUpMenu(int menuId) {
if (toolbar != null){
toolbar.getMenu().clear();
if (menuId > 0) {
toolbar.inflateMenu(menuId);
toolbar.setOnMenuItemClickListener(this);
}
}
}
protected void setUpTitle(int titleResId) {
if (titleResId > 0 && toolbar_title != null) {
toolbar_title.setText(titleResId);
}
}
protected void onNavigationBtnClicked() {
finish();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
return false;
}
@Override
protected void onStart() {
if (AppStatusTracker.getInstance().checkIfShowGesture()){
L.d("need to show gesture");
}
super.onStart();
}
}