View 的 requestLayout 会调到ViewRootImpl 的 requestLayout方法,然后通过 scheduleTraversals 方法向Choreographer 提交一个绘制任务,然后再通过DisplayEventReceiver向底层请求vsync信号,当vsync信号来的时候,会通过JNI回调回来,通过Handler往主线程消息队列post一个异步任务,最终是ViewRootImpl去执行那个绘制任务,调用performTraversals方法,里面是View的三个方法的回调。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LlcEezYh-1579358399384)(./1571622381383.png)]
那么有两个地方会造成掉帧,一个是主线程有其它耗时操作,导致doFrame没有机会在vsync信号发出之后16毫秒内调用,对应下图的3;还有一个就是当前doFrame方法耗时,绘制太久,下一个vsync信号来的时候这一帧还没画完,造成掉帧,对应下图的2。1是正常的
public void test(){
long startTime = System.currentTimeMillis();
doSomething();
long methodTime = System.currentTimeMillis() - startTime;//计算方法耗时
}
##消息机制
一个线程绑定一个Looper,一个Looper维护一个MessageQueue队列,而一个线程可以对应多个Handler
创建Handler的时候需要一个对应的Looper,所以在子线程中创建需要手动调用Looper.prepare(),而主线程因为在ActivityThread的main()中已经调用过了,所以不需要我们手动调用,而looper是通过sThreadLocal.get()返回的,所以每一个线程最多会维护一个Looper,Handler通过sendMessage发送了一个Message到MessageQueue中,而Looper在构造方法里面会生成一个MessageQueue–一个单向链表的消息队列,而通过Looper.loop(),进行了一个死循环的取Message操作,如果有message就取出去,如果没有就阻塞着,因为用到了管道模式(pipe)所以死循环也不会造成anr(因为阻塞着)。
Messagequeue的数据结构是单向链表。
标准做法是应该
Looper.prepare();
create ..
Looper.loop();
Handler的post方法,post了一个Runnable对象,之后通过
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
把Runnaable包装成一个Message(塞进它的callback里面)返回,
而在Handler的**dispatchMessage(Message msg)**方法里做了对应处理,如果msg的callback不等于null,就调用handleCallback(msg),在方法里面直接run了callBack。同理还有View.Post()以及Activity.runOnUIThread()
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
打开一个新的栈里面打开一个Activity,常用于在service中启用Activity等
就是指栈里只能有一个实例,当创建activity的时候,如果栈里已经存在实例了,那么它就会把他上面的activity全部都出栈,自己回到栈顶
与singleTask类似,但是只有目标activity也位于栈顶的时候才会;否则就是正常的创建
默认的创建方式,create几个就生成几个
只有当singleTask和singleTop成功生效的时候,即栈内有,且存在于栈顶(单指singleTop)的时候,这个时候系统不会重新创建,即不会走oncreate(),onresume()onstart(),指挥回调onNewIntent()。
系统配置,常用的有配置页面旋转的时候不重新创建等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NsBAsKVw-1579358399386)(./20180724151603814 1.png)]
Activity的生命周期:
onCreate()->onStart()->onResume()->onPause()->onStop()->onDestroy()
Fragment的生命周期:
onAttach()->onCreate()->onCreateView()->onViewCreate()->onStart()->onResume()->onPause()->onStop()-onDestroyView()->onDestroy()->onDetach()
比如一个fragment加载在Activity里面,我们可以这么认为:创建时是activity的生命周期带动fragment的生命周期,
而销毁时是fragment的生命周期带动activity的生命周期,(Ps:这里的带动是指记忆,怎么记忆的带动,单指他们两同生共死的时候):
####创建时:
在Fragment中存在startActivityForResult()以及onActivityResult()方法,需要通过调用getActivity().setResult(、Fragment.REQUEST_CODE, intent)来设置返回。
这里2和3的前提是如果父activity中重写了onActivityResult,父Activity的onActivityResult中必须添加super.onActivityResult()
###Fragment重叠问题
当屏幕旋转或者内存重启(Fragment以及容器activity被系统回收后再打开时重新初始化)会导致Fragment重叠问题,是因为activity本身重启的时候会恢复Fragment,然后创建Fragment的代码又会新建一个Fragment的原因。
解决方法:在onCreate方法中判断参数Bundle savedInstanceState,为空时初始化Fragment实例,然后在Fragment中通过onSaveInstanceState的方法恢复数据
类型 | 层次 | 继承 | 方法/属性 |
---|---|---|---|
抽象类 | 对一整个类进行抽象 | 单继承 | 可以抽象,也可以不 |
接口 | 对类的某个行为进行抽象 | 子类可以实现多个接口,接口可以继承多个接口 | 必须是抽象 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQ2APO61-1579358399387)(./6332336-66a71231ab40cedf.png)]
事件分发主要涉及三个方法:
###源码讲解
--------------------------------------------------------
--------------Activity的事件分发------------------------
// 事件产生后调用入口
Activiry.dispatchTouchEvent()
--------------------------------------------------------
--------------以下为ViewGroup的事件分发------------------
//事件交给ViewGroup去处理,返回true说明事件被消费,无需执行Activity.onTouchEvent()
if(ViewGroup.dispatchTouchEvent()) {return true}
//disallowIntercept代表是否禁用拦截功能,默认是false,可通过调用requestDisallowInterceptTouchEvent()修改。
//onInterceptTouchEvent(ev)代表拦截器, !onInterceptTouchEvent(ev)值为true代表不拦截,false代表拦截。
//onInterceptTouchEvent()默认返回false,可通过复写该方法修改返回值。
if(disallowIntercept || !onInterceptTouchEvent(ev))
//当前ViewGroup没有拦截当前事件
//循环遍历当前ViewGroup的所有子View,通过x,y坐标找到被点击的View
//调用子View的dispatchTouchEvent()并依赖返回值返回ViewGroup.dispatchTouchEvent()方法
if (child.dispatchTouchEvent(ev)){ return true }
--------------------------------------------------------
--------------以下为View的事件分发------------------
//mOnTouchListener即为当前View是否setOnTouchListener()。
//(mViewFlags & ENABLED_MASK) == ENABLED 当前控件是否启用。
//mOnTouchListener.onTouch()值为复写的onTouch方法返回值是否为true。
//以上条件全部成立则返回true代表事件已消费
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)){
return true
}
//以上条件有一个不成立则调用View.onTouchEvent()
return View.onTouchEvent();
//如果当前View可点击(CLICKABLE或LONG_CLICKABLE状态),则进入switch。
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case ACTION_UP:
//如果当前为抬起事件则调用performClick()
performClick();
//如果mOnClickListener != null,mOnClickListener值通过View.setOnClickListener()赋值。
//由此可见onClick事件是通过ACTION_UP触发的,但是在此触发之前,onTouch事件已经触发,因此onTouch早于onClick。
if (mOnClickListener != null) {
mOnClickListener.onClick(this);
//返回到View的dispatchTouchEvent()方法中。
return true;
}
case ACTION_DOWN:
case ACTION_CANCEL:
case ACTION_MOVE:
}
//只要当前View是可点击的,则返回true;
return true;
}
// 若该控件不可点击,就一定返回false
return false;
//如果拦截了该事件或者用户点击到了空白处(未点到控件),则调用ViewGroup父类的的dispatchTouchEvent(),即View.dispatchTouchEvent()。
return super.dispatchTouchEvent(ev);
//没有控件处理,Activity自己处理
Activity.onTouchEvent()
//主要处理当前点击是否在边界外,true说明事件在边界外,即 消费事件。false则说明未消费。
PhoneWindow.shouldCloseOnTouch()?return ture:false
玉刚说
模块指的是独立的业务模块,比如说“首页模块”,“个人中心模块”,一个模块里面可以包含多个组件。
组件指的是单一的功能组件,如 [视频组件]、[支付组件] 等,每个组件都可以以一个单独的 module 开发,并且可以单独抽出来作为 SDK 对外发布使用。
1. 继承 thread
2. 实现Runnable接口
3. 实现Callable接口
java是单继承多实现的语言,如果选择了继承,那么只能继承Thread,而选择实现的话,就相对灵活很多
Runnable实现的是run方法,而Callable实现的是call方法,
call() 方法有返回值还能抛出异常, run() 方法则没有没有返回值,也不能抛出异常。
线程池可以理解为 管理多个线程,通过线程复用,减少创建与销毁操作,控制线程池中的线程的并发数,避免线程争夺CPU资源造成阻塞,对线程进行管理。
简而言之,线程池的好处是:
1. 减少创建与销毁线程带来的性能消耗
2. 可控制最大并发线程数,避免过多资源竞争而导致系统内存消耗
3. 能更好地控制线程的开启与回收,并且能定时执行任务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nZZjgMv0-1579358399387)(./1577171161413.png)]
1. corePoolSize:核心线程数,如果运行的线程数少于corePoolSize,当有新的任务过来时会创建新的线程来执行这个任务,即使线程池中有其他空闲的线程
2. maximumPoolSize:线程池中允许的最大线程数
3. keepAliveTime:如果线程数多于核心线程数,那么这些多出来的线程如果空闲时间超过keepAliveTime将会被终止。
4. unit:keepAliveTime参数的时间单位。
5. workQueue:任务队列,通过线程池的execute方法会将任务Runnable存储在队列中。
6. threadFactory:线程工厂,用来创建新线程。
7. handler:添加任务出错时的策略捕获器,默认是ThreadPoolExecutor.AbortPolicy ,即添加任务出错就直接抛出异常 。
/// 创建一个并发数为3的线程池,即最大只有3个线程,当有的线程结束了,会创建出新的线程来进行补充
val executorService1 = Executors.newFixedThreadPool(3)
/// 创建一个单线程的线程池,即里面只有一个线程
val executorService2 = Executors.newSingleThreadExecutor()
/// 创建带有缓存的线程池,在执行任务中,如果线程池里面有可用的线程,就拿过来用,如果空闲了60s,那么空闲线程就会被移除
val executorService3 = Executors.newCachedThreadPool()
/// 创建定时和周期性的线程池
val executorService4 = Executors.newSingleThreadScheduledExecutor()
Synchronized是java的一个关键字,用于保证被Synchronized修饰的方法&代码,同一时刻最多只能被一个线程操作,其他线程必须等待该线程执行完后才能操作,从而达到保证线程安全,解决多线程中的并发同步问题(阻塞型并发)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zMZkkf8-1579358399388)(./1577174235970.png)]
/**
* 对象锁
*/
public class Test{
// 对象锁:形式1(方法锁)
public synchronized void Method1(){
System.out.println("我是对象锁也是方法锁");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
// 对象锁:形式2(代码块形式)
public void Method2(){
synchronized (this){
System.out.println("我是对象锁");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
/**
* 方法锁(即对象锁中的形式1)
*/
public synchronized void Method1(){
System.out.println("我是对象锁也是方法锁");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
/**
* 类锁
*/
public class Test{
// 类锁:形式1 :锁静态方法
public static synchronized void Method1(){
System.out.println("我是类锁一号");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
// 类锁:形式2 :锁静态代码块
public void Method2(){
synchronized (Test.class){
System.out.println("我是类锁二号");
try{
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
1. 修饰 实例方法 / 代码块时,(同步)保护的是同一个对象方法的调用 & 当前实例对象
2. 修饰 静态方法 / 代码块时,(同步)保护的是 静态方法的调用 & class 类对象
1. 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
2. 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)
3. 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
4. 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
1. 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)
2. 大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存)
适用于需要高性能加载大量图片的场景
特点 | 具体描述 | 相对于listView |
---|---|---|
1. View组件复用性高 | 缓存RecyclerView.ViewHolder(Adapter必须遵循ViewHolder的设计模式 ,强制实现ViewHolder,优化了性能); |
2.四层缓存,可以支持自定义缓存逻辑,所有recyclerView公用一个缓存池
| 1. ListView缓存了View,需要实现getView(),实现复用的性能优化
2. 两层缓存,快速重用屏幕上的列表项itemView,而不需要重新createView和bindView;缓存离开屏幕的itemView,可以让即将进入屏幕的item复用|
| 样丰富 | 1,通过layoutManager可以实现不同的风格;
2, 可以快速实现增删动画和item分割线| 1,只能实现线性平铺布局,2,只提供了divider属性提供分割线 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mlvhy3g3-1579358399388)(./1578550885902.png)]
Socket={(ip地址1:PORT端口号),(ip地址2:PORT端口号)}
* Socket属于传输层,因为tcp/ip属于传输层,解决的是数据如何在网络中传输中的问题
* Http协议属于应用层,解决的是如何包装数据
* 由于二者属于不同层面,所以本来是没有可比性的,但是随着发展,http封装了下面几层的使用,所有才有了对比: