android 应用被系统回收,莫往Applicaotion存缓存/app被系统回收之后再打开发生了什么...
先上结论:
1、尽量不要往Application里面存放缓存数据(因为app用application缓存切使用全局数据,当这个app为后台程序时又被系统gc回收后我们再次打开app,就会导致空指针)
2、app被gc回收后再次点击该app发生了什么(比如我们 app甲先后依次打开了a,b,c这三个activity,这时a,b,c都是在同一个线程的,这时我们按下home键,利用MMDS强制关闭我们程序甲的process,其实也就是模拟被gc回收了。这个时候我们在打开程序甲,会发现打开的是程序甲这的c这个activity,这个时候c是在一个新的线程里面的,而不是在原来的线程里面的)
有图有真相。下图演示因为当安卓app被系统gc回收后我们再次打开app产生空指针导致数据丢失的现象
application缓存丢失演示.gif
上图程序没挂是因为我们缓存在application的值我们这是用作显示,null就是空了,当我们使用了这个值马上程序就会应为空指针而挂掉。
下面我们使用以下给gc回收的缓存在application的值
application空指针演示.gif
一、不要往Application存缓存数据
当然这不是绝对化,只是这么建议。
在你的App中的很多地方都需要使用到数据信息,它可能是一个session token,一次费时计算的结果等等,通常为了避免Activity之间传递数据的开销,会将这些数据通过持久化来存储。
有人建议将这些数据放在Application对象中方便所有的Activity访问,这个解决方案简单、优雅并且是……完全错误的。
你如果你将数据缓存到Application对象中,那么有可能你的程序最终会由于一个NullPointerException异常而崩溃掉。
1、数据存在Application被gc后数据都是的demo演示
下面通过demo演示因为数据存放在application导致数据丢失的情况:
1、新建一个类MyApplication继承自Application,弄一个那么的属性设置对应的get和set方法,并且让我们的程序的清单文件的application采用我们的MyApplication2、弄三个acitivity,MainActivity有一个按钮,点击则打开FirstActivity,而Activity的onCreate方法干的事就是给MyApplicatipn的name属性set一个值,比如“Try Text”,接着就直接打开我们的SecondActivity3、SecondAcrivity直接有一个TextView控件,这个控件用于显示从MyApplication取出来的那么的值。
嗯就是这样子,正常情况下,我们进入MainActivity,点击 “打开FirstActivity” 只会就进入FirstActivity之后瞬间进入SecondActivity。SecondActivity正常显示FirstActivity给存储在MyApplication里面的值。
这个是没什么问题的。这个时候,我们按下home键,然后利用DDMS把当前app杀掉(模拟给gc回收),然后在打开app,会发现SecondActivity显示的name值变为null,数据丢失了。一旦使用这个值就报空指针。
接下来贴一些代码:
MyApplication
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
}
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MainActivity
public class MainActivity extends Activity {
private TextView mTvOpenFirst;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvOpenFirst= (TextView) findViewById(R.id.mTvOpenFirst);
mTvOpenFirst.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFirst = new Intent(MainActivity.this,FirstActivity.class);
startActivity(openFirst);
}
});
}
}
FirstActivity
public class FirstActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
MyApplication app = (MyApplication) getApplication();
app.setName("Try Text");
startActivity(new Intent(this, SecondActivity.class));
}
}
SecondActivity
public class SecondActivity extends Activity{
private TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mTv= (TextView) findViewById(R.id.mTv);
MyApplication app = (MyApplication) getApplication();
mTv.setText("HELLO " + app.getName());
Log.d("fixmothod", "是否为空:" + (app.getName() == null));
// app.getName()为null,调用下面这句代码就会引发空指针
//boolean equals= app.getName().equals("123");
}
}
去掉备注的boolean equals= app.getName().equals("123");再来一遍引发的报错图
空指针.png
2、原因分析
因为当我们重新打开这个app的时候,系统会为我们重新实例化一个新的Application对象,Application对象并不是始终在内存中的,它有可能会由于系统内存不足而被杀掉。但Android在你恢复这个应用时并不是重新开始启动这个应用,它会创建一个新的Application对象并且启动上次用户离开时的activity以造成这个app从来没有被kill掉得假象。
说到底,重新点击被杀掉的app时,会实例化一个新的Application对象,然后启动被杀死的app在被杀死之前的所停留的最后一个activity。
二、app被系统回收之后再打开发生了什么
android独特的内存机制,导致系统内存不足时会销毁后台的应用,这里我们研究一下应用被销毁后重新加载时的情形
1、问题:打开被gc杀死的程序后,存在Application等得全局静态变量全部被重置
一个安卓应用A先后打开3个Activity: a --> b --> c (此时的a、b、c是在同一个线程的)这个时候如果来了一个电话, 接电话的过程中, 手机内存不够, 那么应用A将会被系统回收
当打完电话,再次进入应用A的时候会发生下面的事情:
1, 系统会重新加载c,而且是在新线程中
2, 现在点返回关闭c,系统就会重新加载b,而且是在新的线程中(跟c不是一个线程)
3, 现在点返回关闭b,系统就会重新加载a,而且是在新的线程中(跟c,b的线程都不相同)(就是先打开c,finish了c时加载b,finish了b时加载a)
注意: 这个时候应用中的全局静态变量将全部重置(有默认值的为默认值,没有默认值的为null)
这就使得出现了错误的数据
2 解决办法:
方法1, 不使用全局变量
不使用全局变量, 放在Application中也不行, 因为重新加载的a,b,c的页面不在一个线程中,Application不唯一了 (有点不成文)
方法2,强制关闭非存储初值的界面,先开启能够先赋值的界面(一般都是登录界面)
结束b和c, 只重新加载a ,在a中重新初始化数据 (a往往是登录界面)
办法2的实现:在b,c等所有 非a的activity 中的onCreate里面加上下面的代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isNull(Config.UserID)) {
if (null != savedInstanceState) {
// activity由系统打开 (是由于手机内存不够,activity在后台被系统回收,再打开时出现的现象)
// 因为系统加载的所有的Activity不在同一个线程,所以要结束除了loginActivity之外的其他线程
android.os.Process.killProcess(android.os.Process.myPid());
} else {
this.finish();
}
return;
}
// ...其他代码
}
代码的原理:因为重新加载的a,b,c都在不同的线程中,所以我们先后舍弃c,b的线程,那么a就会重新加载
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (null != savedInstanceState) {
toast("程序被系统回收,需要重新登录!");
//activity由系统加载的时候savedInstanceState不为空
}
//...其他代码
}
关于以上的方法二这个是在实际app中使用。就不做具体分析了。
方法3,onCreate中添加异常处理 ,遇到异常的时候,就重启程序。
Intent i = getBaseContext().getPackageManager()
.getLaunchIntentForPackage(getBaseContext().getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
方法4、利用savedInstanceState和onRestoreInstanceState
这个方法便捷针对本demo看起来挺便捷的,但是使用的时候要结合具体,因为写起来太麻烦。
我们的Activity可以复写savedInstanceState和onRestoreInstanceState这两个方法,简单说,这个两个方法正常情况下不会被调用,onSaveInstanceState方法只有在程序以外退出或Activity异常终止的时才会被调用,用于保存数据
savedInstanceState只有在程序以外退出或Activity异常终止的时才会被调用,用于保存数据。
onRestoreInstanceState则用来恢复数据,这些数据就是savedInstanceState那里拿来的。
我们只要修改下SecondActivity就可以看到效果:
public class SecondActivity extends Activity{
private TextView mTv;
MyApplication app;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mTv= (TextView) findViewById(R.id.mTv);
app= (MyApplication) getApplication();
mTv.setText("HELLO " + app.getName());
}
/**
* onSaveInstanceState和onRestoreInstanceState一般都是配合着使用的
* 方法只有在程序以外退出或Activity异常终止的时才会被调用,用于保存数据
* @param outState
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 键值对 key value
outState.putString("save_name",app.getName());
}
/**
* onRestoreInstanceState
* 用于恢复在以外发生之前保存的数据
* @param savedInstanceState
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 获取 key 所对应的 value
String save_name = savedInstanceState.getString("save_name");
mTv.setText("HELLO" + save_name);
Log.d("fixmothod", "app.getName()是否为空1: " + app.getName());
app.setName(save_name);
Log.d("fixmothod", "app.getName()是否为空2: " + app.getName());
}
}
利用onSaveInstanceState.gif
三、附上源码
module说明图
module说明.png
本篇完。
参考链接:
android 应用被系统回收,莫往Applicaotion存缓存/app被系统回收之后再打开发生了什么...相关推荐
- android x86 v2ex,GitHub - xm0625/VBrowser-Android: 全网视频嗅探缓存APP
VBrowser-Android 全网视频嗅探缓存APP 简介 一款用于全网视频嗅探.缓存及播放的APP,方便追剧党.出差党随时随地观看缓存好的视频.超强的视频嗅探能力,多线程急速下载. 主要功能 1 ...
- 区块链app源码_区块链app商城系统开发适用于哪些企业
区块链商城系统开发,区块链技术让人们对于软件应用开发有了全新的想象,现在也有越来越多的企业开始布局区块链业务,区块链应用开渐渐成为市场应用的宠儿,随着区块链技术的不断发展,未来也会有越来越多的区块链应 ...
- 钱包系统推荐维金_USDT钱包APP开发系统
USDT点对点支付系统开发,USDT钱包承兑商支付系统开发找[刘先生]USDT承兑商支付接单系统开发,USDT支付开发API接口系统全行业对接,USDT跨境支付交易系统开发,USDT跨境支付系统,US ...
- Android工程师进阶第一课 夯实Java基础 JVM内存模型和GC回收机制
开篇词:跳出舒适区,走在Android行业的前端 你好,我是姜新星,一个深耕 Andorid 领域的老工程师. 记得 2010 年毕业典礼上,某位老师说"你们是最幸福的一届毕业生,正好赶上中 ...
- android 11.0 12.0第三方输入法app设置系统默认输入法
1.概述 在11.0 12.0的产品开发中,有功能需要要求设置默认输入法,替换掉系统的输入法,所以这就需要了解设置 输入法的相关功能需求,然后根据输入法包名来设置默认输入法 2.第三方输入法app设置 ...
- android4.4系统 分屏,基于Android系统的宽屏后视镜分屏方法及系统与流程
本发明涉及后视镜应用系统领域,尤其涉及一种基于Android系统的宽屏后视镜分屏方法及系统. 背景技术: 后视镜是驾驶员坐在驾驶室座位上直接获取汽车后方.侧方和下方等外部信息的工具.为了驾驶员操作方便 ...
- Android短信拦截机制适配的坑(下)--4.4以上系统,主要是6.0
前一篇文章,Android短信拦截机制适配的坑(上)--4.4以下系统 介绍了广播接收的顺序,但是我明确说明在4.4以下系统,那么4.4及以上系统会遇到说明问题呢? 首先我们要来了解4.4系统短信的机 ...
- Android App 设置系统时间,语言和时区、系统重启
文章目录 说明 1. 修改系统时间 1. 设置系统时间(仅设置xx:xx) 2. 设置系统日期(仅设置到xxxx年xx月xx日) 3.设置系统是否自动获取时间 4.判断系统是否自动获取时间 2. 修改 ...
- 题目:身份证录入系统 一、语言和环境 a)实现语言Java, 使用Android开发环境实现《身份证录入系统》APP。具体要求如下: 打开应用后,显示效果如图-1所示:
题目:身份证录入系统 (有需要指导的私信我) 一.语言和环境 a)实现语言 Java b)环境要求 JDK6以上.Eclipse.ADT 二.功能要求 使用Android开发环境实现<身份证录入 ...
最新文章
- 区块链将带来怎样的应用?
- Python 之 Numpy (四)索引
- mysql-explain
- (转)8种常见机器学习算法比较
- luogu3810 【模板】三维偏序(陌上花开)
- 微信小程序开放直播能力,你怎么看?
- 锁是网络数据库中的一个非常重要的概念
- Flask学习之旅--数据库
- 力软敏捷开发框架真正源码_敏捷真正使谁受益?
- SpringBoot项目打成War包??
- 安卓应用安全指南 5.4.2 通过 HTTPS 的通信 规则书
- kong组件_使用Kongzue全家桶组件创建项目全攻略(一、引入和沉浸式的那些事儿)...
- matlab函数句柄介绍
- 网店系统SHOP++、V5shop、shopex全程分析比较
- FPGA数字时钟计数器
- 使用wps-excell画折线图
- 计算机二级office知识大纲,2017计算机二级考试MS office 考试内容大纲
- Java语言 Timer 定时器的四种使用方式
- UVa10019:Funny Encryption Method
- focus search returned a view that wasn't able to take focus!【已更新】