概述:EventBus主要的流程包括以下三个方面:

1.EventBus的初始化过程

2.EventBus的注册过程

3.EventBus的事件发送过程

EventBus的初始化过程:

1.单利初始化

1
2
3
4
5
6
7
8
9
10
11
12
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
//这里其实使用了默认的Builder
//public EventBus() { this(DEFAULT_BUILDER); }
}
}
}
return defaultInstance;
}

2.使用Builder初始化

通过EventBus的静态方法builder()创建EventBusBuilder对象
1
2
3
public static EventBusBuilder builder() {
return new EventBusBuilder();
}
1
EventBus eventBus = EventBus.builder().xxx.build();

第一部分总结:简易vs定制

通过EventBus提供的两种初始化方式,我们可以通过单利简易的使用EventBus为我们提供的默认配置。

而通过第二种Builder模式创建对象开发者可以定制EventBus对象。


EventBus的注册过程:

1
2
3
4
5
6
7
8
9
10
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();//被注册类的类对象
//通过被注册的类对象找到所有响应方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);//遍历所有响应方法逐个订阅
}
}
}

真正的订阅逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/***
*
* @param subscriber register的类
* @param subscriberMethod 类里的subscriber方法
*/

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;//事件类的类对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType存储了<事件类的Class对象,所有的订阅者>
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}

int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}

List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//黏性和继承性的处理
if (subscriberMethod.sticky) {//是黏性事件
if (eventInheritance) {//有继承
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
//当存在很多黏性事件的时候迭代所有事件是很低效的,应该换用高效点的数据结构去查找
//例如,用一个额外的Map来存<父类,List<子类>>
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//根据线程类型调用对应的事件方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {//判断threadMode
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}

1.从基本使用说起

1.1 volley的基本使用

1
mQueue = Volley.newRequestQueue(context)

主要是

1.建立缓存目录
2.通过版本判断使用HttpURLConnection还是HttpClient,源码及注释如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//缓存目录
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
//用哪种Http请求
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//
Network network = new BasicNetwork(stack);
//这里初始化了4个网络线程:NetWorkDispatcher数组

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//start方法内部直接初始化一个CacheDispatcher,循环初始化四个NetWorkDispatcher
//并调用线程的start方法开启这五个线程
queue.start();

return queue;
}

1.2 CacheDispatcher线程

(1)从缓存队列中提出一个请求对象:final Request<?> request = mCacheQueue.take();
(2)判断是否有缓存(ttl)-->无,放进网络请求队列
(3)判断是否过期-->是,放进网络请求队列
(4)是否需要刷新-->是,放进网络请求队列
(5)回调回去:mDelivery.postResponse(request, response);

1.3 NetWorkDispatcher线程

(1)取出网络请求对象:request = mQueue.take();
(2)执行请求:mNetwork.performRequest(request);
        请求会带上上一次请求的header信息(If-None-Match,If-Modified-Since)
        包含对请求超时等异常的处理和retry,如果返回304则更新本地缓存的entry
(3)parseNetworkResponse 此为抽象方法,继承Request类实现该方法,做一些预处理
(4)HttpHeaderParser.parseCacheHeaders
        解析头部信息,主要是http缓存相关,用于缓存过期判断,产生Cache.Entry对象
(5)回调回去:mDelivery.postResponse(request, response);

基本知识

1.自然界的声音信号是连续的,而计算机存储的信号是离散的,将自然界的连续信号转换成计算机能够存储的离散信号的过程叫做信号的采样,如下图(a)为连续信号,(b)为采样后的离散信号

信号的采样

2.采样率:指采样过程中每秒采集多少个样本,单位:Hz

比特率:经过编码压缩后的音频数据每秒需要多少比特来表示,单位:kbps

奈奎斯特采样定理:采样率大于或等于连续信号最高频率的2倍时,采样信号可以完美的重构原始连续信号

在码率较低的情况下,不同编码方案的音频的音质排序为:AAC+> MP3PRO > AAC > RealAudio > WMA > MP3

PackageInfo:

  • 包名获取方法:packageInfo.packageName
  • icon获取获取方法:packageManager.getApplicationIcon(applicationInfo)
  • 应用名称获取方法:packageManager.getApplicationLabel(applicationInfo)
  • 使用权限获取方法:packageManager.getPackageInfo(packageName,PackageManager.GET_PERMISSIONS).requestedPermissions

ApplicationInfo:

ApplicationInfo是从一个特定的应用的imanifest.xml的< application>标签中收集信息。
ApplicationInfo类 继承自 PackageItemInfo
说明:获取一个特定引用程序中节点的信息。
字段说明:
    flags字段: FLAG_SYSTEM 系统应用程序
       FLAG_EXTERNAL_STORAGE 表示该应用安装在sdcard中
常用方法继承至PackageItemInfo类中的loadIcon()和loadLabel()

ResolveInfo:

ResolveInfo通过解析一个与IntentFilter相对应的intent得到信息。它对应于从AndroidManifest.xml的< intent>标签收集到的信息。

ResolveInfo类
说明:根据节点来获取其上一层目录的信息,通常是节点信息。
常用字段:
public ActivityInfo activityInfo 获取 ActivityInfo对象,即节点信息
public ServiceInfo serviceInfo 获取 ServiceInfo对象,即节点信息
常用方法:
Drawable loadIcon(PackageManager pm) 获得当前应用程序的图像
CharSequence loadLabel(PackageManager pm) 获得当前应用程序的label
通过ResolveInfo 获取具体信息方法:

  • 包名获取方法:resolve.activityInfo.packageName
  • icon获取获取方法:resolve.loadIcon(packageManager)
  • 应用名称获取方法:resolve.loadLabel(packageManager).toString()

ActivityInfo:

继承自 PackageItemInfo
说明: 获得应用程序中或者 节点的信息 。我们可以通过它来获取我们设置的任何属性,包括
theme 、launchMode、launchmode等,常用方法继承至PackageItemInfo类中的loadIcon()和loadLabel()