Android OOM的那些事

Context部分

View的构造函数

View.java

/**       * Simple constructor to use when creating a view from code.       *       * @param context The Context the view is running in, through which it can       *        access the current theme, resources, etc.       */      public View(Context context) {          mContext = context;          mResources = context != null ? context.getResources() : null         mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;          // Used for debug only          //++sInstanceCount;          mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  

View 保存了对context的引用,mContext=context;

1.我们知道结构其实和DOM差不多,都会保存其父容器、子容器的引用,因而context的使用需要注意,不要使用静态的子View

2.来看View中的setBackgroundDrawable方法

View.java

 

/**       * Set the background to a given Drawable, or remove the background. If the       * background has padding, this View's padding is set to the background's       * padding. However, when a background is removed, this View's padding isn't       * touched. If setting the padding is desired, please use       * {@link #setPadding(int, int, int, int)}.       *       * @param d The Drawable to use as the background, or null to remove the       *        background       */      public void setBackgroundDrawable(Drawable d) {          boolean requestLayout = false            mBackgroundResource = 0            /*           * Regardless of whether we're setting a new background or not, we want           * to clear the previous drawable.           */          if (mBGDrawable != null) {              mBGDrawable.setCallback(null);              unscheduleDrawable(mBGDrawable);          }             if (d != null) {              Rect padding = sThreadLocal.get();              if (padding == null) {                  padding = new Rect();                  sThreadLocal.set(padding);              }              if (d.getPadding(padding)) {                  setPadding(padding.left, padding.top, padding.right, padding.bottom);              }                 // Compare the minimum sizes of the old Drawable and the new.  If there isn't an old or              // if it has a different minimum size, we should layout again              if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||                      mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {                  requestLayout = true             }                 d.setCallback(this);              if (d.isStateful()) {                  d.setState(getDrawableState());              }              d.setVisible(getVisibility() == VISIBLE, false);              mBGDrawable = d;                 if ((mPrivateFlags & SKIP_DRAW) != 0) {                  mPrivateFlags &= ~SKIP_DRAW;                  mPrivateFlags |= ONLY_DRAWS_BACKGROUND;                  requestLayout = true             }          } else {              /* Remove the background */              mBGDrawable = null                if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {                  /*                   * This view ONLY drew the background before and we're removing                   * the background, so now it won't draw anything                   * (hence we SKIP_DRAW)                   */                  mPrivateFlags &= ~ONLY_DRAWS_BACKGROUND;                  mPrivateFlags |= SKIP_DRAW;              }                 /*               * When the background is set, we try to apply its padding to this               * View. When the background is removed, we don't touch this View's               * padding. This is noted in the Javadocs. Hence, we don't need to               * requestLayout(), the invalidate() below is sufficient.               */                 // The old background's minimum size could have affected this              // View's layout, so let's requestLayout              requestLayout = true         }             computeOpaqueFlags();             if (requestLayout) {              requestLayout();          }             mBackgroundSizeChanged = true         invalidate();      } 

我们注意到d.setCallback(this);

Drawable保存了View的引用,处理了一些回调。因此,Drawable对象使用时需要注意了。不要作为静态对象使用。

 

Resource.getDrewable()方法是比较好的,Resource内部使用了一个Drawable.ConstantState的弱引用缓存,提高了效率。

来看代码:

Resource.java

/*package*/ Drawable loadDrawable(TypedValue value, int id)              throws NotFoundException {             if (TRACE_FOR_PRELOAD) {              // Log only framework resources              if ((id >>> 24) == 0x1) {                  final String name = getResourceName(id);                  if (name != nullAndroid.util.Log.d("PreloadDrawable", name);              }          }             final long key = (((long) value.assetCookie) << 32) | value.data;          Drawable dr = getCachedDrawable(key);             if (dr != null) {              return dr;          }             Drawable.ConstantState cs = sPreloadedDrawables.get(key);          if (cs != null) {              dr = cs.newDrawable(this);          } else {              if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&                      value.type <= TypedValue.TYPE_LAST_COLOR_INT) {                  dr = new ColorDrawable(value.data);              }                 if (dr == null) {                  if (value.string == null) {                      throw new NotFoundException(                              "Resource is not a Drawable (color or path): " + value);                  }                     String file = value.string.toString();                     if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "                          + value.assetCookie + ": " + file);                     if (file.endsWith(".xml")) {                      try {                          XmlResourceParser rp = loadXmlResourceParser(                                  file, id, value.assetCookie, "drawable");                          dr = Drawable.createFromXml(this, rp);                          rp.close();                      } catch (Exception e) {                          NotFoundException rnf = new NotFoundException(                              "File " + file + " from drawable resource ID #0x"                              + Integer.toHexString(id));                          rnf.initCause(e);                          throw rnf;                      }                     } else {                      try {                          InputStream is = mAssets.openNonAsset(                                  value.assetCookie, file, AssetManager.ACCESS_STREAMING);          //                System.out.println("Opened file " + file + ": " + is);                          dr = Drawable.createFromResourceStream(this, value, is,                                  file, null);                          is.close();          //                System.out.println("Created stream: " + dr);                      } catch (Exception e) {                          NotFoundException rnf = new NotFoundException(                              "File " + file + " from drawable resource ID #0x"                              + Integer.toHexString(id));                          rnf.initCause(e);                          throw rnf;                      }                  }              }          }             if (dr != null) {              dr.setChangingConfigurations(value.changingConfigurations);              cs = dr.getConstantState();              if (cs != null) {                  if (mPreloading) {                      sPreloadedDrawables.put(key, cs);                  } else {                      synchronized (mTmpValue) {                          //Log.i(TAG, "Saving cached drawable @ #" +                          //        Integer.toHexString(key.intValue())                          //        + " in " + this + ": " + cs);                          mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));                      }                  }              }          }             return dr;      } 

使用Drawable一般情况下效率较高,且不易发生内存泄露。

 

接下来我们来看下BitMap的使用

 

BitMapFactory.java

 

/**       * Decode an input stream into a bitmap. If the input stream is null, or       * cannot be used to decode a bitmap, the function returns null.       * The stream's position will be where ever it was after the encoded data       * was read.       *       * @param is The input stream that holds the raw data to be decoded into a       *           bitmap.       * @param outPadding If not null, return the padding rect for the bitmap if       *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If       *                   no bitmap is returned (null) then padding is       *                   unchanged.       * @param opts null-ok; Options that control downsampling and whether the       *             image should be completely decoded, or just is size returned.       * @return The decoded bitmap, or null if the image data could not be       *         decoded, or, if opts is non-null, if opts requested only the       *         size be returned (in opts.outWidth and opts.outHeight)       */      public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {          // we don't throw in this case, thus allowing the caller to only check          // the cache, and not force the image to be decoded.          if (is == null) {              return null         }             // we need mark/reset to work properly             if (!is.markSupported()) {              is = new BufferedInputStream(is, 16 * 1024);          }             // so we can call reset() if a given codec gives up after reading up to          // this many bytes. FIXME: need to find out from the codecs what this          // value should be.          is.mark(1024);             Bitmap  bm;             if (is instanceof AssetManager.AssetInputStream) {              bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(),                      outPadding, opts);          } else {              // pass some temp storage down to the native code. 1024 is made up,              // but should be large enough to avoid too many small calls back              // into is.read(...) This number is not related to the value passed              // to mark(...) above.              byte [] tempStorage = null             if (opts != null                 tempStorage = opts.inTempStorage;              if (tempStorage == null                 tempStorage = new byte[16 * 1024];              bm = nativeDecodeStream(is, tempStorage, outPadding, opts);          }             return finishDecode(bm, outPadding, opts);      } 

我们每次调用BitMapFactory中的生产bitmap的方法的时候都会new一个bitmap出来,为了避免内存溢出,我们有两种主要的思路:

1.       使用缓存,避免重复new同一个图片

2.       销毁bitmap,使用完之后立刻销毁bitmap

缓存需要自己实现,如果是销毁的话,可以使用Bitmap中的recycle()方法。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://127.0.0.1/wyysjy.html