校验重复方法签名
该方法取自EventBus3.0,原理是通过HashMap的put方法,在相同key的情况下,会返回旧的数据;
1 | private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) { |
该方法取自EventBus3.0,原理是通过HashMap的put方法,在相同key的情况下,会返回旧的数据;
1 | private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) { |
EventBus是一个使用”观察者模式”的、松耦合的开源框架,能够帮助我们简化代码、松依赖、加速开发。
EventBus基于观察者模式中的发布-订阅(Publish-Subscribe)模式(事件总线),与传统的观察者模式
不同,在传统的观察者模式下,我们更多是处于订阅者的身份,数据的发布大多数情况下我们是不可控的,我
们一般只能触发数据开始发送,而EventBus则将发布(post)和订阅的能力都交由我们自己处理;
当然,EventBus同样拥有观察者模式的优点,就是发布者和订阅者是解耦的。
1、订阅者注册时,会用两个HashMap来保存其信息,分别是根据事件类型(key)保存订阅信息
(订阅者 - 订阅方法)的subscriptionsByEventType和根据订阅者(key)保存其对应的所有订阅事件的
typesBySubscriber;
Kotlin中的接口是可以定义变量和方法体,但变量不可以初始化,也就是不能有状态,只能有定义(Java
中的变量可以被初始化),在被继承时必须初始化接口变量,赋予状态,而方法表示的是实现过程,本身就没
有状态(Java8开始,也支持定义static和default类型的方法,这些方法是可以有方法体的)。
为什么接口中的变量不能被初始化?
因为接口中的变量没有backing field,也就无法存储变量,所以只能做为一种声明;
backing field(类中定义的变量自带,用来存储变量内容,可以理解为堆栈) + get、set 方法 等价于
Kotlin类中的属性Property,这也是Kotlin中独有的;
在Kotlin中只需声明一个变量,就相当于属性,backing field + get、set 方法由Kotlin自动生成;
在Java中,这些都必须自己手动声明;
当一个类继承了多个接口,这些接口出现了两个完全一致的方法声明,必须通过super进行显式指定调用方;
1 | interface n1{ |
SAM接口又称为函数式接口(Single Abstract Method interfaces),这种接口可以简单的理解为:只有
一个抽象方法的接口,这种接口可以通过lambda实现简化,在Java8和Kotlin中都有应用到。
比如View的OnclickListener接口就是一个典型的SAM接口,Kotlin中的函数类型也可以理解为SAM接口
的另一种形式(Kotlin不需要定义SAM接口,有函数类型就够了,对SAM接口的简化只是为了兼容Java)。
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制。
栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)
或者常量池中(字符串常量对象存放在常量池中)。
堆:存放所有new出来的对象。
静态域:存放静态成员(static定义的)。
常量池:存放字符串常量和基本类型常量(public static final)。
非RAM存储:硬盘等永久存储空间。
堆栈:
- 对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享;
- 栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃
圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性;
字符串:
- 对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在
常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永
远只有一份,在堆中有多份;- 对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如
果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也
就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有
”xyz”,就是两个。
Java不支持多继承 ,只支持多实现。但是,很多时候,只是实现一些接口很难解决问题,此时,我们就可以
使用内部类来解决问题。内部类即可与外部类关联(非静态内部类),也可以独立于外部类(静态内部类),并且,
内部类的环境是不受到外部类的影响的,通过它们来继承我们需要的类的能力,能让我们更灵活的解决问题;
可以说,接口只是帮我们解决了部分问题,而内部类赋予类多重继承的能力,使解决问题的方案更加的完整;
MVC和MVP最主要的区别在于M层和V层的沟通方式,MVC模式下,M、V两层能够直接进行沟通,而MVP模式下,
M、V两层只能通过P层进行沟通;
MVC:
传统的MVC模式下,V层通过C从M层中获取到数据,然后展示在V层上;
但是,大多数情况下,在拿到数据后我们需要对数据进行加工,此时不得不将逻辑编写在V层上,久而久之,V层就会变得臃肿;
还有一种情况,当我们操作了V层,通过C修改了M层,当M层在修改后,为了通知V层,M层不得不持有V层引用,因为C是单向的,M层无法通过C通知V层;
MVP:
MVP中的Presenter即能够解决V层业务代码臃肿问题,因为P中持有了V层的引用,直接在P中操作业务逻辑,然后将结果通知给V层(被动P模式,View元素操作完全由P进行。另一种主动P模式,在V层的数据操作还是在V层中进行,P层更多是起到沟通M层和V层数据的作用,这种模式下,V的臃肿并没有解决!);
MVP中的Presenter也解决了M层和V层数据沟通的问题,它双向持有了两层的引用,能直接操作两层数据进行交换和传输;
Handler在创建时,就会在构造方法中拿到当前线程的Looper,而Looper保存有当前线程的消息队列,也
会和Handler绑定到一起;
Handler在哪个线程被创建,就会绑定哪个线程中的Looper;
主线程默认会创建一个MainLooper,而如果在子线程中创建Handler,就需要我们手动为其创建一个
Looper,并绑定到Handler中;
查看ActivityThread类,在main方法中调用了Looper的prepareMainLooper方法来初始化
MainLooper,可以获取它来判断当前的操作是否位于主线程。
使用Looper.prepare()即可为当前线程创建一个Looper,每一个Looper中都自带一个消息队列;
从Activity启动声明周期来看,即使是在onPostResume时也是拿不到宽高的!
因为和Activity中View的布局、测量、绘制相关的类ViewRootImpl在onPostResume时还没有创建,只有
在之后DecorView被addView到WindowManagerImpl中,此时才会创建ViewRootImpl实例,用来管理View
的测量绘制工作;
在ViewRootImpl的performTraversals被执行时,采取真正的测量、布局和绘制View;
performTraversals会被执行两遍,并且在开始测量、布局和绘制之前,会将post进View中的Runnable
取出执行,利用这个特点,我们可以进行双重post,在第二重post中我们就能够最新测量出来的宽高;
如果Fragment是通过FragmentManager进行show和hide操作来实现显隐的,是不会调用Fragment的
的声明周期方法的,但是可以通过onHiddenChanged来进行判断;
如果Fragment是配合ViewPager进行显隐的,则通过Fragment的setUserVisibleHint进行判断;
查看FragmentPagerAdapter和FragmentStatePagerAdapter源码可以看到对setUserVisibleHint
的调用,这个方法被调用非常频繁,在进行滑动操作时,处于当前屏幕和其左右的两个Fragment都有可能被
调用到,但是只有在Fragment成为当前屏幕的Fragment时参数才会为true,需要注意判断,并且不要做太多
的耗时操作,否则会影响到切换的流畅性;
java中Exception、Error都是Throwable的子类!
根据异常的发生方式,可以分为检查异常和非检查异常;
检查异常,要求编译器必须即时处理,Java编译器会在语法中对该类型的异常进行检查,并强制要求我们
通过try-catch语句进行主动捕获,或者用throws语句在方法声明后抛出该异常,否则无法编译通过,除了
Error、RuntimeException和其子类之外的异常都是检查异常;
非检查异常,这类异常编译器不要求我们主动捕获或抛出,发生在程序运行期间,通常能够导致程序崩溃。
其包括了RuntimeException及其子类(nullPointException、ArrayIndexOutOfBoundException等
)和Error;