歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android OOM的那些事

Android OOM的那些事

日期:2017/3/1 10:25:09   编辑:Linux編程

Context部分

View的構造函數

View.java

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

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

1.我們知道結構其實和DOM差不多,都會保存其父容器、子容器的引用,因而context的使用需要注意,不要使用靜態的子View。

2.來看View中的setBackgroundDrawable方法

View.java

  1. /**
  2. * Set the background to a given Drawable, or remove the background. If the
  3. * background has padding, this View's padding is set to the background's
  4. * padding. However, when a background is removed, this View's padding isn't
  5. * touched. If setting the padding is desired, please use
  6. * {@link #setPadding(int, int, int, int)}.
  7. *
  8. * @param d The Drawable to use as the background, or null to remove the
  9. * background
  10. */
  11. public void setBackgroundDrawable(Drawable d) {
  12. boolean requestLayout = false;
  13. mBackgroundResource = 0;
  14. /*
  15. * Regardless of whether we're setting a new background or not, we want
  16. * to clear the previous drawable.
  17. */
  18. if (mBGDrawable != null) {
  19. mBGDrawable.setCallback(null);
  20. unscheduleDrawable(mBGDrawable);
  21. }
  22. if (d != null) {
  23. Rect padding = sThreadLocal.get();
  24. if (padding == null) {
  25. padding = new Rect();
  26. sThreadLocal.set(padding);
  27. }
  28. if (d.getPadding(padding)) {
  29. setPadding(padding.left, padding.top, padding.right, padding.bottom);
  30. }
  31. // Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
  32. // if it has a different minimum size, we should layout again
  33. if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||
  34. mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {
  35. requestLayout = true;
  36. }
  37. d.setCallback(this);
  38. if (d.isStateful()) {
  39. d.setState(getDrawableState());
  40. }
  41. d.setVisible(getVisibility() == VISIBLE, false);
  42. mBGDrawable = d;
  43. if ((mPrivateFlags & SKIP_DRAW) != 0) {
  44. mPrivateFlags &= ~SKIP_DRAW;
  45. mPrivateFlags |= ONLY_DRAWS_BACKGROUND;
  46. requestLayout = true;
  47. }
  48. } else {
  49. /* Remove the background */
  50. mBGDrawable = null;
  51. if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {
  52. /*
  53. * This view ONLY drew the background before and we're removing
  54. * the background, so now it won't draw anything
  55. * (hence we SKIP_DRAW)
  56. */
  57. mPrivateFlags &= ~ONLY_DRAWS_BACKGROUND;
  58. mPrivateFlags |= SKIP_DRAW;
  59. }
  60. /*
  61. * When the background is set, we try to apply its padding to this
  62. * View. When the background is removed, we don't touch this View's
  63. * padding. This is noted in the Javadocs. Hence, we don't need to
  64. * requestLayout(), the invalidate() below is sufficient.
  65. */
  66. // The old background's minimum size could have affected this
  67. // View's layout, so let's requestLayout
  68. requestLayout = true;
  69. }
  70. computeOpaqueFlags();
  71. if (requestLayout) {
  72. requestLayout();
  73. }
  74. mBackgroundSizeChanged = true;
  75. invalidate();
  76. }

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

即Drawable保存了View的引用,處理了一些回調。因此,Drawable對象使用時需要注意了。不要作為靜態對象使用。

Resource.getDrewable()方法是比較好的,Resource內部使用了一個Drawable.ConstantState的弱引用緩存,提高了效率。

來看代碼:

