转载请注明出处谢谢:http://blog.csdn.net/printfcc/article/details/79131479
在学习Android中的Handle出现OOM经过搜索接触到Weak Reference这词,哇靠,英文赶紧百度一下:这玩意儿叫:弱引用。
经过一顿搜索学习,记录一下~(ps:可能会有错误望大神指正:))
首先我们来看一段使用Handler的代码
/**
* Created by vveng on 2018/1/6.
*/
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mTextView.setText("");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData(){
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
这种创建Handler的方式看上去没什么毛病但是会容易造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。
真的是 no picture say ge J8 按照自己的理解做了两张图:
第一张:
当一个android主线程被创建的时候,同时会有一个Looper对象被创建,而这个Looper对象会实现一个MessageQueue(消息队列),当我们创建一个handler对象时,而handler的作用就是放入和取出消息从这个消息队列中,每当我们通过handler将一个msg放入消息队列时,这个msg就会持有一个handler对象的引用。因此当Activity被结束后,这个msg在被取出来之前,这msg会继续存活,但是这个msg持有handler的引用,而handler在Activity中创建,会持有Activity的引用
看看Android官方文档怎么说:
弱引用对象,它们并不禁止其指示对象变得可终结,并被终结,然后被回收。弱引用最常用于实现规范化的映射。
假定垃圾回收器确定在某一时间点上某个对象是弱可到达对象。这时,它将自动清除针对此对象的所有弱引用,以及通过强引用链和软引用,可以从其到达该对象的针对任何其他弱可到达对象的所有弱引用。同时它将声明所有以前的弱可到达对象为可终结的。在同一时间或晚些时候,它将那些已经向引用队列注册的新清除的弱引用加入队列。
通俗易懂的讲:我们知道Java中有垃圾回收机制GC,WeakReference弱引用是这么一个东西,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
那么在Handler中我们要如何使用呢?看下面
/**
* Created by vveng on 2018/1/6.
*/
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference reference; //
public MyHandler(Context context) {
reference = new WeakReference<>(context);//这里传入activity的上下文
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
由于Handler持有的对象是使用弱引用,根据WeakReference弱引用的特点在GC回收时能回收弱引用,这样就避免了OOM,另外还有在消息队列中可能会有待处理的消息Message,所以我们可以在onDestroy()或者onStop()中调用mHandler.removeCallbacksAndMessages(null);来移除所有消息和Runnable
单例造成的内存泄漏
单例设计模式是日常开发中在常见不过的一种,当时你知道若使用不当也很容易造成OOM吗?
由于CustomizeManager 是一个单例模式,那么这个类的生命周期就伴随整个应用的生命周期,而它在被Activity创建的时候引用了Activity,所以当系统GC的时候试图去回收Activity时,发现它却在被另一个任然在内存里的CustomizeManager 所引用,所以GC回收它失败,从而导致了内存泄漏。就如下面的代码:
/**
* Created by vveng on 2018/1/6.
*/
public class CustomizeManager {
private static CustomizeManager instance;
private Context mContext;
public CustomizeManager (Context context) {
this.mContext = context;
}
public static CustomizeManager getInstance(Context context) {
if (instance == null) {
instance = new CustomizeManager (context);
}
return instance;
}
}
解决问题的方法有两种:
/**
* Created by vveng on 2018/1/6.
*/
public class PendingOrderManager {
private static CustomizeManager instance;
//一种是弱引用
private WeakReference wr;
// private Context context;
public CustomizeManager(Context context) {
wr = new WeakReference<>(context);
//第二种获取Application的Context
/**
这样不管传入什么Context最终将使用Application的Context,
而单例的生命周期和应用的一样长,这样就防止了内存泄漏
*/
// this.context = context.getApplicationContext();
}
public static CustomizeManager getInstance(Context context) {
if (instance == null) {
instance = new CustomizeManager(context);
}
return instance;
}
}
非静态内部类创建静态实例造成的内存泄漏
有的时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,可能会出现这种写法:
public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mManager = new TestResource();
}
//...
}
class TestResource {
//...
}
}
这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。正确的做法为:
将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用ApplicationContext
线程造成的内存泄漏
对于线程造成的内存泄漏,也是平时比较常见的,如下这两个示例可能每个人都这样写过:
//——————test1
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
//——————test2
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
上面的异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成,
那么将导致Activity的内存资源无法回收,造成内存泄漏。正确的做法还是使用静态内部类的方式,如下:
static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference weakReference;
public MyAsyncTask(Context context) {
weakReference = new WeakReference<>(context);
}
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity activity = (MainActivity) weakReference.get();
if (activity != null) {
//...
}
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
SystemClock.sleep(10000);
}
}
//——————
new Thread(new MyRunnable()).start();
new MyAsyncTask(this).execute();
这样就避免了Activity的内存资源泄漏,当然在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源。
**其中:**NO1表示Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列。而对于Dialog而言,只有在Activity中才能创建
在JDK1.2,Java就把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
String s = new String("帅哥");
特点:GC不会回收它,就算内存吃紧也打死不回收
HappyClass happy = new HappyClass ();
SoftReference aSoftRef=new SoftReference(happy);
HappyClass happy =(HappyClass)aSoftRef.get();
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref=new SoftReference(aMyObject, queue);
虚引用
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
谢谢浏览到最后~
参考连接:http://blog.csdn.net/u010687392/article/details/49909477