android OOM的那些事
2393 点击·0 回帖
![]() | ![]() | |
![]() | View.java 1. /** 2. 3. * Simple constructor to use when creating a view from code. 4. 5. * 6. 7. * @param context The Context the view is running in, through which it can 8. 9. * access the current theme, resources, etc. 10. 11. */ 12. 13. public View(Context context) { 14. 15. mContext = context; 16. 17. mResources = context != null ? context.getResources() : null; 18. 19. mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED; 20. 21. // Used for debug only 22. 23. //++sInstanceCount; 24. 25. mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 26. 27. } View 保存了对context的,mContext = context; 1.我们知道结构其实和DOM差不多,都会保存其父容器、子容器的,因而context的使用需要注意,不要使用静态的子View。 2.来看View中的setBackgroundDrawable方法 View.java 1. /** 2. 3. * Set the background to a given Drawable, or remove the background. If the 4. 5. * background has padding, this View's padding is set to the background's 6. 7. * padding. However, when a background is removed, this View's padding isn't 8. 9. * touched. If setting the padding is desired, please use 10. 11. * {@link #setPadding(int, int, int, int)}. 12. 13. * 14. 15. * @param d The Drawable to use as the background, or null to remove the 16. 17. * background 18. 19. */ 20. 21. public void setBackgroundDrawable(Drawable d) { 22. 23. boolean requestLayout = false; 24. 25. 26. 27. mBackgroundResource = 0; 28. 29. 30. 31. /* 32. 33. * Regardless of whether we're setting a new background or not, we want 34. 35. * to clear the previous drawable. 36. 37. */ 38. 39. if (mBGDrawable != null) { 40. 41. mBGDrawable.setCallback(null); 42. 43. unscheduleDrawable(mBGDrawable); 44. 45. } 46. 47. 48. 49. if (d != null) { 50. 51. Rect padding = sThreadLocal.get(); 52. 53. if (padding == null) { 54. 55. padding = new Rect(); 56. 57. sThreadLocal.set(padding); 58. 59. } 60. 61. if (d.getPadding(padding)) { 62. 63. setPadding(padding.left, padding.top, padding.right, padding.bottom); 64. 65. } 66. 67. 68. 69. // Compare the minimum sizes of the old Drawable and the new. If there isn't an old or 70. 71. // if it has a different minimum size, we should layout again 72. 73. if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() || 74. 75. mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) { 76. 77. requestLayout = true; 78. 79. } 80. 81. 82. 83. d.setCallback(this); 84. 85. if (d.isStateful()) { 86. 87. d.setState(getDrawableState()); 88. 89. } 90. 91. d.setVisible(getVisibility() == VISIBLE, false); 92. 93. mBGDrawable = d; 94. 95. 96. 97. if ((mPrivateFlags ; SKIP_DRAW) != 0) { 98. 99. mPrivateFlags ;= ~SKIP_DRAW; 100. 101. mPrivateFlags |= ONLY_DRAWS_BACKGROUND; 102. 103. requestLayout = true; 104. 105. } 106. 107. } else { 108. 109. /* Remove the background */ 110. 111. mBGDrawable = null; 112. 113. 114. 115. if ((mPrivateFlags ; ONLY_DRAWS_BACKGROUND) != 0) { 116. 117. /* 118. 119. * This view ONLY drew the background before and we're removing 120. 121. * the background, so now it won't draw anything 122. 123. * (hence we SKIP_DRAW) 124. 125. */ 126. 127. mPrivateFlags ;= ~ONLY_DRAWS_BACKGROUND; 128. 129. mPrivateFlags |= SKIP_DRAW; 130. 131. } 132. 133. 134. 135. /* 136. 137. * When the background is set, we try to apply its padding to this 138. 139. * View. When the background is removed, we don't touch this View's 140. 141. * padding. This is noted in the javadocs. Hence, we don't need to 142. 143. * requestLayout(), the invalidate() below is sufficient. 144. 145. */ 146. 147. 148. 149. // The old background's minimum size could have affected this 150. 151. // View's layout, so let's requestLayout 152. 153. requestLayout = true; 154. 155. } 156. 157. 158. 159. computeOpaqueFlags(); 160. 161. 162. 163. if (requestLayout) { 164. 165. requestLayout(); 166. 167. } 168. 169. 170. 171. mBackgroundSizeChanged = true; 172. 173. invalidate(); 174. 175. } 我们注意到d.setCallback(this); 即Drawable保存了View的,处理了一些回调。因此,Drawable对象使用时需要注意了。不要作为静态对象使用。www.atcpu.com Resource.getDrewable()方法是比较好的,Resource内部使用了一个Drawable.ConstantState的弱缓存,提高了效率。 来看代码: Resource.java 1. /*package*/ Drawable loadDrawable(TypedValue value, int id) 2. 3. throws NotFoundException { 4. 5. 6. 7. if (TRACE_FOR_PRELOAD) { 8. 9. // Log only framework resources 10. 11. if ((id >>> 24) == 0x1) { 12. 13. final String name = getResourceName(id); 14. 15. if (name != null) Android.util.Log.d("PreloadDrawable", name); 16. 17. } 18. 19. } 20. 21. 22. 23. final long key = (((long) value.assetCookie) << 32) | value.data; 24. 25. Drawable dr = getCachedDrawable(key); 26. 27. 28. 29. if (dr != null) { 30. 31. return dr; 32. 33. } 34. 35. 36. 37. Drawable.ConstantState cs = sPreloadedDrawables.get(key); 38. 39. if (cs != null) { 40. 41. dr = cs.newDrawable(this); 42. 43. } else { 44. 45. if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT ;; 46. 47. value.type <= TypedValue.TYPE_LAST_COLOR_INT) { 48. 49. dr = new ColorDrawable(value.data); 50. 51. } 52. 53. 54. 55. if (dr == null) { 56. 57. if (value.string == null) { 58. 59. throw new NotFoundException( 60. 61. "Resource is not a Drawable (color or path): " + value); 62. 63. } 64. 65. 66. 67. String file = value.string.toString(); 68. 69. 70. 71. if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " 72. 73. + value.assetCookie + ": " + file); 74. 75. 76. 77. if (file.endsWith(".xml")) { 78. 79. try { 80. 81. XmlResourceParser rp = loadXmlResourceParser( 82. 83. file, id, value.assetCookie, "drawable"); 84. 85. dr = Drawable.createFromXml(this, rp); 86. 87. rp.close(); 88. 89. } catch (Exception e) { 90. 91. NotFoundException rnf = new NotFoundException( 92. 93. "File " + file + " from drawable resource ID #0x" 94. 95. + Integer.toHexString(id)); 96. 97. rnf.initCause(e); 98. 99. throw rnf; 100. 101. } 102. 103. 104. 105. } else { 106. 107. try { 108. 109. InputStream is = mAssets.openNonAsset( 110. 111. value.assetCookie, file, AssetManager.ACCESS_STREAMING); 112. 113. // System.out.println("Opened file " + file + ": " + is); 114. 115. dr = Drawable.createFromResourceStream(this, value, is, 116. 117. file, null); 118. 119. is.close(); 120. 121. // System.out.println("Created stream: " + dr); 122. 123. } catch (Exception e) { 124. 125. NotFoundException rnf = new NotFoundException( 126. 127. "File " + file + " from drawable resource ID #0x" 128. 129. + Integer.toHexString(id)); 130. 131. rnf.initCause(e); 132. 133. throw rnf; 134. 135. } 136. 137. } 138. 139. } 140. 141. } 142. 143. 144. 145. if (dr != null) { 146. 147. dr.setChangingConfigurations(value.changingConfigurations); 148. 149. cs = dr.getConstantState(); 150. 151. if (cs != null) { 152. 153. if (mPreloading) { 154. 155. sPreloadedDrawables.put(key, cs); 156. 157. } else { 158. 159. synchronized (mTmpValue) { 160. 161. //Log.i(TAG, "Saving cached drawable @ #" + 162. 163. // Integer.toHexString(key.intValue()) 164. 165. // + " in " + this + ": " + cs); 166. 167. mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); 168. 169. } 170. 171. } 172. 173. } 174. 175. } 176. 177. 178. 179. return dr; 180. 181. } 使用Drawable一般情况下效率较高,且不易发生内存泄露。 接下来我们来看下BitMap的使用 BitMapFactory.java 1. /** 2. 3. * Decode an input stream into a bitmap. If the input stream is null, or 4. 5. * cannot be used to decode a bitmap, the function returns null. 6. 7. * The stream's position will be where ever it was after the encoded data 8. 9. * was read. 10. 11. * 12. 13. * @param is The input stream that holds the raw data to be decoded into a 14. 15. * bitmap. 16. 17. * @param outPadding If not null, return the padding rect for the bitmap if 18. 19. * it exists, otherwise set padding to [-1,-1,-1,-1]. If 20. 21. * no bitmap is returned (null) then padding is 22. 23. * unchanged. 24. 25. * @param opts null-ok; Options that control downsampling and whether the 26. 27. * image should be completely decoded, or just is size returned. 28. 29. * @return The decoded bitmap, or null if the image data could not be 30. 31. * decoded, or, if opts is non-null, if opts requested only the 32. 33. * size be returned (in opts.outWidth and opts.outHeight) 34. 35. */ 36. 37. public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { 38. 39. // we don't throw in this case, thus allowing the caller to only check 40. 41. // the cache, and not force the image to be decoded. 42. 43. if (is == null) { 44. 45. return null; 46. 47. } 48. 49. 50. 51. // we need mark/reset to work properly 52. 53. 54. 55. if (!is.markSupported()) { 56. 57. is = new BufferedInputStream(is, 16 * 1024); 58. 59. } 60. 61. 62. 63. // so we can call reset() if a given codec gives up after reading up to 64. 65. // this many bytes. FIXME: need to find out from the codecs what this 66. 67. // value should be. 68. 69. is.mark(1024); 70. 71. 72. 73. Bitmap bm; 74. 75. 76. 77. if (is instanceof AssetManager.AssetInputStream) { 78. 79. bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(), 80. 81. outPadding, opts); 82. 83. } else { 84. 85. // pass some temp storage down to the native code. 1024 is made up, 86. 87. // but should be large enough to avoid too many small calls back 88. 89. // into is.read(...) This number is not related to the value passed 90. 91. // to mark(...) above. 92. 93. byte [] tempStorage = null; 94. 95. if (opts != null) 96. 97. tempStorage = opts.inTempStorage; 98. 99. if (tempStorage == null) 100. 101. tempStorage = new byte[16 * 1024]; 102. 103. bm = nativeDecodeStream(is, tempStorage, outPadding, opts); 104. 105. } 106. 107. 108. 109. return finishDecode(bm, outPadding, opts); 110. 111. } 我们每次调用BitMapFactory中的生产bitmap的方法的时候都会new一个bitmap出来,为了避免内存溢出,我们有两种主要的思路: 1. 使用缓存,避免重复new同一个图片 2. 销毁bitmap,使用完之后立刻销毁bitmap 缓存需要自己实现,如果是销毁的话,可以使用Bitmap中的recycle()方法。 摘自 ZillaPress | |
![]() | ![]() |