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 != null) Android.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()方法。