HSH's Blogs

  • Home

  • Tags

  • Categories

  • Archives

Utils

Posted on 2018-08-03 | Edited on 2018-08-30 | In Android

校验重复方法签名

该方法取自EventBus3.0,原理是通过HashMap的put方法,在相同key的情况下,会返回旧的数据;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
// 构造方法签名
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());

String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
// 放进HashMap查看是否有重复
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
// 如果方法签名重复,并且两个方法是在同一个类或者存在父子或接口继承关系
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}

EventBus源码分析

Posted on 2018-08-03 | Edited on 2018-08-30 | In 框架源码

什么是EventBus

EventBus是一个使用”观察者模式”的、松耦合的开源框架,能够帮助我们简化代码、松依赖、加速开发。

EventBus基于观察者模式中的发布-订阅(Publish-Subscribe)模式(事件总线),与传统的观察者模式
不同,在传统的观察者模式下,我们更多是处于订阅者的身份,数据的发布大多数情况下我们是不可控的,我
们一般只能触发数据开始发送,而EventBus则将发布(post)和订阅的能力都交由我们自己处理;
当然,EventBus同样拥有观察者模式的优点,就是发布者和订阅者是解耦的。

1、订阅者注册时,会用两个HashMap来保存其信息,分别是根据事件类型(key)保存订阅信息
(订阅者 - 订阅方法)的subscriptionsByEventType和根据订阅者(key)保存其对应的所有订阅事件的
typesBySubscriber;

Read more »

接口

Posted on 2018-07-31 | Edited on 2018-08-05 | In Kotlin

定义变量和方法

Kotlin中的接口是可以定义变量和方法体,但变量不可以初始化,也就是不能有状态,只能有定义(Java
中的变量可以被初始化
),在被继承时必须初始化接口变量,赋予状态,而方法表示的是实现过程,本身就没
有状态(Java8开始,也支持定义static和default类型的方法,这些方法是可以有方法体的)。

为什么接口中的变量不能被初始化?

因为接口中的变量没有backing field,也就无法存储变量,所以只能做为一种声明;

backing field(类中定义的变量自带,用来存储变量内容,可以理解为堆栈) + get、set 方法 等价于
Kotlin类中的属性Property,这也是Kotlin中独有的;

在Kotlin中只需声明一个变量,就相当于属性,backing field + get、set 方法由Kotlin自动生成;
在Java中,这些都必须自己手动声明;

解决多接口方法冲突

当一个类继承了多个接口,这些接口出现了两个完全一致的方法声明,必须通过super进行显式指定调用方;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface n1{
fun nn(): Int = 1
}

interface n2{
fun nn(): Int = 2
}

abstract class n3{
open fun nn(): Int = 3
}

class n0 : n1, n2, n3() {
val i = 0
override fun nn(): Int {
if (i > 0) {
return super<n2>.nn()
}else if (i < 0) {
return super<n3>.nn()
}
return super<n1>.nn()
}
}

SAM接口

SAM接口又称为函数式接口(Single Abstract Method interfaces),这种接口可以简单的理解为:只有
一个抽象方法的接口,这种接口可以通过lambda实现简化,在Java8和Kotlin中都有应用到。

比如View的OnclickListener接口就是一个典型的SAM接口,Kotlin中的函数类型也可以理解为SAM接口
的另一种形式(Kotlin不需要定义SAM接口,有函数类型就够了,对SAM接口的简化只是为了兼容Java)。

Kotlin基础

Posted on 2018-07-31 | Edited on 2018-08-05 | In Kotlin

常识

  • Kotlin没有8进制,只有2进制和16进制,十进制;

  • as? 可以进行安全的类型转换,当转换失败时,会被自动赋为null:

    1
    2
    val s String ="aaa";
    val a Int? = s as? Int
Read more »

Java基础

Posted on 2018-07-31 | Edited on 2018-08-05 | In Java

变量的存储位置

1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制。

  1. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)
    或者常量池中(字符串常量对象存放在常量池中)。

  2. 堆:存放所有new出来的对象。

  3. 静态域:存放静态成员(static定义的)。

  4. 常量池:存放字符串常量和基本类型常量(public static final)。

  5. 非RAM存储:硬盘等永久存储空间。


堆栈:

  • 对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享;
  • 栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃
    圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性;

字符串:

  • 对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在
    常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永
    远只有一份,在堆中有多份;
  • 对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如
    果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也
    就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有
    ”xyz”,就是两个。

进程与线程的区别

  • 一个程序至少有一个进程(通过为Activity设置android:process可以让其运行在单独的进程中),一个
    进程至少有一个线程;
  • 进程可以单独存在,而线程不能独立存在,必须依赖于一个进程;
  • 进程是系统资源分配的最小单位,线程是程序执行的最小单位;
  • 进程有自己独立的内存地址空间,进程之间无法直接通讯,只能通过IPC进行通讯;
  • 线程间共享进程的地址空间和内存资源,多个线程共享全局变量和静态变量,所以线程之间的通讯更加方便;
  • 进程之间是相互独立的,一个进程死掉,不会影响到其他进程,而在多线程的环境下,一个线程死掉,其所
    在的整个进程也会死掉;

