什么情况下会导致内存泄露
内存泄露是个折腾的问题。
根本原因:长生命周期的对象持有短生命周期的对象。短周期对象就无法及时释放。
I. 静态集合类引起内存泄露
主要是hashmap,Vector等,如果是静态集合 这些集合没有及时setnull的话,就会一直持有这些对象。
II.remove 方法无法删除set集 Objects.hash(firstName, lastName);
经过测试,hashcode修改后,就没有办法remove了。
III. observer 我们在使用监听器的时候,往往是addxxxlistener,但是当我们不需要的时候,忘记removexxxlistener,就容易内存leak。
广播没有unregisterrecevier
IV.各种数据链接没有关闭,数据库contentprovider,io,sokect等。cursor
V.内部类:
java中的内部类(匿名内部类),会持有宿主类的强引用this。
所以如果是new Thread这种,后台线程的操作,当线程没有执行结束时,activity不会被回收。
Context的引用,当TextView 等等都会持有上下文的引用。如果有static drawable,就会导致该内存无法释放。
VI.单例
单例 是一个全局的静态对象,当持有某个复制的类A是,A无法被释放,内存leak(泄漏)。
如何避免OOM
减少内存对象的占用
I.ArrayMap/SparseArray代替hashmap
II.避免在android里面使用Enum
III.减少bitmap的内存占用
- inSampleSize:缩放比例,在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。
- decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。
- ARGB_8888:分别用8位来记录4个值,所以每个像素会占用32位。
ARGB_4444:分别用4位来记录4个值,所以每个像素会占用16位。
RGB_565:分别用5位、6位和5位来记录RGB三色值,所以每个像素会占用16位。
ALPHA_8:根据注释应该是不保存颜色值,只保存透明度(8位),每个像素会占用8位。
IV.减少资源图片的大小,过大的图片可以考虑分段加载
内存对象的重复利用
大多数对象的复用,都是利用对象池的技术。
I.listview/gridview/recycleview contentview的复用
II.inBitmap 属性对于内存对象的复用ARGB_8888/RBG_565/ARGB_4444/ALPHA_8
这个方法在某些条件下非常有用,比如要加载上千张图片的时候。
III.避免在ondraw方法里面 new对象
IV.StringBuilder 代替+
Android 中如何捕获未捕获的异常
CrashHandler
关键是实现Thread.UncaughtExceptionHandler
然后是在application的oncreate里面注册。
怎么避免ANR
ANR的关键是:处理超时,所以应该避免在UI线程,BroadcastReceiver 还有service主线程中,处理复杂的逻辑和计算而交给work thread操作。
1)避免在activity里面做耗时操作,oncreate & onresume
2)避免在onReceiver里面做过多操作
3)避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。
4)尽量使用handler来处理UI thread & workthread的交互。
如何解决ANR
首先定位ANR发生的log:
04-01 13:12:11.572 I/InputDispatcher( 220): **Application i****s not responding**:Window{2b263310com.android.email/com.android.email.activity.SplitScreenActivitypaused=false}. 5009.8ms since event, 5009.5ms since waitstarted
CPUusage from 4361ms to 699ms ago ----CPU在ANR发生前的使用情况 04-0113:12:15.872 E/ActivityManager( 220): 100%TOTAL: 4.8% user + 7.6% kernel + 87% iowait 04-0113:12:15.872 E/ActivityManager( 220): CPUusage from 3697ms to 4223ms later:-- ANR后CPU的使用量
从log可以看出,cpu在做大量的io操作。
所以可以查看io操作的地方。
当然,也有可能cpu占用不高,那就是 主线程被block(块)住了。
Devik 进程,linux 进程,线程的区别
Dalvik进程。
每一个android app都会独立占用一个dvm虚拟机,运行在linux系统中。
所以dalvik进程和linux进程是可以理解为一个概念。
描述一下 android 的系统架构
从小到上就是:
linux kernel,lib dalvik vm ,application framework, app
Framework 工作方式及原理,Activity 是如何生成一个 view 的,机制是什么
Framework是android 系统对 linux kernel,lib库等封装,提供WMS,AMS,bind机制,handler-message机制等方式,供app使用。
简单来说framework就是提供app生存的环境。
1)Activity在attch方法的时候,会创建一个phonewindow(window的子类)
2)onCreate中的setContentView方法,会创建DecorView
3)DecorView 的addview方法,会把layout中的布局加载进来。
Activity 创建一个 view 是通过 ondraw 画出来的, 画这个 view 之前呢,还会调用 onmeasure方法来计算显示的大小.
Surfaceview 是直接操作硬件的,因为视频播放对帧数有要求,onDraw 效率太低,不够使,Surfaceview 直接把数据写到显存。
Android FrameWork框架原理之进程是个什么东西
首先在Android中一个进程里只能放一个App。(特殊情况:同一个签名的两个App可以放到一个进程里,这里暂不考虑)。当一个App启动,系统zygote孵化器孵化出一个进程来执行这个App。zygote是个什么东东呢?zygote就是一个鸡,不停的生小鸡。zygote是一个进程,当需要启动一个进程来运行App时,zygote这个进程就把自己复制一份,放在另一个空间,就产生了一个进程给这个App来用。底层就是linux,fork(岔路口,分流)一个新进程。新fork的进程里面都有什么呢?
我们需要考虑的部分就是:里面有一个主线程,一个VM(虚拟机),一个Looper,一个MessageQueue。从这里就可以看出,一个进程里面有一个虚拟机。当然这是在Java层来看的,主线程就是UI线程,用来执行代码的,VM就是虚拟的机器,来执行Java字节码的。Looper和MessageQueue接下来细说。
进程产生主线程开始执行。这个时候先重申下线程是个什么东东。线程是执行一段代码用的,执行完这段代码,线程就死了。就这样。这时候我们想这样一个问题,主线程要执行什么,执行完了,死了,我们程序还运行个卵子。所以进程没死,线程应该永远也执行不完。Android中就是这样的,Android的主线程就是这样的,在一个whlie循环里打转转。怎样打转转的呢?主线程也就是一个普通的线程extends
Thread来的嘛,只不过是由FrameWork写好的。启动它的时候调它的start(),由FrameWork在创建它的时候帮我们调了,它的start又调了run(),执行的代码就在run()里面。我们想让主线程不死,就让它在run()里面执行一个 while(true){ },它就一直傻呆在这儿了。这个时候我们已经确保了主线程不会死,但是它要做事啊,不然要它干什么,他怎么做事也就看while循环里的代码怎样写了,这个时候就要看上一篇文章提到的EIT造型了
它只需要在while循环的时候执行一个接口的某个方法,而这个方法是由我们来实现的,我们给他塞进去什么,他就要执行什么,这样不仅解决了要主线程不死,又能一直做事的要求了嘛。就是这么个理,下面看Google是怎么写的。
前面我们讲了进程进程里面不止有主线程,还有一个Looper,一个MessageQueue。其实Looper类里面个loop();主线程要的执行的while循环就写在loop()里。主线程的run()里只要调用Looper.loop(),就一直在这个loop()方法里走不出来了,一直在while循环里。而MessageQueue就是一个信箱,MessageQueue这个对象里有一个队列,队列里放的就是Message对象。(对这个机制不理解的可以看我前面有一篇写的关于Handler的博客),每个Message类里定义的有一个Runnable的属性,用来放代码段的。
loop()里的每次循环就是到MessageQueue的Message队列里取一个Message对象,如果这个Message对象的Runnable属性不为null,就执行Runnable.run()里面的代码。就是这样咯。主线程的执行就是在Looper.loop()里面,循环的每一次都是去MessageQueue里面取一个Message,执行Message.Runnable.run()里面的代码。\
这个讲完了再说说Message从哪里来的问题。这个Message是由FrameWork放到MessageQueue里面的。再从进程创建开始讲,主线程,MessageQueue和Looper都有了,主线程执行到loop()里一直打转转,MessageQueue还是空的没有消息给它处理,然后根据上一篇说的由FrameWork框架的AMS(ActivityManagerService)来new出Activity对象,具体new出的是哪个Activity的实例,是根据Manifest文件里标明的那个启动的Activity。然后FrameWork给这个App的进程发出一个Message,放到MessageQueue里面,这个Message.runnable.run()里面的代码就是Activity.onCreate();
这个Activity的引用是刚new出来的那个作为启动的myActivity(假设在Manifest里面写明的第一个启动的Activity是myActivity)。它的onCreate()方法是我们已经重写了,主线程这个时候就执行到我们写在myActivity的onCreate()中的代码咯。
上面已经解释了怎么从App启动执行到我们写在myActivity.onCreate()中代码。现在再申明一个事情,myActivity的生命周期都不是由它自己控制的,而是由框架控制的。执行顺序并不是简单的执行完onCreate()直接去执行onResume()的。onResume也是框架来通过传Message来让主线程执行的。一般情况下一on开头的函数都是主线程来执行的。比如onCreate(),onResume()。。。再简单说下从onCreate()怎样执行到onResume()的。\
我们在myActivity.onCreate()里面有一行,setContentView(View),这行是要我们把自己写的布局传进去。无论是布局id还是直接一个view都是一样的,不做过多解释。(setContentView时究竟做了什么事,可以参见我的Activity的UI结构的那篇博客)我们知道有个View叫ContentView,我们把自己写的布局交给它,让他去显示。
由setContentView这个方法我们可以明显看出这里我们是调用基类的Activity的setContentView(),而Activity这个基类是框架定义的(参见上篇Acticity是EIT中的E&I由框架定义)。也就是说myActivity.onCreate()告诉框架我要setContentView(),然后框架把我们写的布局投射到屏幕上。这个调框架把布局投射到屏幕的动作,交由框架执行,框架中执行这些动作,到一个合适的时间,框架再发出Message,告诉主线程,现在是显示界面的时候了。然后主线程循环走到取Message,取出来发现框架让他执行myActivity.onResume(),他再去执行myActivity.onResume(),就这样框架决定了什么时候发出执行onResume的动作,主线程才去执行。这就保证了框架的主导权和控制权。由此我们也可以看出App执行的过程中,框架是全程参与的,而且处于主导地位,所以我在上篇说我们写的App只是一个套件组。至于怎么从onResume()再执行到其他的方法,和这个基本一样,不过多叙述。
由上面的分析我们可以看出,其实myActicity中的onCreate()和onResume等并不是真正的myActivity在这个时候创建和显示,这只是框架给App开发者留的接口,让App开发者去处理一些时机要做什么样的处理。其实像点击事件的响应也大致是这么一个流程,后面讲UI的时候再详细描述(其实点击事件就是一个订阅者模式,点击事件是由框架捕获交由主线程去处理的)。到此应该对Android整个App的启动有个基本的认知了吧。
Android 中的动画有哪几类,它们的特点和区别是什么
视图动画,或者说补间动画。只是视觉上的一个效果,实际view属性没有变化,性能好,但是支持方式少。
属性动画,通过变化属性来达到动画的效果,性能略差,支持点击等事件。android 3.0
帧动画,通过drawable一帧帧画出来。
Gif动画,原理同上,canvas画出来。
如何修改 Activity 进入和退出动画
overridePendingTransition
SurfaceView和View的区别
View在UI线程去更新自己;
SurfaceView则在一个子线程中去更新自己
SurfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面在UI的主线程中更新动画,时间一旦太长就会出现问题
SurfaceView在新的线程中更新画面所以不会阻塞你的UI主线程,但是涉及到线程同步,需要SurfaceView中 thread处理.
SurfaceView(表面)优点:
可以在另一个线程中更新界面
不会阻碍界面的交互
View和SurfaceView的区别
基于View的绘图效率不高,主要用于动画变化较少的程序
SurfaceView 绘图效率较高,用于界面更新频繁的程序,如相机预览。
SurfaceView 可以在另一个线程中更新界面。
四、SurfaceView 使用方式:
1.布局中放入SurfaceView
2.实现SurfaceHolder.Callback
3.绘制画布更新画布到SurfaceView(本例是在主线程中更新,可以另起一个线程更新)
通过lockCanvas()将返回绘制的canvas
绘制的过程中,对canvas的绘制不会立刻反应在界面上,直到执行unlockCanvasAndPost()提交刚才修改的界面