// Android 的中等进阶知识点理解
1 内存泄漏是什么 ??
2 Android oom 内存溢出 ??
3 Retrofit详解 ??
4 Rxjava详解 ??
5 Android 性能优化 ??
6 AIDL 详解 ??
7 ANR详解 和场景案例 ?
8 mvp模式 要懂代码原理 ??
9 Activity 的启动模式 ??
10 你在项目中遇见过哪些问题?? 有是怎么解决的 ??
11 两种布局进行嵌套了,怎么就是不让他们发生冲突呢 ?( 好像是什么冲突发生的一个联动 )
12 Android 事件的分发机制 ?? 三个方法分别是什么 ??
13 Handler 问题集合总结 ???
13.1 子线程可以和主线程通信吗?主线程可以和子线程通信吗???
13.2 Android中为什么主线程不会因为Looper.loop()方法造成阻塞,产生anr现象
13.3. 为什么Looper中的死循环不会阻塞主线程?
13.4 Handler 造成内存泄漏?如何解决?
=====================================================================================================================
// Android 的知识点 ----> 解答
(1)Android 内存泄漏。
1.Android 内存泄漏定义。
内存泄露 memory(mai ne rui) leak (li ke),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,
但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
内存泄露会最终导致内存溢出! 当你的请求量超过这个值的时候,就是内存溢出。
**如果一个存活时间长的对象,持有另一个存活时间短的对象,就会导致存活时间短的对象,在GC时被认定,而不能被及时回收也就是我们常说的内存泄漏
。
//引起内存泄漏的场景
1、对于生命周期比Activity长的对象,要避免直接引用Activity的context,可以考虑使用ApplicationContext;
2、广播接收器、EventBus(yi wen te ,ba si)等的使用过程中,注册/反注册应该成对使用;
3、不再使用的资源对象Cursor(ke 涩)、File、Bitmap等要记住正确关闭;
4、集合里面的东西、有加入就应该对应有相应的删除。
5、static关键字修饰的变量由于生命周期过长,容易造成内存泄漏
static对象的生命周期过长,应该谨慎使用。一定要使用要及时进行nul处理。
在andorid开发中,我们经常会在Activity的onCreate中注册广播接受器、EventBus(yi wen te ,ba si)等,如果忘记成对的使用反注册,可能会引起内
存泄漏。开发过程中应该养成良好的相关,
在onCreate或onResume中注册,要记得相应的在onDestroy或onPause中反注册。
在android中,资源性对象比如Cursor、File、Bitmap、视频等,系统都用了一些缓冲技术,在使用这些资源的时候,如果我们确保自己
不再使用这些资源了,要及时关闭,否
则可能引起内存泄漏。因为有些操作不仅仅只是涉及到Daik虚拟机,还涉及到底层C/C++等的内存
管理,不能完全奇希望虚拟机帮我们完成内存管理。
内存泄露解决方案:
1 静态变量导致的内存泄露 :
解决办法:在适当的时候讲静态量重置为null,使其不再持有引用,这样也可以避免内存泄露。比如静态的context,静态的view;静态变量存储在方法区
,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放)
2 静态单例模式持有Activity或者services无法被释放 :
解决办法:引用全局的context 单例模式对应,应用程序的生命周期,所以我们在构造单例的时候尽量避免使用Activity的上下文,而是使用
Application的上下文)
3 非静态内部类导致内存泄露(情况一)
(例如:Handler,非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导
致内存泄露。
mHandler会作为成员变量保存在发送的消息msg中,即msg持有mHandler的引用,而mHandler是Activity的非静态内部类实例,即mHandler持
有Activity的引用,那么我们就可以理解为msg间接持有Activity的引用。msg被发送后先放到消息队列MessageQueue中,然后等待Looper的轮询处理
(MessageQueue和Looper都是与线程相关联的,MessageQueue是Looper引用的成员变量,而Looper是保存在ThreadLocal中的)。那么当Activity退出后,
msg可能仍然存在于消息对列MessageQueue中未处理或者正在处理,那么这样就会导致Activity无法被回收,以致发生Activity的内存泄露。
解决办法:
通常在Android开发中如果要使用内部类,但又要规避内存泄露,一般都会采用静态内部类+弱引用的方式。(当GC执行垃圾回收时,遇到
Activity就会回收并释放所占据的内存单元),但是msg还是有可能存在消息队列MessageQueue中,所以更好的是在Activity销毁时就将mHandler的回调和
发送的消息给移除掉。
非静态内部类导致内存泄露(情况二)
1
非静态内部类造成内存泄露还有一种情况就是使用Thread或者AsyncTask
比如在Activity中直接new一个子线程Thread:(newThread new Runable方式
;新建的子线程Thread和AsyncTask都是匿名内部类对象,默认就隐式的持有外部Activity的引用,导致Activity内存泄露。:
要避免内存泄露的话还是需要像上面Handler一样使用静态内部类+弱应用的方式
2 未取消注册或回调导致内存泄露
:
比如我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个刚播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用
,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。
比如我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个刚
播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。
3 资源未关闭或释放导致内存泄露:
在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被
占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露。
LeakCanary(li ke ,kan nai rui)是一个傻瓜化并且可视化的内存泄露,分析工具
原理:监视一个即将被销毁的对象
白话:对被监控对象创建一个弱引用对象,后台线程检查这个对象是否被清除,如果没有就触发垃圾回收,垃圾回收之后如果这个弱引用对象还存在,说明
发生了内存泄漏
2 Android oom 内存溢出 ??
内存溢出 out of memory(mai bo rui),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;
原因:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对,对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的过小
(3) Retrofit详解 (ruai chuai fei te)
Retrofit其实我们可以理解为OkHttp的加强版,它也是一个网络加载框架。底层是使用OKHttp封装的。准确来说,网络请求的工作本质上是
OkHttp完成,而 Retrofit 仅负责网络请求接口的封装。它的一个特点是包含了特别多注解,方便简化你的代码量。并且还支持很多的开源
库(著名例子:Retrofit + RxJava)。
3 Retrofit的好处?
超级解耦
解耦?解什么耦?
我们在请求接口数据的时候,API接口定义和API接口使用总是相互影响,什么传参、回调等,耦合在一块。
有时候我们会考虑一下怎么封装我们的代码让这两个东西不那么耦合,这个就是Retrofit的解耦目标,也是它的最大的特点。
Retrofit为了实现解耦,使用了特别多的设计模式,这里附上一片很好的文章,里面讲的就是实现原理:
Retrofit分析-漂亮的解耦套路
3.1 支持同步、异步和RxJava
3.2 可以配置不同的反序列化工具来解析数据,如json、xml…
3.3 请求速度快,使用非常方便灵活
Retrofit 是一个网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装
分别有数据解析器和gson,以及rxjava支持的依赖
3.4 创建 用于描述网络请求 的接口
Retrofit将 Http请求 抽象成 Java接口:采用 注解 描述网络请求参数 和配置网络请求参数
(4) RxJava 详解
那么到底什么是RxJava呢?我对它的定义是:RxJava本质上是一个异步操作库,是一个能让你用极其简洁的逻辑去处理繁琐复杂任务的异步事件库。
Rxjava主要作用就是用来处理异步,当你的业务需要访问数据库,访问网络,或者任何耗时的操作,都可以借助Rxjava来实现。
RxJava好在哪
Android平台上为已经开发者提供了AsyncTask,Handler等用来做异步操作的类库,那我们为什么还要选择RxJava呢?答案是简洁!RxJava可以用非常
简洁的代码逻辑来解决复杂问题;而且即使业务逻辑的越来越复杂,它依然能够保持简洁!再配合上Lambda用简单的几行代码分分钟就解决你负责的
业务问题。简直逼格爆表,拿它装逼那是极好的!
(1)观察者模式
RxJava用到了设计模式中的观察者模式。支持数据或事件序列,允许对序列进行组合,并对线程、同步和并发数据结构进行了抽象。
(2)轻量
无依赖库、Jar包小于1M
(3)支持多语言
支持Java 6+和Android 2.3+。RxJava设计初衷就是兼容所有JVM语言,目前支持的JVM语言有Groovy,Clojure,JRuby, Kotlin和Scala。
(4)多线程支持
封装了各种并发实现,如threads, pools, event loops, fibers, actors。
RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。
(5) Android 性能优化 ??
1.启动速度的优化
不应在Application(额 pe ke 神)以及Activity的生命周期回调中做任何费时操作,具体指标大概是你在onCreate,
onResume,onStart等回调中所花费的总时间最好不要超过400ms,否
则用户在桌面点击应用图标后,将感觉到明显的卡顿。
2. 进行布局的优化?
在比如关于布局优化,就是尽量减少布局文件的层级。这个道理很浅显,布局中的层级少了,就意味着Android绘制时的工
作量少了,那么程序的性能自然就提高了。
删除布局中无用的控件和层次,其次有选择地使用性能比较低的ViewGroup。
例如:如果布局中既可以使用LinearLayout也可以使用RelativeLayout(ruai 了 父),那么就采用LinearLayout,这是因为
RelativeLayout的功能比较复杂,它的布局过程需要花费更多的
CPU时间。(帧布局)FrameLayout(fu 瑞 目)和LinearLayout一样都是一种简单高效的
ViewGroup,因此可以考虑使用它们,但是很多时候单纯通过一个LinearLayout或者FrameLayout(fu 瑞 目)无法实现
产品效果,需要通过嵌套的方式来完
成。这种情况下还是建议采用RelativeLayout,(ruai 了 父)因为ViewGroup的嵌套就相当于增加了布局的层级,同样会降低程序的性能。
线程优化的思想是采用线程池:
1 避免程序中存在大量的Thread。线程池可以重用内部的线程,从而避免了线程的创建和销毁带来的性能开销,
2 同时线程池可以有效的控制线程的最大并发数,避免了大量线程因互相抢占系统资源而导致阻塞现象的发生。
3 因此在实际开发中应尽量采用线程池,而不是每次都要创建一个Thread对象。
(1)避免创建过多的对象
(2)不要过多的使用枚举类型,枚举占用的内存空间比整型要大
(3)常量请使用static final来修饰
(4)适当的使用软引用和弱引用
(5)采用内存缓存和磁盘缓存
(6)尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏的问题
性能问题一般归结为三类:
1.UI卡顿和稳定性:这类问题用户可直接感知,最为重要;
2.内存问题:内存问题主要表现为内存泄露,或者内存使用不当导致的内存抖动。如果存在内存泄露,应用会不断消耗内存,易导致频繁
gc使系统出现卡顿,或者出现OOM报
错;内存抖动也会导致UI卡顿。
3.耗电问题:会影响续航,表现为不必要的自启动,不恰当持锁导致系统无法正常休眠,系统休眠后频繁唤醒系统等
优化内存空间:
没有内存泄漏,并不意味着内存就不需要优化,在移动设备上,由于物理设备的存储空间有限,Android 系统对每个应用进程也都分配了有限的堆内存,因
此使用最小内存对象或者资源可以减小内存开销,同时让GC 能更高效地回收不再需要使用的对象,让应用堆内存保持充足的可用内存,使应用更稳定高效
地运行。常见做法如下:
1 对象引用。强引用、软引用、弱引用、虚引用四种引用类型,根据业务需求合理使用不同,选择不同的引用类型。
2 减少不必要的内存开销。注意自动装箱,增加内存复用,比如有效利用系统自带的资源、视图复用、对象池、Bitmap对象的复用。
3 使用最优的数据类型。比如针对数据类容器结构,可以使用ArrayMap数据结构,避免使用枚举类型,使用缓存Lrucache等等。
4 图片内存优化。可以设置位图规格,根据采样因子做压缩,用一些图片缓存方式对图片进行管理等等。
稳定性优化:
Android 应用的稳定性定义很宽泛,影响稳定性的原因很多,比如内存使用不合理、代码异常场景考虑不周全、代码逻辑不合理等,都会对应用的稳定性造
成影响。其中最常见的两个场景是:Crash 和 ANR,这两个错误将会使得程序无法使用,比较常用的解决方式如下:
1 提高代码质量。比如开发期间的代码审核,看些代码设计逻辑,业务合理性等。
2 代码静态扫描工具。常见工具有Android Lint、Findbugs、Checkstyle、PMD等等。
3Crash监控。把一些崩溃的信息,异常信息及时地记录下来,以便后续分析解决。
4Crash上传机制。在Crash后,尽量先保存日志到本地,然后等下一次网络正常时再上传日志信息。
内存分析工具:
做内存优化前,需要了解当前应用的内存使用现状,通过现状去分析哪些数据类型有问题,各种类型的分布情况如何,以及在发现问题后如何发现是哪些具
体对象导致的,这就需要相关工具来帮助我们。
1,Memory Monitor (mai bo 瑞 毛内特)
Memory Monitor 是一款使用非常简单的图形化工具,可以很好地监控系统或应用的内存使用情况,主要有以下功能:
显示可用和已用内存,并且以时间为维度实时反应内存分配和回收情况。
快速判断应用程序的运行缓慢是否由于过度的内存回收导致。
快速判断应用是否由于内存不足导致程序崩溃。
2,Heap Viewer (hei pu 优窝)
Heap Viewer 的主要功能是查看不同数据类型在内存中的使用情况,可以看到当前进程中的 Heap Size 的情况,分别有哪些类型的数据,以及各种类型数
据占比情况。通过分析这些数据来找到大的内存对象,再进一步分析这些大对象,进而通过优化减少内存开销,也可以通过数据的变化发现内存泄漏。
3,Memory Analyzer Tool(MAT) (mai bo 瑞 ,啊 了 ruai 则 ,tu,)
MAT 是一个快速,功能丰富的 Java Heap 分析工具,通过分析 Java 进程的内存快照 HPROF 分析,从众多的对象中分析,快速计算出在内存中对象占用的
大小,查看哪些对象不能被垃圾收集器回收,并可以通过视图直观地查看可能造成这种结果的对象。
(6) AIDL 详解 ??
AIDL 详解?
AIDL Service可以将程序中的某个接口公开,这样在其他的应用程序中就可以象访问本地对象一样访问AIDL服务对象了。
AIDL的目的就是实现通信接口
AIDL的作用是让你可以在自己的APP里绑定一个其他APP的service,这样你的APP可以和其他APP交互。
(7) ANR详解 和场景案例 ?
在Android系统中,应用程序的响应由Activity Manager及Window Manager两个系统服务所监控。通常情况下,应用常出现四类情况
,系统将报ANR:
ANR的类型
1.InputDispatchTimeout(常见)
input事件在5S内没有处理完成发生了ANR。
logcat日志关键字:Input event dispatching timed out
2.BroadcastTimeout
前台Broadcast:onReceiver在10S内没有处理完成发生ANR。
后台Broadcast:onReceiver在60s内没有处理完成发生ANR。
logcat日志关键字:Timeout of broadcast BroadcastRecord
3.ServiceTimeout
前台Service:onCreate,onStart,onBind等生命周期在20s内没有处理完成发生ANR。
后台Service:onCreate,onStart,onBind等生命周期在200s内没有处理完成发生ANR
logcat日志关键字:Timeout executing service
4.ContentProviderTimeout
ContentProvider 在10S内没有处理完成发生ANR。
logcat日志关键字:timeout publishing content providers
一、 查看events_log
在events_log,从日志中搜索关键字:am_anr,找到出:
1 ANR时间:07-20 15:36:36.472
2 进程pid:1480
3 进程名:com.xxxx.moblie
4 ANR类型:Input dispatching timed out
5 events_log 中的关键字 am_anr
通过traces文件,我们可以拿到线程名、堆栈信息、线程当前状态
总结一下这分析流程:
首先我们在events_log中搜索am_anr,找到出现ANR的时间点、进程PID、ANR类型、
然后再找搜索PID,找前5秒左右的日志。过滤ANR IN 查看CPU信息,
接着查看traces.txt,找到java的堆栈信息定位代码位置,最后查看源码,分析与解决问题。这个过程基本能找到发生ANR的来龙去脉。
**** 总体思路
导出ANR日志信息,根据日志信息,判断确认发生ANR的包名类名,进程号,发生时间,导致ANR原因类型等。
关注系统资源信息,包括ANR发生前后的CPU,内存,IO等系统资源的使用情况。
查看主线程状态,关注主线程是否存在耗时、死锁、等锁等问题,判断该ANR是App导致还是系统导致的。
结合应用日志,代码或源码等,分析ANR问题发生前,应用是否有异常,其中具体问题具体分析。
ANR问题原因可以分为两大类,一是系统资源不足导致,二是自身代码逻辑导致,
// 一个详解的博客网址
https://blog.csdn.net/bugmiao/article/details/111643815
(8) 说说你的这些项目的架构。 mvp模式
MVP 相对来说 比MVC 能更好一点。 MVP 现在能主流一点吧。
MVP模式是App的一种分包的一种架构 也是一种架构 Model View Presenter (po rai te)
MVP代表Model,View和Presenter。
View层负责处理用户事件和视图部分的展示。在Android中,它可能是Activity或者Fragment类。
Model层负责 数据的实体模型、数据访问接口、数据库操作等,用于存取数据 和 处理业务逻辑。
Presenter层是连接(或适配)View和Model的桥梁。
好处:
分离了视图逻辑 和 业务逻辑,降低了耦合
MVC、MVP与MVVM之间的对比分析?
MVC:PC时代就有的架构方案,在Android上也是最早的方案,Activity/Fragment这些上帝角色既承担了V的角色,也承担了C的角色,
小项目开发起来十分顺手,大项目就会遇到耦合过重,Activity/Fragment类过大等问题。
MVP:为了解决MVC耦合过重的问题,MVP的核心思想就是提供一个Presenter(pe 燃 特)将视图逻辑I和业务逻辑相分离,达到解耦的目的。
MVVM:使用ViewModel代替Presenter(pe 燃 特),实现数据与View的双向绑定,这套框架最早使用的data-binding(邦德)
将数据绑定到xml里,这么做在大规模应用的时候是不行的,不过数据绑定是一个很有用的概念,
(9) Activic 的启动模式 ??
standard、 singleTop、 singleTask、 singleinstance 在代码中用的意思??
1. Standard(标准模式,默认)
2. SingleTop(栈顶复用模式)
1 —》 ----》 2 ----》 3(top模式)—》然后在点击3的时候他就存在复用了。不会再出现栈顶了
3. SingleTask(栈内复用模式)
singleTask( 一键退出 )比如: 1 --》 2 --》 3 --》 4 --》 ( 设置singleTask模式后 ) 返回退出键后 直接到 1的界面
4. SingleInstance(单实例模式) ( 比如说是打电话的方式,它就是个 SingleInstance 的一个模式 直接是它的一个 )
(10)两种布局进行嵌套了,怎么就是不让他们发生冲突呢 ?( 好像是什么冲突发生的一个联动 )
外部滑动方向与内部方向不一致
外部滑动方向与内部方向一致
从根本的情况来解决就是: 重写滑动事件 做事件分发
in te sai bu te
从父View着手,重写onInterceptTouchEvent方法,在父View需要拦截的时候拦截事件,不需要则不拦截返回false。
用外部拦截法自定义一个方法,在开始滑动的条件下,判断手势的横纵方向的距离大小,判断用户到底是要横向滑动还是纵向滑动。纵
向滑动意为拉动刷新,就拦截事件,横向滑动就意为滑动viewpager,就不拦截事件。解决效果:
内部拦截法 外部拦截法
内部拦截法是在View中进行拦截。此时ViewGroup、View均需要进行处理。然后再View中通过
外部拦截法在ViewGroup中进行拦截
在Android实际开发过程中经常会遇到View之间的滑动冲突,如ScrollView与Listview、RecyclerView之间的嵌套使用。在很好的解决
此类问题之前,我们应深入的了解Android事件响应机制。
(11) Android 事件的分发机制 ??
Android 事件机制传递的顺序 Activity —> ViewGroup —> View
重要的三个方法:
dispatchTouchEvent:用于进行点击事件的分发
oninterceptTouchEvent:用于进行点击事件的拦截
onTouchEvent:用于处理点击事件
执行流程分别是:
Activity: dispatchTouchEvent() ------》 onTouchEvento
()
ViewGroup: dispatchTouchEvent()-----》 onTouchEvento() oninterceptTouchEvent()-------》 onTouchEvento
()
View : dispatchTouchEvent()-----》 onTouchEven()
(12) 你在项目中遇见过哪些问题?? 有是怎么解决的 ??
12.1 安卓6三方手机(三星Galaxy5S),选择闹钟重复周期,勾选框勾选会错行
由于性能问题,他没有使用 ViewHolder 的一个复用,就是第一次进来的时候,是直接获取到布局然后哪到控件最后进行 view.setTag(holder):
盘
墓
在次进来 if((view.getTag0) instanceof ViewHolder){
在次得到之后就不存在,消耗资源的一个操作,然后拿到的之前存入的 ViewHoler对象
holder =(ViewHolder) view.getTag0;
12.2 对手表找手机,混合声音的修改。
知识点音频焦点
//先请求音频焦点
mAudioManager.requestAudiofocus(null, AudioManager.STREAM_MUSIC.
AudioManager.AUDIOFOCUS GAIN _TRANSIENT_EXCLUSIVE);
// 停止播放时,放弃音频的焦点
mAudioManager.abandonAudioFocus(null);
12.3 小语种阿拉伯语言镜像语言和镜像布局出现的问题。 marging(ma针) Right (ruai te)
android:layout_marginEnd="-6dp"
android:layout_marginStart:---->该属性等同于 android:layout _marginRight。
android:layout marginEnd:---->该属性等同于
android:layout_marginLeft。
用来自动匹配从右向左写的语言(阿拉伯语等)
layout_marginStart是Android4.2新添加的参数,用来自动匹配从右向左写的语言(阿拉伯语等)。正常的中文英文,layout_marginStart就等于
layout_marginLeft,当系统
圈
置为阿拉伯语时,layout_marginStart就相当于layout_marginRight,这是系统自动实现的。
同时使用layout_marginLeft和
layout marginStart。首先两者参数不会叠加,其次,只要定义了layout marginStart,不管layout_marginLeft在前在后定义,系统都只使用
layout_marginStart的参数来布局。
12.4 对于Android 性能的优化 ??
12.4.1.启动速度的优化
(1)不应在Application(额 pe ke 神)以及Activity的生命周期回调中做任何费时操作,具体指标大概是你在onCreate,
onResume,onStart等回调中所花费的总时间最好不要超过400ms,否
则用户在桌面点击应用图标后,将感觉到明显的卡顿。
12.4.2. 进行布局的优化?
在比如关于布局优化,就是尽量减少布局文件的层级。这个道理很浅显,布局中的层级少了,就意味着Android绘制时的工
作量少了,那么程序的性能自然就提高了。
删除布局中无用的控件和层次,其次有选择地使用性能比较低的ViewGroup。
例如:如果布局中既可以使用LinearLayout也可以使用RelativeLayout(ruai 了 父),那么就采用LinearLayout,这是因为
RelativeLayout的功能比较复杂,它的布局过程需要花费更多的
CPU时间。(帧布局)FrameLayout(fu 瑞 目)和LinearLayout一样都是一种简单高效的
ViewGroup,因此可以考虑使用它们,但是很多时候单纯通过一个LinearLayout或者FrameLayout(fu 瑞 目)无法实现
产品效果,需要通过嵌套的方式来完
成。这种情况下还是建议采用RelativeLayout,(ruai 了 父)因为ViewGroup的嵌套就相当于增加了布局的层级,同样会降低程序的性能。
12.4.3.线程优化
线程优化的思想就是采用线程池,避免程序中存在大量的Thread。线程池可以重用内部的线程,从而避免了线程的创建和销毁锁带来的性
能开销,同时线程池还能有效地控制线
程池的最大并法术,避免大量的线程因互相抢占系统资源从而导致阻塞现象的发生。因此在实际开
发中,尽量采用线程池,而不是每次都要创建一个Thread对象。
12.5. Handler造成内存泄漏? 和解决的方案?
内存泄漏的原因是:类成员的生命周期,大于类对象的生命周期 ----- 换句话说就是一个需要销毁的对象,由于成员被外部引用而无法销毁。-----
比如,
一个Activity中有一个Handler成员对象。如果这个Handler发送了一个延时很长的消息,那么这个Handler在很长一段时间内都不能销毁,
因为发送的消息的Message引用了这个Handler(msg.target),而这个Message还在消息队列中存活。这样,即使finish了
这个Activity,它仍然会在内存中存活,造成内存泄漏。
解决方式一是:
使用static修饰Handler。如果Handler需要引用Activity,那么使用WeakReference(wei ke ::ruai bei)弱引用。
解决方式二是:
在Activity销毁的时候,在onDestroy()回调中清除Handler的所有回调。但是注意如果发送的消息周期的确是长于本Activity的,那么就不能使用方法二了
。
(13) Handler 问题集合总结 ???
13.1 子线程可以和主线程通信吗? 主线程可以和子线程通信吗???
13.2 Android中为什么主线程不会因为Looper.loop()方法造成阻塞,产生anr现象
13.3 为什么Looper中的死循环不会阻塞主线程?
卡顿的主要原因是主线程干了太多的事情,导致了16ms(毫秒)之内无法完成一帧的绘制,
ANR的发生的必要条件是,需要有输入,也就是用户点击了屏幕之类的,否则是不会发生ANR的。
究其根本原因,造成ANR的原因很大程度上大致与卡顿是相似的,但是二者是完全不同性质的两个事件。
卡顿是因为出于某种原因导致的绘制时间过长,而ANR的原因是对用户的操作响应超时。
而Looper中的死循环是为了读取消息,要知道Android应用本质上是消息驱动的,不管是卡顿还是ANR,本质上都是对应Handler或者Handler.Callback
的handleMessage()处理消息方法的执行时间太长;而Looper中的死循环是在体系之外的,不在某个Handler的handleMessage()方法体之中,自然也就
不会引起卡顿和ANR了。
13.4 在子线程中创建Handler报错是为什么?
没有lopper.prepare(ke pai er)
13.5 Handler机制,sendMessage和post(Runnable)(ruai li bao ) 的区别?
总结 post方法和handleMessage方法的不同在于,post的runnable(ruai li bao 可执行的)会直接在callback(kao ba ke 回调)
中调用run方法执行, 而sendMessage方法要用户主动重写mCallback或者handleMessage方法来处理。
13.6 消息队列的数据结构是什么?
链表。
13.7 Handler 是怎样起到切换线程作用的? 是怎样在子线程发送消息然后在主线程处理的?
Handler发送消息的过程仅仅只是把消息放进消息队列里,这是在子线程里完成的。
主线程的Looper是一直在主线程运行的,
当发现有新消息之后,就会提取出来,然后再在主线程把消息传递给Handler进行处理;这样就完成了线程切换。