Android开发优化之的强引用、软引用、弱引用的使用

StuartHoffm 8年前
   <p style="text-align:center"><img src="https://simg.open-open.com/show/512c645eff352510a315df4da1b09c58.png"></p>    <ul>     <li> <h3>引言</h3> </li>    </ul>    <p>早在JDK1.2,Java就把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。</p>    <p>但是平时我们的代码中似乎很少出现这些, 而之前还看到过一份代码中, 一个Activity中有一个静态变量持有对自己的弱引用,来达到类似的singleTask的效果.</p>    <p>So, 是时候系统的学习一下软引用、弱引用这些,并对我们的代码进行优化了.</p>    <ul>     <li> <h3>强引用</h3> </li>    </ul>    <pre>  <code class="language-java">String str = new String("xiamin");</code></pre>    <p>强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。</p>    <p>强引用特点:</p>    <ul>     <li>强引用可以直接访问目标对象。</li>     <li>强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象。</li>     <li>强引用可能导致内存泄露。</li>    </ul>    <ul>     <li> <h3>软引用 (SoftReference)</h3> 简单用法</li>    </ul>    <pre>  <code class="language-java">MyObject aRef = new  MyObject();      SoftReference aSoftRef=new SoftReference(aRef);        MyObject anotherRef=(MyObject)aSoftRef.get();</code></pre>    <p>java.lang.ref包中提供了几个类:SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,以便跟踪Java虚拟机回收所引用的对象的活动。</p>    <p>如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。</p>    <pre>  <code class="language-java">ReferenceQueue queue = new  ReferenceQueue();      SoftReference  ref=new  SoftReference(aMyObject, queue);</code></pre>    <p>那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。</p>    <p>在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。常用的方式为:</p>    <pre>  <code class="language-java">SoftReference ref = null;      while ((ref = (EmployeeRef) q.poll()) != null) {          // 清除ref      }</code></pre>    <p>当然,我们作为Android开发中使用,正常是用来处理图片这种占用内存大的类的.</p>    <p>所以,我们应该这样使用.</p>    <pre>  <code class="language-java">View view = findViewById(R.id.button);      Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);      Drawable drawable = new BitmapDrawable(bitmap);      SoftReference<Drawable> drawableSoftReference =                                                   new SoftReference<Drawable>(drawable);      if(drawableSoftReference != null) {          view.setBackground(drawableSoftReference.get());      }</code></pre>    <p>这样的好处是</p>    <p>通过软引用的get()方法,取得drawable对象实例的强引用,发现对象被未回收。在GC在内存充足的情况下,不会回收软引用对象。此时view的背景显示.</p>    <p>实际情况中,我们会获取很多图片.然后可能给很多个view展示, 这种情况下很容易内存吃紧导致oom,</p>    <p>内存吃紧,系统开始会GC。这次GC后,drawables.get()不再返回Drawable对象,而是返回null,这时屏幕上背景图不显示,说明在系统内存紧张的情况下,软引用被回收。</p>    <p>使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。</p>    <p>需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。</p>    <ul>     <li> <h3>弱引用 (WeakReference)</h3> <p>用法</p> <pre>  <code class="language-java">WeakReference<User> sr = new WeakReference<User>(new User());</code></pre> <p>如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。</p> <p>弱引用与软引用的根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。</p> </li>    </ul>    <p>使用场景,handler的使用防止内存泄露</p>    <pre>  <code class="language-java">import android.os.Bundle;  import android.os.Handler;  import android.os.Message;  import android.support.v7.app.AppCompatActivity;  import java.lang.ref.WeakReference;    public class MainActivity extends AppCompatActivity {        private Handler handler  ;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);            handler = new MyHandler( this ) ;            new Thread(new Runnable() {              @Override              public void run() {                 handler.sendEmptyMessage( 0 ) ;              }          }).start() ;        }        private static class MyHandler extends Handler {          WeakReference<MainActivity> weakReference ;            public MyHandler(MainActivity activity ){              weakReference  = new WeakReference<MainActivity>( activity) ;          }            @Override          public void handleMessage(Message msg) {              super.handleMessage(msg);              if ( weakReference.get() != null ){                  // update android ui              }          }      }    }</code></pre>    <p>在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。</p>    <p>软引用,弱引用都非常适合来保存那些可有可无的缓存数据。如果这样做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间。</p>    <ul>     <li> <h3>到底什么时候使用软引用,什么时候使用弱引用呢?</h3> <p>个人认为,如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。</p> <p>还有就是可以根据对象是否经常使用来判断。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。</p> <p>另外,和弱引用功能类似的是WeakHashMap。WeakHashMap对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。</p> </li>    </ul>    <h3> </h3>    <p> </p>    <p>来自:http://www.jianshu.com/p/8488079a939b</p>    <p> </p>