内存泄露,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()提交刚才修改的界面

相关文章
|
7月前
|
机器学习/深度学习 存储 算法
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
296 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
|
6月前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
232 0
|
10月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
本文详细介绍了进程间通信(IPC)的六种主要方式:管道、信号、消息队列、共享内存、信号量和套接字。每种方式都有其特点和适用场景,如管道适用于父子进程间的通信,消息队列能传递结构化数据,共享内存提供高速数据交换,信号量用于同步控制,套接字支持跨网络通信。通过对比和分析,帮助读者理解并选择合适的IPC机制,以提高系统性能和可靠性。
1374 14
|
6月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
189 3
|
算法 JavaScript 前端开发
新生代和老生代内存划分的原理是什么?
【10月更文挑战第29天】新生代和老生代内存划分是JavaScript引擎为了更高效地管理内存、提高垃圾回收效率而采用的一种重要策略,它充分考虑了不同类型对象的生命周期和内存使用特点,通过不同的垃圾回收算法和晋升机制,实现了对内存的有效管理和优化。
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
1353 2
|
8月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
579 56
|
9月前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
447 0
|
11月前
|
运维 监控 Ubuntu
【运维】如何在Ubuntu中设置一个内存守护进程来确保内存不会溢出
通过设置内存守护进程,可以有效监控和管理系统内存使用情况,防止内存溢出带来的系统崩溃和服务中断。本文介绍了如何在Ubuntu中编写和配置内存守护脚本,并将其设置为systemd服务。通过这种方式,可以在内存使用超过设定阈值时自动采取措施,确保系统稳定运行。
423 4

热门文章

最新文章