Resource.java

  1. /*package*/ Drawable loadDrawable(TypedValue value, int id)
  2. throws NotFoundException {
  3. if (TRACE_FOR_PRELOAD) {
  4. // Log only framework resources
  5. if ((id >>> 24) == 0x1) {
  6. final String name = getResourceName(id);
  7. if (name != null) Android.util.Log.d("PreloadDrawable", name);
  8. }
  9. }
  10. final long key = (((long) value.assetCookie) << 32) | value.data;
  11. Drawable dr = getCachedDrawable(key);
  12. if (dr != null) {
  13. return dr;
  14. }
  15. Drawable.ConstantState cs = sPreloadedDrawables.get(key);
  16. if (cs != null) {
  17. dr = cs.newDrawable(this);
  18. } else {
  19. if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
  20. value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
  21. dr = new ColorDrawable(value.data);
  22. }
  23. if (dr == null) {
  24. if (value.string == null) {
  25. throw new NotFoundException(
  26. "Resource is not a Drawable (color or path): " + value);
  27. }
  28. String file = value.string.toString();
  29. if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
  30. + value.assetCookie + ": " + file);
  31. if (file.endsWith(".xml")) {
  32. try {
  33. XmlResourceParser rp = loadXmlResourceParser(
  34. file, id, value.assetCookie, "drawable");
  35. dr = Drawable.createFromXml(this, rp);
  36. rp.close();
  37. } catch (Exception e) {
  38. NotFoundException rnf = new NotFoundException(
  39. "File " + file + " from drawable resource ID #0x"
  40. + Integer.toHexString(id));
  41. rnf.initCause(e);
  42. throw rnf;
  43. }
  44. } else {
  45. try {
  46. InputStream is = mAssets.openNonAsset(
  47. value.assetCookie, file, AssetManager.ACCESS_STREAMING);
  48. // System.out.println("Opened file " + file + ": " + is);
  49. dr = Drawable.createFromResourceStream(this, value, is,
  50. file, null);
  51. is.close();
  52. // System.out.println("Created stream: " + dr);
  53. } catch (Exception e) {
  54. NotFoundException rnf = new NotFoundException(
  55. "File " + file + " from drawable resource ID #0x"
  56. + Integer.toHexString(id));
  57. rnf.initCause(e);
  58. throw rnf;
  59. }
  60. }
  61. }
  62. }
  63. if (dr != null) {
  64. dr.setChangingConfigurations(value.changingConfigurations);
  65. cs = dr.getConstantState();
  66. if (cs != null) {
  67. if (mPreloading) {
  68. sPreloadedDrawables.put(key, cs);
  69. } else {
  70. synchronized (mTmpValue) {
  71. //Log.i(TAG, "Saving cached drawable @ #" +
  72. // Integer.toHexString(key.intValue())
  73. // + " in " + this + ": " + cs);
  74. mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
  75. }
  76. }
  77. }
  78. }
  79. return dr;
  80. }

使用Drawable一般情況下效率較高,且不易發生內存洩露。

接下來我們來看下BitMap的使用

BitMapFactory.java

  1. /**
  2. * Decode an input stream into a bitmap. If the input stream is null, or
  3. * cannot be used to decode a bitmap, the function returns null.
  4. * The stream's position will be where ever it was after the encoded data
  5. * was read.
  6. *
  7. * @param is The input stream that holds the raw data to be decoded into a
  8. * bitmap.
  9. * @param outPadding If not null, return the padding rect for the bitmap if
  10. * it exists, otherwise set padding to [-1,-1,-1,-1]. If
  11. * no bitmap is returned (null) then padding is
  12. * unchanged.
  13. * @param opts null-ok; Options that control downsampling and whether the
  14. * image should be completely decoded, or just is size returned.
  15. * @return The decoded bitmap, or null if the image data could not be
  16. * decoded, or, if opts is non-null, if opts requested only the
  17. * size be returned (in opts.outWidth and opts.outHeight)
  18. */
  19. public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
  20. // we don't throw in this case, thus allowing the caller to only check
  21. // the cache, and not force the image to be decoded.
  22. if (is == null) {
  23. return null;
  24. }
  25. // we need mark/reset to work properly
  26. if (!is.markSupported()) {
  27. is = new BufferedInputStream(is, 16 * 1024);
  28. }
  29. // so we can call reset() if a given codec gives up after reading up to
  30. // this many bytes. FIXME: need to find out from the codecs what this
  31. // value should be.
  32. is.mark(1024);
  33. Bitmap bm;
  34. if (is instanceof AssetManager.AssetInputStream) {
  35. bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(),
  36. outPadding, opts);
  37. } else {
  38. // pass some temp storage down to the native code. 1024 is made up,
  39. // but should be large enough to avoid too many small calls back
  40. // into is.read(...) This number is not related to the value passed
  41. // to mark(...) above.
  42. byte [] tempStorage = null;
  43. if (opts != null)
  44. tempStorage = opts.inTempStorage;
  45. if (tempStorage == null)
  46. tempStorage = new byte[16 * 1024];
  47. bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
  48. }
  49. return finishDecode(bm, outPadding, opts);
  50. }

我們每次調用BitMapFactory中的生產bitmap的方法的時候都會new一個bitmap出來,為了避免內存溢出,我們有兩種主要的思路:

1. 使用緩存,避免重復new同一個圖片

2. 銷毀bitmap,使用完之後立刻銷毀bitmap

緩存需要自己實現,如果是銷毀的話,可以使用Bitmap中的recycle()方法。

更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11

Copyright © Linux教程網 All Rights Reserved