-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
170 lines (81 loc) · 111 KB
/
search.xml
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>jni环境搭建</title>
<link href="/2019/02/18/jni%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
<url>/2019/02/18/jni%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/</url>
<content type="html"><![CDATA[<p>最近需要研究如何快速定位以及防护安卓native的crash相关知识,故此产生了这篇有关搭建jni环境的文章。<a id="more"></a> </p><ol><li><p>新建一个hello world工程,这个相信大家都会;</p></li><li><p>在新建工程中的MainActivity类中添加如下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">static {</span><br><span class="line"> System.loadLibrary("MyJni");//导入生成的链接库文件</span><br><span class="line">}</span><br><span class="line">public native String getStringFromNative();//本地方法</span><br></pre></td></tr></table></figure></li><li><p>在Perferences->Tools->External Tools下面添加如下两个配置:</p><p><img src="/2019/02/18/jni环境搭建/javah.jpg" alt="javah"></p><p>上图中的Arguments为:-classpath . -jni -d \$ModuleFileDir\$/src/main/jni \$FileClass\$,Working directory为:\$ModuleFileDir\$/src/main/Java。</p><p><img src="/2019/02/18/jni环境搭建/ndk-build.jpg" alt="ndk-build"></p><p>上图中的Program为你的ndk-build.cmd所在路径,我的路径为/Users/admin/Library/Android/sdk/ndk-bundle/ndk-build,Working directory为:\$ModuleFileDir\$/src/main/jni。</p></li><li><p>右键点击工程中的MainActivity类文件,选择External Tools->javah,这个时候会在src->main->jni目录下生成相应jni的.h文件。</p></li><li><p>在jni目录下面新建test.c文件,添加如下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">//</span><br><span class="line">// Created by admin on 2019/2/15.</span><br><span class="line">//</span><br><span class="line"></span><br><span class="line">#include<jni.h></span><br><span class="line">#include<stdio.h></span><br><span class="line">//导入我们创建的头文件</span><br><span class="line">#include "com_example_admin_test_MainActivity.h"</span><br><span class="line"></span><br><span class="line">JNIEXPORT jstring JNICALL Java_com_example_admin_test_MainActivity_getStringFromNative</span><br><span class="line"> (JNIEnv *env, jclass jclass){</span><br><span class="line"> //返回一个字符串</span><br><span class="line"> return (*env)->NewStringUTF(env,"This is my first NDK Application");</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>在jni目录下面新建Android.mk文件,添加如下代码:</p><p>其中MyJni为so文件名称,test.c为需要编译的.c源文件</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">LOCAL_PATH := $(call my-dir)</span><br><span class="line">include $(CLEAR_VARS)</span><br><span class="line"></span><br><span class="line">LOCAL_MODULE := MyJni</span><br><span class="line">LOCAL_SRC_FILES := test.c</span><br><span class="line">include $(BUILD_SHARED_LIBRARY)</span><br></pre></td></tr></table></figure></li><li><p>在jni目录下面新建Application.mk文件,添加如下代码:</p><p>表示生成适配所有机型架构的.so文件</p><p><code>APP_ABI := all</code></p></li><li><p>将MainActivity中的代码改成如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">public class MainActivity extends Activity {</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> protected void onCreate(Bundle savedInstanceState) {</span><br><span class="line"> super.onCreate(savedInstanceState);</span><br><span class="line"> setContentView(R.layout.activity_main);</span><br><span class="line"> ((TextView)findViewById(R.id.textview)).setText(getStringFromNative());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> static {</span><br><span class="line"> System.loadLibrary("MyJni");//导入生成的链接库文件</span><br><span class="line"> }</span><br><span class="line"> public native String getStringFromNative();//本地方法</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>在app的build.gradle文件的android中添加如下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">sourceSets {</span><br><span class="line"> main() {</span><br><span class="line"> jniLibs.srcDirs = ['src/main/libs']</span><br><span class="line"> jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>在工程的grade.properties文件中添加如下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">android.useDeprecatedNdk=true</span><br></pre></td></tr></table></figure></li><li><p>右键点击jni目录,选择External Tools->ndk-build,这个时候会在和jni同目录的libs和obj中生成相应的.so文件。其中obj中的.so文件是包含调试信息的,libs中的.so文件不包含。</p></li></ol><p>完成以上步骤之后,点击运行,工程就能正常跑起来了。</p><p><strong>注:</strong></p><p>在mac电脑上,需要配置adb、ndk-build的环境,否则会报command not found,步骤如下:</p><p>启动Mac自带的终端Terminal;</p><p>进入当前用户的home目录:在命令行输入echo $HOME;</p><p>创建.bash_profile文件:命令行输入touch .bash_profile;</p><p>打开.bash_profile文件:命令行输入open -e .bash_profile此命令行输入完毕后,会自动弹出 .bash_profile 文件的编辑窗口,添加如下信息到文件中export PATH=${PATH}:/Users/admin/Library/Android/sdk/platform-tools:/Users/admin/Library/Android/sdk/tools:/Users/admin/Library/Android/sdk/ndk-bundle,请注意在文件中写的路径和你相应文件放置的路径一致。</p>]]></content>
<categories>
<category> jni, ndk </category>
</categories>
<tags>
<tag> jni, ndk </tag>
</tags>
</entry>
<entry>
<title>EventBus3.0源码解析-04</title>
<link href="/2019/01/10/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-04/"/>
<url>/2019/01/10/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-04/</url>
<content type="html"><![CDATA[<p><a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-01/#more" target="_blank" rel="noopener">EventBus3.0源码解析-01</a>这篇文章简单的介绍了EventBus的使用流程,我们知道EventBus有三个主要入口方法,分别为</p><ul><li><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().register(Object);</span><br></pre></td></tr></table></figure><a id="more"></a></li><li><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().unregister(Object);</span><br></pre></td></tr></table></figure></li><li><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().post(Object);</span><br></pre></td></tr></table></figure></li></ul><p><a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-02/" target="_blank" rel="noopener">EventBus3.0源码解析-02</a>这篇文章已经介绍了register的逻辑,本文将介绍post,unregister以及sticky事件的逻辑。</p><h2 id="Post"><a href="#Post" class="headerlink" title="Post"></a>Post</h2><p><strong>post</strong>方法的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">public void post(Object event) {</span><br><span class="line">// 线程安全的变量</span><br><span class="line"> PostingThreadState postingState = currentPostingThreadState.get();</span><br><span class="line"> // 事件队列</span><br><span class="line"> List<Object> eventQueue = postingState.eventQueue;</span><br><span class="line"> eventQueue.add(event);</span><br><span class="line"> </span><br><span class="line"> // 是否当前线程正在发送事件,如果正在发送事件,那么队列的事件都会处理,这里直接返回就行</span><br><span class="line"> if (!postingState.isPosting) {</span><br><span class="line"> // 当前线程是否是主线程</span><br><span class="line"> postingState.isMainThread = isMainThread();</span><br><span class="line"> // 当前线程是否正在发送事件的标识</span><br><span class="line"> postingState.isPosting = true;</span><br><span class="line"> if (postingState.canceled) {</span><br><span class="line"> throw new EventBusException("Internal error. Abort state was not reset");</span><br><span class="line"> }</span><br><span class="line"> try {</span><br><span class="line"> // 遍历队列里面的每个事件</span><br><span class="line"> while (!eventQueue.isEmpty()) {</span><br><span class="line"> // 单个事件具体处理逻辑</span><br><span class="line"> postSingleEvent(eventQueue.remove(0), postingState);</span><br><span class="line"> }</span><br><span class="line"> } finally {</span><br><span class="line"> // 重置标志</span><br><span class="line"> postingState.isPosting = false;</span><br><span class="line"> postingState.isMainThread = false;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>post方法里面的大多数逻辑通过看代码注释就能明白,我们这里主要介绍它的核心方法<strong>postSingleEvent</strong>(Object event, PostingThreadState postingState),该方法的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {</span><br><span class="line"> Class<?> eventClass = event.getClass();</span><br><span class="line"> boolean subscriptionFound = false;</span><br><span class="line"> // 是否考虑事件类的继承,默认false</span><br><span class="line"> if (eventInheritance) {</span><br><span class="line"> // 找到eventClass以及eventClass的父类以及接口</span><br><span class="line"> List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);</span><br><span class="line"> int countTypes = eventTypes.size();</span><br><span class="line"> for (int h = 0; h < countTypes; h++) {</span><br><span class="line"> Class<?> clazz = eventTypes.get(h);</span><br><span class="line"> // 处理每个事件</span><br><span class="line"> subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> // 处理事件</span><br><span class="line"> subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);</span><br><span class="line"> }</span><br><span class="line"> if (!subscriptionFound) {</span><br><span class="line"> if (logNoSubscriberMessages) {</span><br><span class="line"> logger.log(Level.FINE, "No subscribers registered for event " + eventClass);</span><br><span class="line"> }</span><br><span class="line"> if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&</span><br><span class="line"> eventClass != SubscriberExceptionEvent.class) {</span><br><span class="line"> post(new NoSubscriberEvent(this, event));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>postSingleEvent方法的实现逻辑为,首先判断是否考虑事件类的继承,如果不考虑,则只通知订阅该event的方法。如果考虑事件类的继承,则会通知订阅该event以及该event的父类以及其实现的接口的所有方法。我们看一下<strong>postSingleEventForEventType</strong>(Object event, PostingThreadState postingState, Class<?> eventClass)方法的实现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {</span><br><span class="line"> CopyOnWriteArrayList<Subscription> subscriptions;</span><br><span class="line"> synchronized (this) {</span><br><span class="line"> subscriptions = subscriptionsByEventType.get(eventClass);</span><br><span class="line"> }</span><br><span class="line"> if (subscriptions != null && !subscriptions.isEmpty()) {</span><br><span class="line"> for (Subscription subscription : subscriptions) {</span><br><span class="line"> postingState.event = event;</span><br><span class="line"> postingState.subscription = subscription;</span><br><span class="line"> boolean aborted = false;</span><br><span class="line"> try {</span><br><span class="line"> postToSubscription(subscription, event, postingState.isMainThread);</span><br><span class="line"> aborted = postingState.canceled;</span><br><span class="line"> } finally {</span><br><span class="line"> postingState.event = null;</span><br><span class="line"> postingState.subscription = null;</span><br><span class="line"> postingState.canceled = false;</span><br><span class="line"> }</span><br><span class="line"> if (aborted) {</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> return false;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该方法就是找到订阅了eventClass的所有订阅方法,然后通过<strong>postToSubscription</strong>(Subscription subscription, Object event, boolean isMainThread)通知订阅方法执行:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {</span><br><span class="line"> switch (subscription.subscriberMethod.threadMode) {</span><br><span class="line"> case POSTING:</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> break;</span><br><span class="line"> case MAIN:</span><br><span class="line"> if (isMainThread) {</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> } else {</span><br><span class="line"> mainThreadPoster.enqueue(subscription, event);</span><br><span class="line"> }</span><br><span class="line"> break;</span><br><span class="line"> case MAIN_ORDERED:</span><br><span class="line"> if (mainThreadPoster != null) {</span><br><span class="line"> mainThreadPoster.enqueue(subscription, event);</span><br><span class="line"> } else {</span><br><span class="line"> // temporary: technically not correct as poster not decoupled from subscriber</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> }</span><br><span class="line"> break;</span><br><span class="line"> case BACKGROUND:</span><br><span class="line"> if (isMainThread) {</span><br><span class="line"> backgroundPoster.enqueue(subscription, event);</span><br><span class="line"> } else {</span><br><span class="line"> invokeSubscriber(subscription, event);</span><br><span class="line"> }</span><br><span class="line"> break;</span><br><span class="line"> case ASYNC:</span><br><span class="line"> asyncPoster.enqueue(subscription, event);</span><br><span class="line"> break;</span><br><span class="line"> default:</span><br><span class="line"> throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>invokeSubscriber方法就是通过反射执行相应的订阅方法,各个ThreadMode的具体含义可以参考<a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-01/" target="_blank" rel="noopener">这篇文章</a>。</p><h2 id="Unregister"><a href="#Unregister" class="headerlink" title="Unregister"></a>Unregister</h2><p><strong>unregister</strong>方法的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">public synchronized void unregister(Object subscriber) {</span><br><span class="line"> List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);</span><br><span class="line"> if (subscribedTypes != null) {</span><br><span class="line"> for (Class<?> eventType : subscribedTypes) {</span><br><span class="line"> unsubscribeByEventType(subscriber, eventType);</span><br><span class="line"> }</span><br><span class="line"> typesBySubscriber.remove(subscriber);</span><br><span class="line"> } else {</span><br><span class="line"> logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个方法的实现非常简单,就是将该订阅类的订阅信息从subscriptionsByEventType和typesBySubscriber变量中剔除。</p><h2 id="Sticky事件"><a href="#Sticky事件" class="headerlink" title="Sticky事件"></a>Sticky事件</h2><p>sticky事件的使用分为两步:</p><p>1、调用postSticky方法:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">public void postSticky(Object event) {</span><br><span class="line"> synchronized (stickyEvents) {</span><br><span class="line"> stickyEvents.put(event.getClass(), event);</span><br><span class="line"> }</span><br><span class="line"> // Should be posted after it is putted, in case the subscriber wants to remove immediately</span><br><span class="line"> post(event);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该方法首先将sticky事件保存在stickyEvents变量中,然后走正常的post事件流程。</p><p>2、当我们调用EventBus的register方法时,会执行以下逻辑:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">if (subscriberMethod.sticky) {</span><br><span class="line"> // 是否考虑注册方法的参数,事件的继承关系</span><br><span class="line"> if (eventInheritance) {</span><br><span class="line"> // Existing sticky events of all subclasses of eventType have to be considered.</span><br><span class="line"> // Note: Iterating over all events may be inefficient with lots of sticky events,</span><br><span class="line"> // thus data structure should be changed to allow a more efficient lookup</span><br><span class="line"> // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).</span><br><span class="line"> Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();</span><br><span class="line"> for (Map.Entry<Class<?>, Object> entry : entries) {</span><br><span class="line"> Class<?> candidateEventType = entry.getKey();</span><br><span class="line"> if (eventType.isAssignableFrom(candidateEventType)) {</span><br><span class="line"> Object stickyEvent = entry.getValue();</span><br><span class="line"> checkPostStickyEventToSubscription(newSubscription, stickyEvent);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> Object stickyEvent = stickyEvents.get(eventType);</span><br><span class="line"> checkPostStickyEventToSubscription(newSubscription, stickyEvent);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>如果不考虑事件类的继承关系,那么只是把stickyEvents中eventType拿出来进行通知,如果考虑继承,那么需要把stickyEvents中eventType以及eventType的子类事件都拿出来进行通知。checkPostStickyEventToSubscription方法就是调用上面介绍的postToSubscription方法。</p><p>如果大家看完我的本系列文章之后,对EventBus还有不明白的地方,请下载<a href="https://github.com/greenrobot/EventBus" target="_blank" rel="noopener">EventBus</a>源码进行学习。</p>]]></content>
<categories>
<category> EventBus3.0 </category>
</categories>
<tags>
<tag> EventBus3.0 </tag>
</tags>
</entry>
<entry>
<title>EventBus3.0源码解析-03</title>
<link href="/2019/01/09/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-03/"/>
<url>/2019/01/09/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-03/</url>
<content type="html"><![CDATA[<p><a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-02/" target="_blank" rel="noopener">EventBus3.0源码解析-02</a>中我们介绍到,当用户调用EventBus的register方法时,会收集保存相应的订阅信息。而在收集订阅信息的过程中,有两种方式,一种通过反射收集,一种通过注解处理器在编译期生成的索引文件进行收集。第一种方式已经在<a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-02/" target="_blank" rel="noopener">EventBus3.0源码解析-02</a>中进行了详细的介绍,本文将介绍通过索引文件的方式。<a id="more"></a></p><h2 id="注解处理器"><a href="#注解处理器" class="headerlink" title="注解处理器"></a>注解处理器</h2><p>如果读者对注解处理器还不是很了解,可以阅读<a href="https://cugyong.github.io/2019/01/04/%E5%AE%89%E5%8D%93%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8-processor/" target="_blank" rel="noopener">这篇文章</a>进行学习。本文我假设大家对注解处理器已经有了基本的了解,将主要介绍EventBus3.0的注解处理器逻辑。其主要实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {</span><br><span class="line"> Messager messager = processingEnv.getMessager();</span><br><span class="line"> try {</span><br><span class="line"> String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);</span><br><span class="line"> if (index == null) {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR, "No option " + OPTION_EVENT_BUS_INDEX +</span><br><span class="line"> " passed to annotation processor");</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> verbose = Boolean.parseBoolean(processingEnv.getOptions().get(OPTION_VERBOSE));</span><br><span class="line"> int lastPeriod = index.lastIndexOf('.');</span><br><span class="line"> String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null;</span><br><span class="line"></span><br><span class="line"> round++;</span><br><span class="line"> if (verbose) {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.NOTE, "Processing round " + round + ", new annotations: " +</span><br><span class="line"> !annotations.isEmpty() + ", processingOver: " + env.processingOver());</span><br><span class="line"> }</span><br><span class="line"> if (env.processingOver()) {</span><br><span class="line"> if (!annotations.isEmpty()) {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR,</span><br><span class="line"> "Unexpected processing state: annotations still available after processing over");</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (annotations.isEmpty()) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (writerRoundDone) {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR,</span><br><span class="line"> "Unexpected processing state: annotations still available after writing.");</span><br><span class="line"> }</span><br><span class="line"> collectSubscribers(annotations, env, messager);</span><br><span class="line"> checkForSubscribersToSkip(messager, indexPackage);</span><br><span class="line"></span><br><span class="line"> if (!methodsByClass.isEmpty()) {</span><br><span class="line"> createInfoIndexFile(index);</span><br><span class="line"> } else {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.WARNING, "No @Subscribe annotations found");</span><br><span class="line"> }</span><br><span class="line"> writerRoundDone = true;</span><br><span class="line"> } catch (RuntimeException e) {</span><br><span class="line"> // IntelliJ does not handle exceptions nicely, so log and print a message</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR, "Unexpected error in EventBusAnnotationProcessor: " + e);</span><br><span class="line"> }</span><br><span class="line"> return true;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过上面代码我们知道,其核心逻辑应该在<strong>collectSubscribers</strong>,<strong>checkForSubscribersToSkip</strong>和<strong>createInfoIndexFile</strong>三个方法中。</p><p><strong>注意</strong>这句代码String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX),OPTION_EVENT_BUS_INDEX的值为“eventBusIndex”,它将获取注解处理器的可选参数,是由用户指定的,那么怎么指定呢?</p><p>1、由@SupportedOptions指定注解处理器可以接收哪些可选参数:</p><p><img src="/2019/01/09/EventBus3源码解析-03/注解处理器指定可选参数.png" alt="指定可选参数"></p><p>2、在app工程的build.gradle文件中传入参数值:</p><p><img src="/2019/01/09/EventBus3源码解析-03/传入注解处理器可选参数.png" alt="传入可选参数"></p><p>后面我们接着介绍<strong>collectSubscribers</strong>,<strong>checkForSubscribersToSkip</strong>和<strong>createInfoIndexFile</strong>三个方法。</p><h3 id="collectSubscribers方法"><a href="#collectSubscribers方法" class="headerlink" title="collectSubscribers方法"></a>collectSubscribers方法</h3><p><strong>collectSubscribers</strong>方法的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">private void collectSubscribers(Set<? extends TypeElement> annotations, RoundEnvironment env, Messager messager) {</span><br><span class="line"> for (TypeElement annotation : annotations) {</span><br><span class="line"> // 找到所有有annotation注解的元素信息</span><br><span class="line"> Set<? extends Element> elements = env.getElementsAnnotatedWith(annotation);</span><br><span class="line"> for (Element element : elements) {</span><br><span class="line"> // 元素是否是可执行元素,比如方法</span><br><span class="line"> if (element instanceof ExecutableElement) {</span><br><span class="line"> ExecutableElement method = (ExecutableElement) element;</span><br><span class="line"> // 检查方法定义是否符合规范</span><br><span class="line"> if (checkHasNoErrors(method, messager)) {</span><br><span class="line"> // 方法所在的类元素</span><br><span class="line"> TypeElement classElement = (TypeElement) method.getEnclosingElement();</span><br><span class="line"> // 保存找到的元素</span><br><span class="line"> methodsByClass.putElement(classElement, method);</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>collectSubscribers方法逻辑相对简单,相信读者通过阅读代码注释就能明白,<strong>就是把所有有用的信息收集到了methodsByClass变量中</strong>。其中,checkHasNoErrors方法的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">private boolean checkHasNoErrors(ExecutableElement element, Messager messager) {</span><br><span class="line"> // 方法修饰符是否包含static</span><br><span class="line"> if (element.getModifiers().contains(Modifier.STATIC)) {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must not be static", element);</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> // 方法修饰符是否包含public</span><br><span class="line"> if (!element.getModifiers().contains(Modifier.PUBLIC)) {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must be public", element);</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> List<? extends VariableElement> parameters = ((ExecutableElement) element).getParameters();</span><br><span class="line"> // 方法是否只有一个参数</span><br><span class="line"> if (parameters.size() != 1) {</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must have exactly 1 parameter", element);</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> return true;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="checkForSubscribersToSkip"><a href="#checkForSubscribersToSkip" class="headerlink" title="checkForSubscribersToSkip"></a>checkForSubscribersToSkip</h3><p>checkForSubscribersToSkip方法的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">private void checkForSubscribersToSkip(Messager messager, String myPackage) {</span><br><span class="line"> for (TypeElement skipCandidate : methodsByClass.keySet()) {</span><br><span class="line"> TypeElement subscriberClass = skipCandidate;</span><br><span class="line"> while (subscriberClass != null) {</span><br><span class="line"> // subscriberClass类在myPackage包名下面是否可见</span><br><span class="line"> if (!isVisible(myPackage, subscriberClass)) {</span><br><span class="line"> boolean added = classesToSkip.add(skipCandidate);</span><br><span class="line"> if (added) {</span><br><span class="line"> String msg;</span><br><span class="line"> if (subscriberClass.equals(skipCandidate)) {</span><br><span class="line"> msg = "Falling back to reflection because class is not public";</span><br><span class="line"> } else {</span><br><span class="line"> msg = "Falling back to reflection because " + skipCandidate +</span><br><span class="line"> " has a non-public super class";</span><br><span class="line"> }</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.NOTE, msg, subscriberClass);</span><br><span class="line"> }</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> List<ExecutableElement> methods = methodsByClass.get(subscriberClass);</span><br><span class="line"> if (methods != null) {</span><br><span class="line"> for (ExecutableElement method : methods) {</span><br><span class="line"> String skipReason = null;</span><br><span class="line"> VariableElement param = method.getParameters().get(0);</span><br><span class="line"> TypeMirror typeMirror = getParamTypeMirror(param, messager);</span><br><span class="line"> if (!(typeMirror instanceof DeclaredType) ||</span><br><span class="line"> !(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {</span><br><span class="line"> skipReason = "event type cannot be processed";</span><br><span class="line"> }</span><br><span class="line"> if (skipReason == null) {</span><br><span class="line"> TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();</span><br><span class="line"> // eventTypeElement事件类在myPackage包名下面是否可见</span><br><span class="line"> if (!isVisible(myPackage, eventTypeElement)) {</span><br><span class="line"> skipReason = "event type is not public";</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (skipReason != null) {</span><br><span class="line"> boolean added = classesToSkip.add(skipCandidate);</span><br><span class="line"> if (added) {</span><br><span class="line"> String msg = "Falling back to reflection because " + skipReason;</span><br><span class="line"> if (!subscriberClass.equals(skipCandidate)) {</span><br><span class="line"> msg += " (found in super class for " + skipCandidate + ")";</span><br><span class="line"> }</span><br><span class="line"> messager.printMessage(Diagnostic.Kind.NOTE, msg, param);</span><br><span class="line"> }</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> subscriberClass = getSuperclass(subscriberClass);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>checkForSubscribersToSkip方法的逻辑是,因为我们将在myPackage包名下面创建索引文件,而如果某些订阅类或者父类在myPackage包名下面是不可见的,那么这些类不应该创建相应的索引代码。同样的,如果订阅方法的事件参数类在myPackage包名下面是不可见的,那么这些类也不应该创建相应的索引代码。</p><h3 id="createInfoIndexFile"><a href="#createInfoIndexFile" class="headerlink" title="createInfoIndexFile"></a>createInfoIndexFile</h3><p>createInfoIndexFile方法就是按照一定规则自动生成java代码,创建相应java源文件。下面通过一个具体的例子来说明createInfoIndexFile方法会生成什么样的文件。</p><p>1、在OtherClass类中使用@Subscribe声明了一个订阅方法:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">public class OtherClass {</span><br><span class="line"></span><br><span class="line"> @Subscribe()</span><br><span class="line"> public void test(String str){</span><br><span class="line"> Log.d("eventbusTest", "OtherClass");</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>2、在MainActivity类中使用@Subscribe声明了一个订阅方法:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">public class MainActivity extends AppCompatActivity {</span><br><span class="line">@Subscribe()</span><br><span class="line"> public void test(String str){</span><br><span class="line"> Log.d("eventbusTest", "MainActivity");</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>3、点击build,会在你通过eventBusIndex指定的路径下面生成一个索引文件,本文指定是”com.yong.cugyong.eventbus_sample.MyEventBusIndex“:</p><p><img src="/2019/01/09/EventBus3源码解析-03/索引文件路径.png" alt="索引文件路径"></p><p>MyEventBusIndex文件的内容如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">/** This class is generated by EventBus, do not edit. */</span><br><span class="line">public class MyEventBusIndex implements SubscriberInfoIndex {</span><br><span class="line"> private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;</span><br><span class="line"></span><br><span class="line"> static {</span><br><span class="line"> SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();</span><br><span class="line"></span><br><span class="line"> putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {</span><br><span class="line"> new SubscriberMethodInfo("test", String.class),</span><br><span class="line"> }));</span><br><span class="line"></span><br><span class="line"> putIndex(new SimpleSubscriberInfo(OtherClass.class, true, new SubscriberMethodInfo[] {</span><br><span class="line"> new SubscriberMethodInfo("test", String.class),</span><br><span class="line"> }));</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static void putIndex(SubscriberInfo info) {</span><br><span class="line"> SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {</span><br><span class="line"> SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);</span><br><span class="line"> if (info != null) {</span><br><span class="line"> return info;</span><br><span class="line"> } else {</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里大家只要知道编译工程之后会生成这样一个文件,在下一部分将告诉大家具体含义。</p><h2 id="索引文件使用"><a href="#索引文件使用" class="headerlink" title="索引文件使用"></a>索引文件使用</h2><p>在文章开头我们介绍到,用户调用EventBus的register方法时,会收集保存相应的订阅信息,而这里我们就介绍怎么通过索引文件找到想要的信息,对register逻辑还不是很了解的朋友可以先看一下<a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-02/" target="_blank" rel="noopener">EventBus3.0源码解析-02</a>。</p><p>通过索引文件收集信息的入口函数是SubscriberMethodFinder类的findUsingInfo方法,该方法的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {</span><br><span class="line"> FindState findState = prepareFindState();</span><br><span class="line"> findState.initForSubscriber(subscriberClass);</span><br><span class="line"> while (findState.clazz != null) {</span><br><span class="line"> findState.subscriberInfo = getSubscriberInfo(findState);</span><br><span class="line"> if (findState.subscriberInfo != null) {</span><br><span class="line"> SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();</span><br><span class="line"> for (SubscriberMethod subscriberMethod : array) {</span><br><span class="line"> if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {</span><br><span class="line"> findState.subscriberMethods.add(subscriberMethod);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> // 索引文件中未找到,则使用反射</span><br><span class="line"> findUsingReflectionInSingleClass(findState);</span><br><span class="line"> }</span><br><span class="line"> findState.moveToSuperclass();</span><br><span class="line"> }</span><br><span class="line"> return getMethodsAndRelease(findState);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该方法里面的大多数逻辑我们都在<a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-02/" target="_blank" rel="noopener">EventBus3.0源码解析-02</a>中进行了介绍,这里我们只介绍不同点<strong>getSubscriberInfo</strong>方法,其实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">private SubscriberInfo getSubscriberInfo(FindState findState) {</span><br><span class="line"> // 如果需要找的信息已经存在,直接返回</span><br><span class="line"> if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {</span><br><span class="line"> SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();</span><br><span class="line"> if (findState.clazz == superclassInfo.getSubscriberClass()) {</span><br><span class="line"> return superclassInfo;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> // 通常会直接走这一块逻辑</span><br><span class="line"> if (subscriberInfoIndexes != null) {</span><br><span class="line"> // 遍历所有索引文件</span><br><span class="line"> for (SubscriberInfoIndex index : subscriberInfoIndexes) {</span><br><span class="line"> SubscriberInfo info = index.getSubscriberInfo(findState.clazz);</span><br><span class="line"> if (info != null) {</span><br><span class="line"> return info;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return null;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该方法遍历所有索引文件,调用索引文件中getSubscriberInfo方法根据订阅类寻找订阅信息。因为这种寻找订阅信息的方式没有反射,所以效率很高。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>EventBus通过索引文件寻找订阅信息,是通过在编译期通过注解处理器生成相应的索引文件,然后在运行期使用索引文件寻找订阅信息,这样在运行期就不需要使用到反射了,提高了效率。</p>]]></content>
<categories>
<category> EventBus3.0 </category>
</categories>
<tags>
<tag> EventBus3.0 </tag>
</tags>
</entry>
<entry>
<title>EventBus3.0源码解析-02</title>
<link href="/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-02/"/>
<url>/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-02/</url>
<content type="html"><![CDATA[<p><a href="https://cugyong.github.io/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-01/#more" target="_blank" rel="noopener">EventBus3.0源码解析-01</a>这篇文章简单的介绍了EventBus的使用流程,我们知道EventBus有三个主要入口方法,分别为</p><ul><li><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().register(Object);</span><br></pre></td></tr></table></figure><a id="more"></a></li><li><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().unregister(Object);</span><br></pre></td></tr></table></figure></li><li><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().post(Object);</span><br></pre></td></tr></table></figure></li></ul><p>本文将通过register入口方法及其逻辑,分析EventBus源码。</p><p>EventBus的register方法实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">public void register(Object subscriber) {</span><br><span class="line"> Class<?> subscriberClass = subscriber.getClass();</span><br><span class="line"> List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);</span><br><span class="line"> synchronized (this) {</span><br><span class="line"> for (SubscriberMethod subscriberMethod : subscriberMethods) {</span><br><span class="line"> subscribe(subscriber, subscriberMethod);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>register函数主要实现了两个功能,1、将订阅类中所有订阅方法收集;2、按一定规则保存收集的信息。</p><h2 id="订阅方法收集"><a href="#订阅方法收集" class="headerlink" title="订阅方法收集"></a>订阅方法收集</h2><p>订阅方法收集主要由<strong>SubscriberMethodFinder</strong>类的findSubscriberMethods函数实现,我们看下其具体实现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {</span><br><span class="line"> // 先从缓存中获取,即之前该类是否注册使用过</span><br><span class="line"> List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);</span><br><span class="line"> if (subscriberMethods != null) {</span><br><span class="line"> return subscriberMethods;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 是否忽略注解处理器生成的索引文件</span><br><span class="line"> if (ignoreGeneratedIndex) {</span><br><span class="line"> subscriberMethods = findUsingReflection(subscriberClass);</span><br><span class="line"> } else {</span><br><span class="line"> subscriberMethods = findUsingInfo(subscriberClass);</span><br><span class="line"> }</span><br><span class="line"> if (subscriberMethods.isEmpty()) {</span><br><span class="line"> throw new EventBusException("Subscriber " + subscriberClass</span><br><span class="line"> + " and its super classes have no public methods with the @Subscribe annotation");</span><br><span class="line"> } else {</span><br><span class="line"> METHOD_CACHE.put(subscriberClass, subscriberMethods);</span><br><span class="line"> return subscriberMethods;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>1、如果参数中的subscriberClass类之前注册过,那么直接从METHOD_CACHE缓存中获取信息返回。</p><p>2、根据ignoreGeneratedIndex判断是否要根据注解处理器在编译期生成的索引文件来寻找订阅方法信息,该方式是从EventBus3.0中引入的,目的是为了提高EventBus的整体效率。如果ignoreGeneratedIndex为true,则使用反射来寻找订阅方法信息,否则,先根据生成的索引文件来寻找订阅方法信息,索引文件中找不到,再使用反射寻找。</p><p>3、如果找到的订阅方法信息非空,则将这些信息缓存在METHOD_CACHE中。</p><p>第1、3步没有什么好讲的内容,主要的内容在第2步。由于根据索引文件寻找订阅方法信息的方式还涉及注解处理器,稍微复杂,所以会单独开一篇文章讲这块。本文主要介绍通过反射来寻找订阅方法信息的方式。</p><p>我们来看一下<strong>findUsingReflection</strong>的具体实现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {</span><br><span class="line"> FindState findState = prepareFindState();</span><br><span class="line"> findState.initForSubscriber(subscriberClass);</span><br><span class="line"> // 遍历订阅类以及其父类</span><br><span class="line"> while (findState.clazz != null) {</span><br><span class="line"> findUsingReflectionInSingleClass(findState);</span><br><span class="line"> findState.moveToSuperclass();</span><br><span class="line"> }</span><br><span class="line"> return getMethodsAndRelease(findState);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">private FindState prepareFindState() {</span><br><span class="line"> synchronized (FIND_STATE_POOL) {</span><br><span class="line"> for (int i = 0; i < POOL_SIZE; i++) {</span><br><span class="line"> FindState state = FIND_STATE_POOL[i];</span><br><span class="line"> if (state != null) {</span><br><span class="line"> FIND_STATE_POOL[i] = null;</span><br><span class="line"> return state;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return new FindState();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>prepareFindState</strong>()方法会尝试从缓存中寻找是否有现存的FindState对象,如果有,则直接使用缓存的FindState对象,否则,直接new一个FindState对象。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">void initForSubscriber(Class<?> subscriberClass) {</span><br><span class="line">// 保存订阅类信息</span><br><span class="line"> this.subscriberClass = clazz = subscriberClass;</span><br><span class="line"> // 是否忽略父类</span><br><span class="line"> skipSuperClasses = false;</span><br><span class="line"> subscriberInfo = null;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>initForSubscriber</strong>方法就是简单的初始化了一些参数。</p><p>接下来我们看下核心方法<strong>findUsingReflectionInSingleClass</strong>的实现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">private void findUsingReflectionInSingleClass(FindState findState) {</span><br><span class="line"> Method[] methods;</span><br><span class="line"> try {</span><br><span class="line"> // getDeclaredMethods()方法的效率要优于getMethods()</span><br><span class="line"> methods = findState.clazz.getDeclaredMethods();</span><br><span class="line"> } catch (Throwable th) {</span><br><span class="line"> methods = findState.clazz.getMethods();</span><br><span class="line"> findState.skipSuperClasses = true;</span><br><span class="line"> }</span><br><span class="line"> for (Method method : methods) {</span><br><span class="line"> // 获取方法的修饰符, 比如public, static等修饰符</span><br><span class="line"> int modifiers = method.getModifiers();</span><br><span class="line"> if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {</span><br><span class="line"> // 获取方法的参数信息</span><br><span class="line"> Class<?>[] parameterTypes = method.getParameterTypes();</span><br><span class="line"> // 方法只有一个参数</span><br><span class="line"> if (parameterTypes.length == 1) {</span><br><span class="line"> // 获取方法的@Subscribe注解信息</span><br><span class="line"> Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);</span><br><span class="line"> // 如果方法有@Subscribe注解信息</span><br><span class="line"> if (subscribeAnnotation != null) {</span><br><span class="line"> Class<?> eventType = parameterTypes[0];</span><br><span class="line"> // 检查当前method是否是有效的订阅方法</span><br><span class="line"> if (findState.checkAdd(method, eventType)) {</span><br><span class="line"> // 如果当前method是有效的订阅方法,则把该订阅方法收集起来</span><br><span class="line"> ThreadMode threadMode = subscribeAnnotation.threadMode();</span><br><span class="line"> findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {</span><br><span class="line"> String methodName = method.getDeclaringClass().getName() + "." + method.getName();</span><br><span class="line"> throw new EventBusException("@Subscribe method " + methodName +</span><br><span class="line"> "must have exactly 1 parameter but has " + parameterTypes.length);</span><br><span class="line"> }</span><br><span class="line"> } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {</span><br><span class="line"> String methodName = method.getDeclaringClass().getName() + "." + method.getName();</span><br><span class="line"> throw new EventBusException(methodName +</span><br><span class="line"> " is a illegal @Subscribe method: must be public, non-static, and non-abstract");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>findUsingReflectionInSingleClass方法加了详细的注解,大多数逻辑都能直接看代码注释弄明白。这里主要要介绍的是<strong>findState.checkAdd(method, eventType)</strong>方法,checkAdd的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">boolean checkAdd(Method method, Class<?> eventType) {</span><br><span class="line"> // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.</span><br><span class="line"> // Usually a subscriber doesn't have methods listening to the same event type.</span><br><span class="line"> Object existing = anyMethodByEventType.put(eventType, method);</span><br><span class="line"> if (existing == null) {</span><br><span class="line"> return true;</span><br><span class="line"> } else {</span><br><span class="line"> if (existing instanceof Method) {</span><br><span class="line"> if (!checkAddWithMethodSignature((Method) existing, eventType)) {</span><br><span class="line"> // Paranoia check</span><br><span class="line"> throw new IllegalStateException();</span><br><span class="line"> }</span><br><span class="line"> // Put any non-Method object to "consume" the existing Method</span><br><span class="line"> anyMethodByEventType.put(eventType, this);</span><br><span class="line"> }</span><br><span class="line"> return checkAddWithMethodSignature(method, eventType);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>考虑到同一个类里面通常只会有一个方法监听同一类事件,为了效率,所以EventBus先根据事件类型去匹配同一个类中是否已经有添加过的方法监听同一类事件,如果没有,则直接认为该方法为有效订阅方法,如果之前已经添加过方法监听同一类事件,则通过checkAddWithMethodSignature来判断该方法是否为有效订阅方法。<strong>checkAddWithMethodSignature</strong>的实现如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {</span><br><span class="line"> methodKeyBuilder.setLength(0);</span><br><span class="line"> methodKeyBuilder.append(method.getName());</span><br><span class="line"> methodKeyBuilder.append('>').append(eventType.getName());</span><br><span class="line"></span><br><span class="line"> String methodKey = methodKeyBuilder.toString();</span><br><span class="line"> Class<?> methodClass = method.getDeclaringClass();</span><br><span class="line"> Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);</span><br><span class="line"> // methodClassOld 是否是 methodClass的父类或者接口</span><br><span class="line"> if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {</span><br><span class="line"> // Only add if not already found in a sub class</span><br><span class="line"> return true;</span><br><span class="line"> } else {</span><br><span class="line"> // Revert the put, old class is further down the class hierarchy</span><br><span class="line"> subscriberClassByMethodKey.put(methodKey, methodClassOld);</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>根据一定规则计算出方法的唯一特征名称,如果具有该特征名称的方法之前还没有添加过,则认为当前方法为有效订阅方法。否则,如果之前添加过的方法所在的类是当前方法所在类的父类或者接口,考虑到子类重写,认为当前方法为有效订阅方法。否则认为当前方法为非有效订阅方法。</p><p>通过上面的多次调用<strong>findUsingReflectionInSingleClass</strong>方法,我们已经把有效的订阅信息收集到了findState的subscriberMethods变量中。</p><h2 id="保存所有订阅信息"><a href="#保存所有订阅信息" class="headerlink" title="保存所有订阅信息"></a>保存所有订阅信息</h2><p>我们再回到findUsingReflection方法,接着会调用<strong>getMethodsAndRelease(findState)</strong>方法,我们看看它的实现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {</span><br><span class="line"> List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);</span><br><span class="line"> findState.recycle();</span><br><span class="line"> synchronized (FIND_STATE_POOL) {</span><br><span class="line"> for (int i = 0; i < POOL_SIZE; i++) {</span><br><span class="line"> if (FIND_STATE_POOL[i] == null) {</span><br><span class="line"> FIND_STATE_POOL[i] = findState;</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return subscriberMethods;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>它其实做的事情就是把findState放入到缓存中,然后返回刚刚收集到的有效订阅信息。</p><p>现在我们回到<strong>register</strong>方法,我们收集到了有效的订阅信息List<subscribermethod>,接着会循环调用<strong>subscribe(subscriber, subscriberMethod)</strong>方法,我们看看这个方法的实现:</subscribermethod></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">// Must be called in synchronized block</span><br><span class="line">private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {</span><br><span class="line"> Class<?> eventType = subscriberMethod.eventType;</span><br><span class="line"> Subscription newSubscription = new Subscription(subscriber, subscriberMethod);</span><br><span class="line"> CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);</span><br><span class="line"> if (subscriptions == null) {</span><br><span class="line"> subscriptions = new CopyOnWriteArrayList<>();</span><br><span class="line"> subscriptionsByEventType.put(eventType, subscriptions);</span><br><span class="line"> } else {</span><br><span class="line"> if (subscriptions.contains(newSubscription)) {</span><br><span class="line"> throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "</span><br><span class="line"> + eventType);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 优先级高的放列表的前面</span><br><span class="line"> int size = subscriptions.size();</span><br><span class="line"> for (int i = 0; i <= size; i++) {</span><br><span class="line"> if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {</span><br><span class="line"> subscriptions.add(i, newSubscription);</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);</span><br><span class="line"> if (subscribedEvents == null) {</span><br><span class="line"> subscribedEvents = new ArrayList<>();</span><br><span class="line"> typesBySubscriber.put(subscriber, subscribedEvents);</span><br><span class="line"> }</span><br><span class="line"> subscribedEvents.add(eventType);</span><br><span class="line"></span><br><span class="line"> if (subscriberMethod.sticky) {</span><br><span class="line"> // 是否考虑注册方法的参数,事件的继承关系</span><br><span class="line"> if (eventInheritance) {</span><br><span class="line"> // Existing sticky events of all subclasses of eventType have to be considered.</span><br><span class="line"> // Note: Iterating over all events may be inefficient with lots of sticky events,</span><br><span class="line"> // thus data structure should be changed to allow a more efficient lookup</span><br><span class="line"> // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).</span><br><span class="line"> Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();</span><br><span class="line"> for (Map.Entry<Class<?>, Object> entry : entries) {</span><br><span class="line"> Class<?> candidateEventType = entry.getKey();</span><br><span class="line"> if (eventType.isAssignableFrom(candidateEventType)) {</span><br><span class="line"> Object stickyEvent = entry.getValue();</span><br><span class="line"> checkPostStickyEventToSubscription(newSubscription, stickyEvent);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> Object stickyEvent = stickyEvents.get(eventType);</span><br><span class="line"> checkPostStickyEventToSubscription(newSubscription, stickyEvent);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个函数的代码虽然很长,但是其只做了三件事情,1、将订阅信息以事件类型为key保存在subscriptionsByEventType变量中(方便post方法调用的时候,以事件为key寻找订阅信息);2、将事件类型以订阅类为key保存在typesBySubscriber变量中(方便unregister方法调用的时候,以订阅类为key寻找事件类型,然后根据subscriptionsByEventType变量找到订阅信息);3、处理sticky事件逻辑。关于第3点,考虑到本文篇幅,我将另外开一篇文章进行分析。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>所以通过调用<strong>register</strong>方法,最终订阅方法信息会被最终保存在<strong>subscriptionsByEventType</strong>变量和<strong>typesBySubscriber</strong>变量中。</p><p>如果你喜欢,感谢给个赞。如果有什么问题,欢迎评论。</p>]]></content>
<categories>
<category> EventBus3.0 </category>
</categories>
<tags>
<tag> EventBus3.0 </tag>
</tags>
</entry>
<entry>
<title>EventBus3.0源码解析-01</title>
<link href="/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-01/"/>
<url>/2019/01/08/EventBus3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-01/</url>
<content type="html"><![CDATA[<p>最近在学习EventBus源码,本着学习完之后要及时总结,方便以后回顾的心态,这里做一个系列来分析EventBus的源码。本系列讨论的都是最新的EventBus3.0的源码。<a href="https://github.com/greenrobot/EventBus" target="_blank" rel="noopener">EventBus GitHub地址</a> <a id="more"></a></p><p><strong>EventBus在gradle工程中引入的方式如下:</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">implementation 'org.greenrobot:eventbus:3.1.1'</span><br></pre></td></tr></table></figure><h1 id="EventBus的基本流程"><a href="#EventBus的基本流程" class="headerlink" title="EventBus的基本流程"></a>EventBus的基本流程</h1><p><img src="/2019/01/08/EventBus3源码解析-01/eventbus调用流程.png" alt="基本流程"></p><p>从官网流程图可以看出,EventBus使用的是观察者模式,Publisher(发布者)调用post方法将事件发送给订阅了该事件的Subscriber(订阅者)。</p><ul><li>Publisher(发布者)—调用post方法发布事件的类就是发布者</li><li>Subscriber(订阅者)—通过@Subscribe注解的方法就是订阅者</li><li>EventBus—负责把post方法发布的事件通知给相应的订阅者</li></ul><p><strong>EventBus的基本使用逻辑如下:</strong></p><ol><li><p>定义事件类型</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public static class MessageEvent { /* Additional fields if needed */ }</span><br></pre></td></tr></table></figure></li><li><p>通过@Subscriber准备订阅方法</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">@Subscribe(threadMode = ThreadMode.MAIN) </span><br><span class="line">public void onMessageEvent(MessageEvent event) {/* Do something */};</span><br></pre></td></tr></table></figure><p>其中@Subscriber可以指定三个参数:</p><ul><li><p>ThreadMode—指定订阅方法(上文中的onMessageEvent方法)运行在什么线程,默认是POSTING;</p><p>POSTING:表示订阅方法运行在当前发布事件的线程(调用post方法的线程);</p><p>MAIN:表示订阅方法运行在UI线程。如果当前发布事件的线程是UI线程,则订阅方法直接运行,否则,事件入队列等待触发订阅方法(入队列的方式不阻塞线程);</p><p>MAIN_ORDERED:表示订阅方法运行在UI线程。它和MAIN的区别是,事件会直接入队列等待触发订阅方法;</p><p>BACKGROUND:表示订阅方法运行在后台线程。如果当前发布事件的线程是后台线程,则订阅方法直接运行,否则,订阅方法将会运行在一个后台线程中,该后台线程没有停止的话将会负责运行所有有BACKGROUND标识的订阅方法;</p><p>ASYNC:表示订阅方法将总是运行在一个新的后台线程,即即使当前发布事件的线程是后台线程,它也会运行在一个新的后台线程。</p></li><li><p>priority—表示该方法的优先级,默认是0。当发布事件的时候,同一线程中,优先级值越大的方法,会被更早调用。<strong>注意,sticky事件除外,sticky事件是通过反射先被找到的订阅方法先被通知</strong>。</p></li><li><p>sticky—如果为true,将会发送最新的sticky事件给相应的订阅者,默认为false;</p><p>sticky的使用一般为:如果用户希望先调用postSticky发布事件,再注册订阅者接收该事件,则可以指定sticky为true。</p></li></ul></li><li><p>注册和取消注册包含订阅方法的订阅者</p><p>比如在Activity中,方式一般如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line"> public void onStart() {</span><br><span class="line"> super.onStart();</span><br><span class="line"> EventBus.getDefault().register(this);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> public void onStop() {</span><br><span class="line"> super.onStop();</span><br><span class="line"> EventBus.getDefault().unregister(this);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> // 订阅方法</span><br><span class="line"> @Subscribe(threadMode = ThreadMode.MAIN) </span><br><span class="line"> public void onMessageEvent(MessageEvent event) {/* Do something */};</span><br></pre></td></tr></table></figure></li><li><p>发布事件</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EventBus.getDefault().post(new MessageEvent());</span><br></pre></td></tr></table></figure></li></ol><p>通过上文,我们知道eventbus的基本使用流程主要有四个步骤,而其中涉及eventbus的主要有三个方法,分别为register注册订阅者,unregister取消注册,post发布事件。后续我将分别以这三个方法为入口,对eventbus源码进行解析,敬请期待。</p>]]></content>
<categories>
<category> EventBus3.0 </category>
</categories>
<tags>
<tag> EventBus3.0 </tag>
</tags>
</entry>
<entry>
<title>android开发-常用网址</title>
<link href="/2019/01/05/android%E5%BC%80%E5%8F%91-%E5%B8%B8%E7%94%A8%E7%BD%91%E5%9D%80/"/>
<url>/2019/01/05/android%E5%BC%80%E5%8F%91-%E5%B8%B8%E7%94%A8%E7%BD%91%E5%9D%80/</url>
<content type="html"><![CDATA[<h5 id="android-development-网址"><a href="#android-development-网址" class="headerlink" title="android development 网址"></a>android development 网址</h5><ul><li><a href="https://developer.android.com/guide/" target="_blank" rel="noopener">https://developer.android.com/guide/</a></li></ul><h5 id="AOP学习:"><a href="#AOP学习:" class="headerlink" title="AOP学习:"></a>AOP学习:</h5><ul><li><a href="https://blog.csdn.net/innost/article/details/49387395" target="_blank" rel="noopener">https://blog.csdn.net/innost/article/details/49387395</a></li><li><a href="http://www.eclipse.org/aspectj/doc/released/quick5.pdf" target="_blank" rel="noopener">http://www.eclipse.org/aspectj/doc/released/quick5.pdf</a></li><li><a href="http://www.eclipse.org/aspectj/doc/released/progguide/semantics.html" target="_blank" rel="noopener">http://www.eclipse.org/aspectj/doc/released/progguide/semantics.html</a></li><li><a href="http://www.eclipse.org/aspectj/doc/released/runtime-api/index.html" target="_blank" rel="noopener">http://www.eclipse.org/aspectj/doc/released/runtime-api/index.html</a></li></ul>]]></content>
</entry>
<entry>
<title>安卓注解处理器-processor</title>
<link href="/2019/01/04/%E5%AE%89%E5%8D%93%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8-processor/"/>
<url>/2019/01/04/%E5%AE%89%E5%8D%93%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8-processor/</url>
<content type="html"><![CDATA[<p>最近在学习安卓开源框架发现,很多的开源框架都使用到了注解处理器,例如EventBus3.0。本文通过一个简单的Demo来介绍如何使用注解处理器。Demo链接为<a href="https://github.com/cugyong/AnnotationProcessor-sample" target="_blank" rel="noopener">https://github.com/cugyong/AnnotationProcessor-sample</a> ,如果喜欢的话,欢迎大家给star。<a id="more"></a></p><h2 id="Demo需求描述"><a href="#Demo需求描述" class="headerlink" title="Demo需求描述"></a>Demo需求描述</h2><p>用户通过执行一个传入参数为A(类对象)的静态方法,该方法会最终把参数A中加了特定注解的所有方法执行一遍。</p><h2 id="需求实现"><a href="#需求实现" class="headerlink" title="需求实现"></a>需求实现</h2><p><img src="/2019/01/04/安卓注解处理器-processor/框架流程.png" alt="框架流程"></p><p>整个项目分为四个部分:</p><ul><li>注解–要使用的注解类型,这部分通常也可以放在lib中;</li><li>注解处理器–要对注解进行处理的逻辑,包括收集有特定注解类型的方法信息以及生成特定的java文件;</li><li>lib–封装合适的接口,供具体调用方调用;</li><li>sample–具体的调用方逻辑。</li></ul><p>首先新建一个安卓工程,点击运行展示的是hello world。</p><h3 id="注解"><a href="#注解" class="headerlink" title="注解"></a>注解</h3><p>在上述工程中new->Module->Java Library,新建一个Java Library Module,命名为annotation。在该Module下创建一个文件AnnotationTest.java,</p><p><img src="/2019/01/04/安卓注解处理器-processor/annotation.png" alt="annotation"></p><p>AnnotationTest.java里面代码如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@Retention(RetentionPolicy.CLASS)</span><br><span class="line">@Target(ElementType.METHOD)</span><br><span class="line">public @interface AnnotationTest {</span><br><span class="line"> String name() default "test";</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>1、注解@Retention按生命周期来划分可分为3类:</p><ul><li>RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;</li><li>RetentionPolicy.CLASS:注解被保留到class文件,当jvm加载class文件时候被遗弃,这是默认的生命周期;</li><li>RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。</li></ul><p>这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。</p><p>2、注解@Target表示修饰的注解能使用的范围,ElementType.METHOD表示@AnnotationTest注解只能作用在方法上。</p><h3 id="注解处理器"><a href="#注解处理器" class="headerlink" title="注解处理器"></a>注解处理器</h3><p>参照上部分,在工程中new->Module->Java Library,新建一个Java Library Module, 在该Module下创建一个文件ProcessorTest.java。在该Module下的build.gradle的dependencies中添加如下配置:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 自动为processor注册</span><br><span class="line">implementation 'com.google.auto.service:auto-service:1.0-rc2'</span><br><span class="line">// 该Module依赖上部分建立的annotation Module</span><br><span class="line">implementation project(':annotation')</span><br></pre></td></tr></table></figure><p>com.google.auto.service:auto-service:1.0-rc2依赖的作用是为注解处理器自动注册,它会生成META-INF文件夹。</p><p>注解处理器ProcessorTest的定义如下,其中@AutoService(Processor.class)就是build.gradle中加的依赖帮助其自动注册。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">@AutoService(Processor.class) // 自动为ProcessorTest注册,生成META-INF文件</span><br><span class="line">public class ProcessorTest extends AbstractProcessor{</span><br></pre></td></tr></table></figure><p>注解处理器ProcessorTest主要包含以下几个部分:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public synchronized void init(ProcessingEnvironment processingEnvironment) {</span><br><span class="line"> super.init(processingEnvironment);</span><br><span class="line"></span><br><span class="line"> mMessager = processingEnvironment.getMessager();</span><br><span class="line"> mFiler = processingEnvironment.getFiler();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>init方法是注解处理器会自动调用的初始化方法,其中mFiler是用来生成java源文件的工具,mMessager是用来打印日志的,它们的具体使用会在后面介绍。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public Set<String> getSupportedAnnotationTypes() {</span><br><span class="line"> Set<String> supportAnnotationTypes = new HashSet<>();</span><br><span class="line"> supportAnnotationTypes.add(AnnotationTest.class.getCanonicalName());</span><br><span class="line"> return supportAnnotationTypes;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>getSupportedAnnotationTypes()方法返回该注解处理器支持的注解类型,这里返回的就是我们之前声明的新的注解类型@AnnotationTest。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public SourceVersion getSupportedSourceVersion() {</span><br><span class="line"> return SourceVersion.latestSupported();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>getSupportedSourceVersion()方法一般就按照上述实现就行。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {</span><br><span class="line">// 打印日志</span><br><span class="line"> mMessager.printMessage(Diagnostic.Kind.NOTE, "process start");</span><br><span class="line"> Map<String, List<String>> collectInfos = new HashMap<>();</span><br><span class="line"> for (TypeElement annotation: annotations){</span><br><span class="line"> Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation);</span><br><span class="line"> for (Element element: elements){</span><br><span class="line"> // 检查element是否符合我们定义的规范</span><br><span class="line"> if (!checkValid(element)){</span><br><span class="line"> mMessager.printMessage(Diagnostic.Kind.NOTE, "checkValid not pass");</span><br><span class="line"> return false;</span><br><span class="line"> }else {</span><br><span class="line"> ExecutableElement executableElement = (ExecutableElement) element;</span><br><span class="line"> // 获取被注解的方法所在的类</span><br><span class="line"> TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();</span><br><span class="line"> // 获取类的全名,包括包名</span><br><span class="line"> String classFullName = typeElement.getQualifiedName().toString();</span><br><span class="line"> // 被注解的方法的名字</span><br><span class="line"> String methodName = executableElement.getSimpleName().toString();</span><br><span class="line"> List<String> methods = collectInfos.get(classFullName);</span><br><span class="line"> if (methods == null){</span><br><span class="line"> methods = new ArrayList<>();</span><br><span class="line"> collectInfos.put(classFullName, methods);</span><br><span class="line"> }</span><br><span class="line"> methods.add(methodName);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for (Map.Entry<String, List<String>> entry: collectInfos.entrySet()){</span><br><span class="line"> mMessager.printMessage(Diagnostic.Kind.NOTE, entry.getKey());</span><br><span class="line"> // 生成java源文件</span><br><span class="line"> createJavaFile(entry.getKey(), entry.getValue());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return true;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>process方法是我们的主要逻辑处理的地方,主要逻辑就是收集所有有@AnnotationTest注解的方法以及其所在的类信息,然后根据每个类信息,生成一个新的类文件,并在新的类文件的特定方法中调用所有关联的注解方法。生成java源文件将使用Filer对象,具体如何使用请下载demo看源代码。</p><p><strong>注:</strong> </p><p>1、当你点击buid project时,注解处理器将会执行,而Messager对象打印出来的日志信息可以在Gradle Console窗口中看到。</p><p>2、如果你在该Module中使用中文注解,因为该Module为java library,可能会报GBK编码错误,解决办法是在该Module的build.gradle中添加如下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//指定编译的编码</span><br><span class="line">tasks.withType(JavaCompile){</span><br><span class="line"> options.encoding = "UTF-8"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Lib"><a href="#Lib" class="headerlink" title="Lib"></a>Lib</h3><p>在工程中new->Module->Android Library ,新建一个Android Library Module,封装接口给调用方使用,具体逻辑请参考demo。</p><p>最终该demo的功能是点击Hello world文字,会依此执行MainActivity中使用@AnnotationTest注解的方法。</p>]]></content>
<categories>
<category> 注解 </category>
</categories>
<tags>
<tag> annotation、processor、自动生成java文件 </tag>
</tags>
</entry>
</search>