内存泄露,OOM,ANR ,Devik 进程,Framework原理,Activity 生成一个 view,Android 中的动画,SurfaceView和V

简介: 内存泄露,OOM,ANR ,Devik 进程,Framework原理,Activity 生成一个 view,Android 中的动画,SurfaceView和V

什么情况下会导致内存泄露



内存泄露是个折腾的问题。


根本原因:长生命周期的对象持有短生命周期的对象。短周期对象就无法及时释放。


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()提交刚才修改的界面

目录
相关文章
|
4天前
|
算法 JavaScript 前端开发
新生代和老生代内存划分的原理是什么?
【10月更文挑战第29天】新生代和老生代内存划分是JavaScript引擎为了更高效地管理内存、提高垃圾回收效率而采用的一种重要策略,它充分考虑了不同类型对象的生命周期和内存使用特点,通过不同的垃圾回收算法和晋升机制,实现了对内存的有效管理和优化。
|
28天前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
132 2
|
1月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
存储 Linux 调度
深入理解操作系统:从进程管理到内存分配
【8月更文挑战第44天】本文将带你深入操作系统的核心,探索其背后的原理和机制。我们将从进程管理开始,理解如何创建、调度和管理进程。然后,我们将探讨内存分配,了解操作系统如何管理计算机的内存资源。最后,我们将通过一些代码示例,展示这些概念是如何在实际操作系统中实现的。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
|
2月前
|
安全 Android开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
145 8
|
13天前
|
API Android开发
判断前台 Activity 是否属于本进程
一种判断前台 Activity 是否属于本进程的方法。
24 10
|
1月前
|
缓存 算法 调度
深入浅出操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅。我们将从进程管理的基本概念出发,逐步深入到内存管理的复杂世界,最终探索如何通过实践技巧来优化系统性能。文章将结合理论与实践,通过代码示例,帮助读者更好地理解操作系统的核心机制及其在日常技术工作中的重要性。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往操作系统深层次理解的大门。
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。

热门文章

最新文章

相关实验场景

更多