1,Activity 生命周期
onCreate() 创建
onRestart() 重新启动
onStart()启动
onResume()可见
onPause()正在停止,不能交互
onStop()不可见
onDestroy() 即将销毁。
第一次启动: onCreate() > onStart> onResume();
A activity 到B activity A_onPause()>B_onCreate()>B_onStart()>B_onResume()>A_onStop()
B activity 返回A activity B_onPause >A_onRestart()> A_onStart()> A_onResume()>B_onStop()>B_onDestroy()
显示和隐藏标准的 AlertDialog 不会对 MainActivity 的生命周期有任何的影响。
显示和隐藏全屏的 AlertDialog 不会对 MainActivity 的生命周期有任何的影响。
主题为 Dialog 的 Activity 会对 MainActivity 的生命周期有影响,并且跳转主题为 Dialog 的 Activity 与跳转普通的 Activity 的生命周期变化相同。
横竖屏切换时的生命周期调用为:onPause() -> onStop() -> onDestory() -> onCreate() -> onStart() -> onResume() 。就是一个销毁再重建的过程。
将 MainActivity 的 android:configChanges 设置为 orientation【o:rui:ten:tion】 ,之后切换横竖屏并不会有任何的生命周期方法的调用
内存不足时,杀死应用,前台的 Activity 生命周期为 onPause() -> onStop() 并没有调用 onDestory() 方法,所以主进程现在属于后台进程。
fragment被创建的时候,经历包含onAttach、onCreate、onCreateView、onActivityCreated方法;fragment对用户可见的时候,经历包含onStart、onResume方法;fragment进入“后台模式”的时候,经历onPause、onStop方法;fragment被销毁了(或者持有它的activity被销毁了),经历包含onPause、onStop、onDestroyView、onDestroy、onDetach方法;并且可用onCreate、onCreateView、onActivityCreated方法Bundle对象保存一个fragment的对象。
onAttach:当Fragment与Activity发生关联时调用
onCreate:创建Fragment时被回调,经历暂停或停止状态继而恢复后,想保留Fragment的基本组件,则在此进行初始化。
onCreateView:首次绘制页面时候调用,在此可以创建View,也可以返回null,这样不建议耗时操作。
onActivityCreated:Fragment绑定Activity,在onCreate方法已经执行完成并返回,在该方法内可以进行与Activity交互的UI操作,不能在此之前跟Activity进行交互。
onStart:启动 Fragment 时被回调,此时Fragment可见,只是还没有在前台显示,因此无法与用户进行交互
onResume:Fragment在前台可见,处于活动状态,用户可与之交互
onPause:Fragment处于暂停状态,但依然可见,用户不能与之交互
onStop:停止Fragment回调,Fragment完全不可见
onDestoryView:销毁与Fragment有关的视图,但未与Activity解除绑定
onDestory:销毁 Fragment 时被回调,通常按Back键退出或者Fragment被回收时调用此方法,此后接onDetach
onDetach:与onAttach相对应,当Fragment与Activity关联被取消时调用
setUserVisibleHint:调用次方法可以设置Fragment可见或者不可见。可以调用getUserVisibleHint()获得Fragment的可见或不可见状态,如果可见则进行懒加载操作
在异常情况下,系统默认恢复 TextView 的文本信息
在切换横竖屏时,onPause() 方法中打印了 TextView 的文本内容,切换重建后,在 onResume() 中获取到 TextView 的内容与之前的内容相同,并且要注意,重建后 onCreate() 与 onStart() 方法中获取 TextView 的文本内容是布局文件中的默认内容。
2, Activity 启动模式
standard 标准模式 每启动一个都会创建一个实例
singleTop 栈顶复用 如果在栈顶就调用onNewintent ,从onResume()开始
singleTask 栈内复用 栈内有,就复用,并将其上的Activity 移出
singleInstance 单例模式 系统会给该Activity 创建一个栈
3 .Service
onCreat()创建服务
onStartCommand()开始服务
onDestroy()销毁服务
onBind()绑定服务
onUnbind()解绑服务
1)启动Service服务
单次:startService() —> onCreate() —> onStartCommand()
多次:startService() —> onCreate() —> onStartCommand() —> onStartCommand()
2)停止Service服务
stopService() —> onDestroy()
3)绑定Service服务
bindService() —> onCreate() —> onBind()
4)解绑Service服务
unbindService() —> onUnbind() —> onDestroy()
5)启动绑定Service服务
startService() —> onCreate() —> onStartCommand() —> bindService() —> onBind()
6)解绑停止Service服务
unbindService() —> onUnbind() —> stopService() —> onDestroy()
7)解绑绑定Service服务
unbindService() —> onUnbind(ture) —> bindService() —> onRebind()
8 ,stopSelf()也可以停止服务
。如果是多个组件绑定到一个服务上,当绑定到该服务的所有组件都被销毁时,服务才会停止。
4,什么时候用Service
service是一个应用组件,长时间运行在后台,不需要直接跟用户交互。
Service不是运行在独立的线程,所以不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。
1) 默认情况下,Service其实是运行在主线程中的,如果需要执行复杂耗时的操作,必须在Service中再创建一个Thread来执行任务。
(2) Service的优先级高于后台挂起的Activity,当然,也高于Activity所创建的Thread,因此,系统可能在内存不足的时候优先杀死后台的Activity或者Thread,而不会轻易杀死Service组件,即使被迫杀死Service,也会在资源可用时重启被杀死的Service
在Service中创建的Thread,适合长期执行一些独立于APP的后台任务,比较常见的就是:在Service中保持与服务器端的长连接
5,intentService
Service不是运行在独立的线程,所以不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。
1,可用于执行后台耗时的任务,任务执行后会自动停止。
2,具有高优先级,适合高优先级的后台任务,且不容易被系统杀死。
3,可以多次启动,每个耗时操作都会以工作队列的方式在IntentService的onHandleIntent回调方法中执行
内部其实就是 HandlerThread + Handler , Handler发消息调用onHandleIntent()
执行完任务会 执行 stopSelf(), 自己会自杀,
优先级高, 设为0 ,为高优先级
6,Android 里的多线程
多线程的应用在Android开发中是非常常见的,常用方法主要有:
继承Thread类
实现Runnable接口
Handler
AsyncTask
HandlerThread
HandlerThread的本质:继承Thread类 & 封装Handler类
// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
/ 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
由run方法可知HandlerThrea线程运行创建了Looper实例,并开启了Looper循环,循环从消息队列中获取消息并给Handler进行处理。
注:子线程 用Handler 用法
7,Looper
1,主线程(UI线程)
UI线程中Looper已经都创建好了,不用我们去创建和循环。
2,普通线程
普通线程中使用Looper需要我们自己去prepare()、loop()。
看一下普通线程中创建使用Looper的方式,代码如下:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
印象中在UI线程没有出现过Looper相关的东东,这是因为UI线程中会自动创建Looper对象并进行消息循环,我们不再需要调用Looper.prepare()和Looper.loop(),但是在子线程中如果想要创建使用Handelr则需要向如上所示。
我们通过源码看一下Looper实例创建的方法:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
sThreadLocal为ThreadLocal类型变量,用来存储线程中的Looper对象。prepare方法中首先判断sThreadLocal是否存储对象,如果存储了则抛出异常,这是因为在同一个线程中Loop.prepare()方法不能调用两次,也就是同一个线程中最多有一个Looper实例
接着看Looper的构造器:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
在构造器中,创建了一个MessageQueue消息队列;然后获取当前的线程,使Looper实例与线程绑定。由prepare方法可知一个线程只会有一个Looper实例,所以一个Looper实例也只有一个MessageQueue实例。但这并不代表一个线程只能有一个MessageQueue实例,这是为什么呢?很简单,我们可以自己new 一个MessageQueue实例就可以了,但这个MessageQueue并不是该线程中Handelr对应的消息队列。
总结:
UI线程会自动创建Looper实例、并且调用loop()方法,不需要我们再调用prepare()和loop().
Looper与创建它的线程绑定,确保一个线程最多有一个Looper实例,同时一个Looper实例只有一个MessageQueue实例。
loop()函数循环从MessageQueue中获取消息,并将消息交给消息的target的dispatchMessage去处理。如果MessageQueue中没有消息则获取消息可能会阻塞。
通过调用Looper的quit或quitsafely终止消息循环。
相同点:
将不在接受新的事件加入消息队列。
不同点
当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
6, Parcelable 与 Serializable 区别
两者最大的区别在于存储媒介的不同,Serializable使用IO读写存储在硬盘上,而Parcelable是直接在内存中读写,很明显内存的读写速度通常大于IO读写,
android上应该尽量采用Parcelable,效率至上
编码上:
Serializable代码量少,写起来方便
Parcelable代码多一些
效率上:
Parcelable的速度比高十倍以上
serializable的迷人之处在于你只需要对某个类以及它的属性实现Serializable 接口即可。Serializable 接口是一种标识接口(marker interface),这意味着无需实现方法,Java便会对这个对象进行高效的序列化操作。
这种方法的缺点是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。
Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了
7内存抖动;
避免在ondraw()创建对象,因为你会频繁创建只使用一次的对象,就会导致内存的迅速攀升,就很可能触犯GC的回收,
这种在短时间内反复发生内存增长和回收的循环。有界面卡顿的风险。Android 官方叫Memory churn
在onDraw()里创建对象往往跟绘制相关,这些对象又经常包含通往系统下层Native的对象引用,这就导致回收耗时会更高。
8 MVC、MVP 和 MVVM 区别
1.1 MVC
1.1.1 MVC三个字母的含义
View :对应的XML文件
Model:实体类javabean
Controllor: 对应Activity或者Fragment
1.1.3 优缺点(以android的角度讲)
优点
Xml就是View层,与java逻辑代码解耦,具有一定的解耦
缺点
没有固定的model层,Activity中代表Controllor,网络接口的model也在Activity中,一个逻辑稍微复杂的页面代码行数都上千行,影响阅读,Activity既然控制着View,又含有Model。相当耦合
2.2 MVP
2.2.1 MVP三个字母的含义
View 对应于Activity和Xml,负责页面的展示
Model 实体类javabean
Presenter 控制器 负责完成View于Model间的交互与逻辑处理(网络和逻辑)
2.2.3 优缺点
优点:
Model和View之间解耦,两者交互由Presenter完成,把部分的逻辑的代码从Fragment和Activity业务的逻辑移出来
Activity或者Fragment只是用来展示控件,或者控制控件的显示和隐藏,以及View的变化
在MVP中View和presenter要相互持有,方便调用对方
缺点:
随着业务的增加,IView的接口和IPresenter的接口会越来越多
更换Xml里面的控件会引起IView接口和IPressenter接口的改动
2.3 MVVM(只从android考虑)
2.3.1 MVVM三个字母的含义
View 还是Activity或者fragment,也就是Xml,负责页面的展示,或者控制控件的显示和隐藏,以及View的变化,不参与任何逻辑和数据的处理
Viewmodel 主要负责业务逻辑和数据处理,本身不持有 View 层引用,通过LiveData(如果项目中有Rxjava可以不引用LiveData) 向 View 层发送数据,通过DataBinding更改View中的UI层
Model:实体类javabean,便是指这里的Repository ,主要负责从本地数据库或者远程服务器来获取数据,Repository统一了数据的入口,获取到数据,将数据发送
2.3.3 优缺点
优点 :
View和数据双向绑定,当model变化时会自动同步给View
crash会降低,xml里面自己会有判断
View(Activity或者Fragment)层就是View层,不处理任何关于数据的逻辑
缺点 :
Xml里面写代码,对于android有点不习惯,并且会使xml臃肿
无法快速定位crash位置,Debug比较困难
双向绑定不利于View的复用,因为每个xml里面都有一个model,但是每个页面的mode有可能不一样
xml里面写java代码,不能用kotlin的简单语言
UI线程是不安全
悲观锁:
Binder 是基于文件的通信方式
RxJava
dispatch Touch Event 事件分发
On Intercept touch Event 是否拦截
On Touch Event 消耗
ViewGroup和View 的dispatchTouchEvent 是做事件分发,那么这个事件可能分发出去的四个目标
注:------> 后面代表事件目标需要怎么做。
1、 自己消费,终结传递。------->return true ;
2、 给自己的onTouchEvent处理-------> 调用super.dispatchTouchEvent()系统默认会去调用 onInterceptTouchEvent,在onInterceptTouchEvent return true就会去把事件分给自己的onTouchEvent处理。
3、 传给子View------>调用super.dispatchTouchEvent()默认实现会去调用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就会把事件传给子类。
4、 不传给子View,事件终止往下传递,事件开始回溯,从父View的onTouchEvent开始事件从下到上回归执行每个控件的onTouchEvent------->return false;
注: 由于View没有子View所以不需要onInterceptTouchEvent 来控件是否把事件传递给子View还是拦截,所以View的事件分发调用super.dispatchTouchEvent()的时候默认把事件传给自己的onTouchEvent处理(相当于拦截),对比ViewGroup的dispatchTouchEvent 事件分发,View的事件分发没有上面提到的4个目标的第3点。
ViewGroup和View的onTouchEvent方法是做事件处理的,那么这个事件只能有两个处理方式:
1、自己消费掉,事件终结,不再传给谁----->return true;
2、继续从下往上传,不消费事件,让父View也能收到到这个事件----->return false;View的默认实现是不消费的。所以super==false。
ViewGroup的onInterceptTouchEvent方法对于事件有两种情况:
1、拦截下来,给自己的onTouchEvent处理--->return true;
2、不拦截,把事件往下传给子View---->return false,ViewGroup默认是不拦截的,所以super==false;
网友评论