内部类的优点

Java不支持多继承 ,只支持多实现。但是,很多时候,只是实现一些接口很难解决问题,此时,我们就可以
使用内部类来解决问题。内部类即可与外部类关联(非静态内部类),也可以独立于外部类(静态内部类),并且,
内部类的环境是不受到外部类的影响的,通过它们来继承我们需要的类的能力,能让我们更灵活的解决问题;

可以说,接口只是帮我们解决了部分问题,而内部类赋予类多重继承的能力,使解决问题的方案更加的完整;

MVX

Posted on 2018-07-30 | Edited on 2018-08-05 | In Android

三层架构与MVX模式

  • MVX是一种设计模式,而三层架构则是一种架构思想,或者说是一种编写代码的纲领;
  • MVX可以认为是在三层架构下的一种具体的表现;
  • 三层架构分为:表现层,业务逻辑层,数据访问层;其中的数据访问层即Model概念区别于MVX模式的Model;
  • 三层架构中的Model表示对数据的访问的总代理,涉及了数据的增删改查操作,数据可以来自本地或者网络;
  • MVX模式下的Model就是一种纯粹的数据类,是一个数据实体的表现;

MVC、MVP

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

Posted on 2018-07-30 | Edited on 2018-08-05 | In Android

Handler消息机制

  • Handler在创建时,就会在构造方法中拿到当前线程的Looper,而Looper保存有当前线程的消息队列,也
    会和Handler绑定到一起;

  • Handler在哪个线程被创建,就会绑定哪个线程中的Looper;

  • 主线程默认会创建一个MainLooper,而如果在子线程中创建Handler,就需要我们手动为其创建一个
    Looper,并绑定到Handler中;

    查看ActivityThread类,在main方法中调用了Looper的prepareMainLooper方法来初始化
    MainLooper,可以获取它来判断当前的操作是否位于主线程。

  • 使用Looper.prepare()即可为当前线程创建一个Looper,每一个Looper中都自带一个消息队列;

Read more »

Android 基础

Posted on 2018-07-30 | Edited on 2018-08-30 | In Android

Android 的数据存储方式

在Activity启动时何时能够拿到View宽高

从Activity启动声明周期来看,即使是在onPostResume时也是拿不到宽高的!

因为和Activity中View的布局、测量、绘制相关的类ViewRootImpl在onPostResume时还没有创建,只有
在之后DecorView被addView到WindowManagerImpl中,此时才会创建ViewRootImpl实例,用来管理View
的测量绘制工作;

在ViewRootImpl的performTraversals被执行时,采取真正的测量、布局和绘制View;

performTraversals会被执行两遍,并且在开始测量、布局和绘制之前,会将post进View中的Runnable
取出执行,利用这个特点,我们可以进行双重post,在第二重post中我们就能够最新测量出来的宽高;

Fragment

Posted on 2018-07-30 | Edited on 2018-08-05 | In Android

判断Fragment可见性

  • 如果Fragment是通过FragmentManager进行show和hide操作来实现显隐的,是不会调用Fragment的
    的声明周期方法的,但是可以通过onHiddenChanged来进行判断;

  • 如果Fragment是配合ViewPager进行显隐的,则通过Fragment的setUserVisibleHint进行判断;

    查看FragmentPagerAdapter和FragmentStatePagerAdapter源码可以看到对setUserVisibleHint
    的调用,这个方法被调用非常频繁,在进行滑动操作时,处于当前屏幕和其左右的两个Fragment都有可能被
    调用到,但是只有在Fragment成为当前屏幕的Fragment时参数才会为true,需要注意判断,并且不要做太多
    的耗时操作,否则会影响到切换的流畅性;

Read more »

Java异常、错误

Posted on 2018-07-30 | Edited on 2018-08-05 | In Java

image

java中Exception、Error都是Throwable的子类!

  • Exception即异常,是程序本身可以处理的;
  • Error即错误,是程序本身无法处理的,通常是系统出现故障,或者虚拟机自身发出的错误,一般不需要主动
    进行处理,比如OutOfMemoryError、StackOverflowError等等;

根据异常的发生方式,可以分为检查异常和非检查异常;

  • 检查异常,要求编译器必须即时处理,Java编译器会在语法中对该类型的异常进行检查,并强制要求我们
    通过try-catch语句进行主动捕获,或者用throws语句在方法声明后抛出该异常,否则无法编译通过,除了
    Error、RuntimeException和其子类之外的异常都是检查异常;

  • 非检查异常,这类异常编译器不要求我们主动捕获或抛出,发生在程序运行期间,通常能够导致程序崩溃。
    其包括了RuntimeException及其子类(nullPointException、ArrayIndexOutOfBoundException等
    )和Error;

1…456…8

黄声焕

71 posts
9 categories
21 tags
© 2019 黄声焕
Powered by Hexo v3.7.1
|
Theme — NexT.Gemini v6.3.0