android OOM的那些事
16lz
2021-01-24
Context部分
View的构造函数
View.java
- /**
- *Simpleconstructortousewhencreatingaviewfromcode.
- *
- *@paramcontextTheContexttheviewisrunningin,throughwhichitcan
- *accessthecurrenttheme,resources,etc.
- */
- publicView(Contextcontext){
- mContext=context;
- mResources=context!=null?context.getResources():null;
- mViewFlags=SOUND_EFFECTS_ENABLED|HAPTIC_FEEDBACK_ENABLED;
- //Usedfordebugonly
- //++sInstanceCount;
- mTouchSlop=ViewConfiguration.get(context).getScaledTouchSlop();
- }
View 保存了对context的引用,mContext = context;
1.我们知道结构其实和DOM差不多,都会保存其父容器、子容器的引用,因而context的使用需要注意,不要使用静态的子View。
2.来看View中的setBackgroundDrawable方法
View.java
- /**
- *SetthebackgroundtoagivenDrawable,orremovethebackground.Ifthe
- *backgroundhaspadding,thisView'spaddingissettothebackground's
- *padding.However,whenabackgroundisremoved,thisView'spaddingisn't
- *touched.Ifsettingthepaddingisdesired,pleaseuse
- *{@link#setPadding(int,int,int,int)}.
- *
- *@paramdTheDrawabletouseasthebackground,ornulltoremovethe
- *background
- */
- publicvoidsetBackgroundDrawable(Drawabled){
- booleanrequestLayout=false;
- mBackgroundResource=0;
- /*
- *Regardlessofwhetherwe'resettinganewbackgroundornot,wewant
- *toclearthepreviousdrawable.
- */
- if(mBGDrawable!=null){
- mBGDrawable.setCallback(null);
- unscheduleDrawable(mBGDrawable);
- }
- if(d!=null){
- Rectpadding=sThreadLocal.get();
- if(padding==null){
- padding=newRect();
- sThreadLocal.set(padding);
- }
- if(d.getPadding(padding)){
- setPadding(padding.left,padding.top,padding.right,padding.bottom);
- }
- //ComparetheminimumsizesoftheoldDrawableandthenew.Ifthereisn'tanoldor
- //ifithasadifferentminimumsize,weshouldlayoutagain
- 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{
- /*Removethebackground*/
- mBGDrawable=null;
- if((mPrivateFlags&ONLY_DRAWS_BACKGROUND)!=0){
- /*
- *ThisviewONLYdrewthebackgroundbeforeandwe'reremoving
- *thebackground,sonowitwon'tdrawanything
- *(henceweSKIP_DRAW)
- */
- mPrivateFlags&=~ONLY_DRAWS_BACKGROUND;
- mPrivateFlags|=SKIP_DRAW;
- }
- /*
- *Whenthebackgroundisset,wetrytoapplyitspaddingtothis
- *View.Whenthebackgroundisremoved,wedon'ttouchthisView's
- *padding.ThisisnotedintheJavadocs.Hence,wedon'tneedto
- *requestLayout(),theinvalidate()belowissufficient.
- */
- //Theoldbackground'sminimumsizecouldhaveaffectedthis
- //View'slayout,solet'srequestLayout
- requestLayout=true;
- }
- computeOpaqueFlags();
- if(requestLayout){
- requestLayout();
- }
- mBackgroundSizeChanged=true;
- invalidate();
- }
我们注意到d.setCallback(this);
即Drawable保存了View的引用,处理了一些回调。因此,Drawable对象使用时需要注意了。不要作为静态对象使用。
Resource.getDrewable()方法是比较好的,Resource内部使用了一个Drawable.ConstantState的弱引用缓存,提高了效率。
来看代码:
Resource.java
- /*package*/DrawableloadDrawable(TypedValuevalue,intid)
- throwsNotFoundException{
- if(TRACE_FOR_PRELOAD){
- //Logonlyframeworkresources
- if((id>>>24)==0x1){
- finalStringname=getResourceName(id);
- if(name!=null)android.util.Log.d("PreloadDrawable",name);
- }
- }
- finallongkey=(((long)value.assetCookie)<<32)|value.data;
- Drawabledr=getCachedDrawable(key);
- if(dr!=null){
- returndr;
- }
- Drawable.ConstantStatecs=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=newColorDrawable(value.data);
- }
- if(dr==null){
- if(value.string==null){
- thrownewNotFoundException(
- "ResourceisnotaDrawable(colororpath):"+value);
- }
- Stringfile=value.string.toString();
- if(DEBUG_LOAD)Log.v(TAG,"Loadingdrawableforcookie"
- +value.assetCookie+":"+file);
- if(file.endsWith(".xml")){
- try{
- XmlResourceParserrp=loadXmlResourceParser(
- file,id,value.assetCookie,"drawable");
- dr=Drawable.createFromXml(this,rp);
- rp.close();
- }catch(Exceptione){
- NotFoundExceptionrnf=newNotFoundException(
- "File"+file+"fromdrawableresourceID#0x"
- +Integer.toHexString(id));
- rnf.initCause(e);
- throwrnf;
- }
- }else{
- try{
- InputStreamis=mAssets.openNonAsset(
- value.assetCookie,file,AssetManager.ACCESS_STREAMING);
- //System.out.println("Openedfile"+file+":"+is);
- dr=Drawable.createFromResourceStream(this,value,is,
- file,null);
- is.close();
- //System.out.println("Createdstream:"+dr);
- }catch(Exceptione){
- NotFoundExceptionrnf=newNotFoundException(
- "File"+file+"fromdrawableresourceID#0x"
- +Integer.toHexString(id));
- rnf.initCause(e);
- throwrnf;
- }
- }
- }
- }
- 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,"Savingcacheddrawable@#"+
- //Integer.toHexString(key.intValue())
- //+"in"+this+":"+cs);
- mDrawableCache.put(key,newWeakReference<Drawable.ConstantState>(cs));
- }
- }
- }
- }
- returndr;
- }
使用Drawable一般情况下效率较高,且不易发生内存泄露。
接下来我们来看下BitMap的使用
BitMapFactory.java
- /**
- *Decodeaninputstreamintoabitmap.Iftheinputstreamisnull,or
- *cannotbeusedtodecodeabitmap,thefunctionreturnsnull.
- *Thestream'spositionwillbewhereeveritwasaftertheencodeddata
- *wasread.
- *
- *@paramisTheinputstreamthatholdstherawdatatobedecodedintoa
- *bitmap.
- *@paramoutPaddingIfnotnull,returnthepaddingrectforthebitmapif
- *itexists,otherwisesetpaddingto[-1,-1,-1,-1].If
- *nobitmapisreturned(null)thenpaddingis
- *unchanged.
- *@paramoptsnull-ok;Optionsthatcontroldownsamplingandwhetherthe
- *imageshouldbecompletelydecoded,orjustissizereturned.
- *@returnThedecodedbitmap,ornulliftheimagedatacouldnotbe
- *decoded,or,ifoptsisnon-null,ifoptsrequestedonlythe
- *sizebereturned(inopts.outWidthandopts.outHeight)
- */
- publicstaticBitmapdecodeStream(InputStreamis,RectoutPadding,Optionsopts){
- //wedon'tthrowinthiscase,thusallowingthecallertoonlycheck
- //thecache,andnotforcetheimagetobedecoded.
- if(is==null){
- returnnull;
- }
- //weneedmark/resettoworkproperly
- if(!is.markSupported()){
- is=newBufferedInputStream(is,16*1024);
- }
- //sowecancallreset()ifagivencodecgivesupafterreadingupto
- //thismanybytes.FIXME:needtofindoutfromthecodecswhatthis
- //valueshouldbe.
- is.mark(1024);
- Bitmapbm;
- if(isinstanceofAssetManager.AssetInputStream){
- bm=nativeDecodeAsset(((AssetManager.AssetInputStream)is).getAssetInt(),
- outPadding,opts);
- }else{
- //passsometempstoragedowntothenativecode.1024ismadeup,
- //butshouldbelargeenoughtoavoidtoomanysmallcallsback
- //intois.read(...)Thisnumberisnotrelatedtothevaluepassed
- //tomark(...)above.
- byte[]tempStorage=null;
- if(opts!=null)
- tempStorage=opts.inTempStorage;
- if(tempStorage==null)
- tempStorage=newbyte[16*1024];
- bm=nativeDecodeStream(is,tempStorage,outPadding,opts);
- }
- returnfinishDecode(bm,outPadding,opts);
- }
我们每次调用BitMapFactory中的生产bitmap的方法的时候都会new一个bitmap出来,为了避免内存溢出,我们有两种主要的思路:
1. 使用缓存,避免重复new同一个图片
2. 销毁bitmap,使用完之后立刻销毁bitmap
缓存需要自己实现,如果是销毁的话,可以使用Bitmap中的recycle()方法。
更多相关文章
- Android中Timer与TimerTask的使用 间隔执行
- Android(安卓)SwitchButton(滑动开关)
- Spinner 使用方法小结
- 【ClassNotFoundExcept】Android应用程序启动时发生AndroidRunti
- Android(安卓)Device Monitor 报 open failed: Permission denie
- Android中利用画图类和线程画出闪烁的心形
- Android-中常用方法集锦
- Android(安卓)首选网络模式默认值的修改方法
- android 中如何限制 EditText 最大输入字符数(2)