Android基础(1 - 15)
目录
1,什么是 ANR 如何避免它?
2,Android 事件分发机制(重要);
3,Android View的绘制流程(重要);
4,Android 消息处理机制(重要);
5,四大组件及生命周期(重要);
6,Service 的一些细节;
7,什么是AIDL 以及如何使用?(例子,了解);
8,Serializable 和Parcelable 的区别(重要);
9,保证Android后台不被杀死的几种方法;
10,Service 的生命周期(重要);
11,一张Bitmap所占内存以及内存占用的计算;
12,什么情况下会导致内存泄露(重要)?
13,Looper、Handler、MessageQueue,Message作用和存在的意义;
14,synchronized同步时,锁的位置;
15,ThreadLocal详解(不了解使用场景);
1,什么是 ANR 如何避免它?
在 Android 上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。
用户可以选择让程序继续运行,但是,他们在使用你的应用程序时,并不希望每次都要处理这个对话框。因此,在程序里对响应性能的设计很重要,这样,系统不会显示ANR 给用户。
不同的组件发生 ANR 的时间不一样,主线程(Activity、Service)是 5 秒,BroadCastReceiver 是 10 秒。
解决方案:
将所有耗时操作,比如访问网络,Socket 通信,查询大量 SQL 语句,复杂逻辑计算等都放在子线程中去,然后通过 handler.sendMessage、runOnUITread、AsyncTask 等方式更新 UI。无论如何都要确保用户界面操作的流畅度。如果耗时操作需要让用户等待,那么可以在界面上显示进度条。
2,Android 事件分发机制(重要):
【转发】
Android 事件分发机制
-
整个View的事件转发流程是:
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent -
总结起来:
-
dispatchTouchEvent
return true:表示该View内部消化掉了所有事件
return false:表示事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费
return super.dispatchTouchEvent(ev):默认事件将分发给本层的事件拦截onInterceptTouchEvent方法进行处理 -
onInterceptTouchEvent
return true:表示将事件进行拦截,并将拦截到的事件交由本层控件的onTouchEvent进行处理
return false:表示不对事件进行拦截,事件得以成功分发到子View
return super.onInterceptTouchEvent(ev):默认表示不拦截该事件,并将事件传递给下一层View的dispatchTouchEvent -
onTouchEvent
return true:表示onTouchEvent处理完事件后消费了此次事件
return fasle:表示不响应事件,那么该事件将会不断向上层View的onTouchEvent方法传递,直到某个View的onTouchEvent方法返回true
return super.dispatchTouchEvent(ev):表示不响应事件,结果与return false一样
3,Android View的绘制流程(重要):
【转发】
Android View的绘制流程
View的绘制是从ViewRoot的performTraversals()方法开始的,经过measure,layout,draw这个3大步骤。
-
measure的过程:
measure过程是对整个view树的所有控件计算宽高
measure是冲ViewRoot类中的host.measure开始的,内部调用的是View的measure(int widthMeasureSpec,int heightMeasureSpec)方法,measure方法里面调用了onMeasure(int widthMeasureSpec,int heightMeasureSpec)方法,方法中的两个参数都是是MeasureSpec类型(指父控件对子控件宽高的期望值,它是一个32位的int类型数,前两位表示测量模式,后30位表示测量大小)
测量模式一共有3种:
1)EXACTLY 精确测量模式,xml文件中写200dp,march_parent等代表使用该模式,
2)AT_MOST 最大模式,xml文件中写wrap_content表示使用该模式。
3)UNSPECIFIED 无限大测量模式,只有在绘制特定自定义View时才用的到这个模式。
真正代表测量结束的方法是setMeasuredDimension方法,该方法传入的两个参数是宽高的SpecSize。测量结束后我们可以通过getMeasureHeight和getMeasureWidth来获取测量宽高。
自定义ViewGroup一定要重写onMeasure方法,用于测量子View的宽高,不重写的话子View没有宽高。
自定义View如果在xml中使用了wrap_content属性,就需要重写onMeasure方法来设置wrap_content的默认大小,不然会显示出match_parent的效果。 -
layout的过程:
ViewGroup用来将子View放在合适的位置上。
layout是从ViewRoot类中的host.layout开始的,内部调用的是ViewGroup的layout方法。在ViewGroup的layout方法中,先调用setFrame来确定自己的左上右下的位置,再调用onLayout来确定子View的位置。
自定义ViewGroup一定要重写layout方法来确定子View的位置,自定义View一般不需要重写该方法,它的位置是右父控件确定的。 -
draw过程:
此过程是真正将内容展示在屏幕上让我们能够看到的过程。
draw是从ViewRoot类中的host.draw开始的,内部调用的是View的draw方法。
draw的步骤:
1)绘制背景。
2)绘制内容,也就是调用onDraw方法。
3)绘制子View,调用的是dispatchDraw方法。
4)绘制装饰,如listview的滚动条等。
对于View的绘制过程,既可以说是简单的,也可以说是复杂的,简单的在于Google已经帮我们将draw框架写好了,我们在自定义ViewGroup时不用管draw过程,只需要实现measure和layout过程。复杂在于,我们写继承View的自定义控件的时候需要重写onDraw方法,这样才能绘制出你自定义的View的内容,onDraw(Canvas canvas)方法中最重要的两个东西是Paint和Canvas,这个使用起来算是比较复杂的。
4,Android 消息处理机制(重要):
消息处理机制【转发】
Android 消息处理机制
5,四大组件及生命周期(重要):
Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器。
Activity Service【转发】
Android四大基本组件介绍与生命周期
6,Service 的一些细节:
【转发】
Service 的一些细节
7,什么是AIDL 以及如何使用?(例子,了解)
必要了解的概念
a.IPC
IPC是Inner-Process Communication,就是进程间通信。
b.AIDL
AIDL是Android Interface Define Language 安卓接口语言缩写。
c.Binder
Binder是android中负责进程间通信的驱动类,Binder内部设计十分复杂这里我们暂不做深入研究,这里我们只需要了解它是负责进程间通信的类即可。
【转发】
AIDL使用以及原理分析
8,Serializable 和Parcelable 的区别:
android上应该尽量采用Parcelable,效率至上
编码上:
Serializable代码量少,写起来方便
Parcelable代码多一些
效率上:
Parcelable的速度比Serializable 高十倍以上
serializable的迷人之处在于你只需要对某个类以及它的属性实现Serializable 接口即可。Serializable 接口是一种标识接口(marker interface),这意味着无需实现方法,Java便会对这个对象进行高效的序列化操作。
这种方法的缺点是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。
Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了
【转发】
序列化Serializable和Parcelable的理解和区别
9,保证Android后台不被杀死的几种方法:
【转发】
保证Android后台不被杀死的几种方法
10,Service 的生命周期(重要):
【转发】
Service生命周期最全面解析
11,一张Bitmap所占内存以及内存占用的计算:
计算【转发】
计算bitmap占用内存大小
12,什么情况下会导致内存泄露(重要)?
- Bitmap泄露;
- IO流未关闭;
- 单例模式泄露;
- 静态变量持有Context实例泄露;
- 服务未解绑注册泄露;
- Handler泄露;
- 异步线程泄露;
【转发】
Android 内存泄露实践分析
13,Looper、Handler、MessageQueue,Message作用和存在的意义:
Looper
我们知道一个线程是一段可执行的代码,当可执行代码执行完成后,线程生命周期便会终止,线程就会退出,那么做为App的主线程,如果代码段执行完了会怎样?,那么就会出现App启动后执行一段代码后就自动退出了,这是很不合理的。所以为了防止代码段被执行完,只能在代码中插入一个死循环,那么代码就不会被执行完,然后自动退出,怎么在在代码中插入一个死循环呢?那么Looper出现了,在主线程中调用Looper.prepare()...Looper.loop()就会变当前线程变成Looper线程(可以先简单理解:无限循环不退出的线程),Looper.loop()方法里面有一段死循环的代码,所以主线程会进入while(true){...}的代码段跳不出来,但是主线程也不能什么都不做吧?其实所有做的事情都在while(true){...}里面做了,主线程会在死循环中不断等其他线程给它发消息(消息包括:Activity启动,生命周期,更新UI,控件事件等),一有消息就根据消息做相应的处理,Looper的另外一部分工作就是在循环代码中会不断从消息队列挨个拿出消息给主线程处理。
MessageQueue
MessageQueue 存在的原因很简单,就是同一线程在同一时间只能处理一个消息,同一线程代码执行是不具有并发性,所以需要队列来保存消息和安排每个消息的处理顺序。多个其他线程往UI线程发送消息,UI线程必须把这些消息保持到一个列表(它同一时间不能处理那么多任务),然后挨个拿出来处理,这种设计很简单,我们平时写代码其实也经常这么做。每一个Looper线程都会维护这样一个队列,而且仅此一个,这个队列的消息只能由该线程处理。
Handler
简单说Handler用于同一个进程的线程间通信。Looper让主线程无限循环地从自己的MessageQueue拿出消息处理,既然这样我们就知道处理消息肯定是在主线程中处理的,那么怎样在其他的线程往主线程的队列里放入消息呢?其实很简单,我们知道在同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的,只要考虑并发性做好同步就行了,那么只要拿到MessageQueue 的实例,就可以往主线程的MessageQueue放入消息,主线程在轮询的时候就会在主线程**处理这个消息。那么怎么拿到主线程 MessageQueue的实例,是可以拿到的(在主线程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 为了统一添加消息和消息的回调处理,又专门构建了Handler类,你只要在主线程构建Handler类,那么这个Handler实例就获取主线程MessageQueue实例的引用(获取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的时候就通过这个引用往消息队列里插入新消息。Handler 的另外一个作用,就是能统一处理消息的回调。这样一个Handler发出消息又确保消息处理也是自己来做,这样的设计非常的赞。具体做法就是在队列里面的Message持有Handler的引用(哪个handler 把它放到队列里,message就持有了这个handler的引用),然后等到主线程轮询到这个message的时候,就来回调我们经常重写的Handler的handleMessage(Message msg)方法。
Message
Message 很简单了,你想让主线程做什么事,总要告诉它吧,总要传递点数据给它吧,Message就是这个载体。
14,synchronized同步时,锁的位置:
- 对于普通的方法同步,锁是当前实例对象。
- 对于静态方法同步,锁是当前类的class对象。
- 对于方法块同步,锁是synchronized括号里的对象。
15,ThreadLocal详解(不了解使用场景):
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用。作用:提供一个线程内公共变量(比如本次请求的用户信息),减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
【转发】
【Java 并发】详解 ThreadLocal 有自定义的例子