├── md ├── android │ ├── base │ │ ├── images │ │ │ ├── 1. 生命周期-1.gif │ │ │ ├── 7. 顶级 View 的: DecorView 的结构-2.jpg │ │ │ └── 7. View 绘制 - performTraversals 工作原理-1.jpg │ │ ├── 1. 生命周期.md │ │ ├── 5. View 滑动相关.md │ │ ├── 7. View 的滑动冲突.md │ │ ├── 3. 跨进程通信 方式 及 问题.md │ │ ├── 4. Socket.md │ │ ├── 8. View 的工作原理.md │ │ ├── 2. Activity 启动模式.md │ │ ├── 9. 自定义 View 分类 和 须知.md │ │ ├── 6. View 的事件分发机制.md │ │ └── 10. RemoteViews.md │ ├── test │ │ ├── 1. 为什么要做单元测试.md │ │ └── 2. 测试的内容.md │ ├── framework │ │ ├── Android 系统特点.md │ │ ├── Android Framework 概述.md │ │ ├── Android APK 的运行过程.md │ │ ├── Android 代码编译和 APK 安装过程.md │ │ ├── Binder.md │ │ ├── Binder AIDL.md │ │ └── Looper Handler MessageQueue.md │ └── LruCache 源码解析.md └── java │ └── sort │ ├── 冒泡排序.md │ ├── 快速排序.md │ ├── 希尔排序.md │ ├── 插入排序.md │ └── 选择排序.md ├── .gitignore ├── LICENSE ├── code └── android │ ├── framework │ └── otto │ │ ├── Produce.java │ │ ├── HandlerFinder.java │ │ ├── Subscribe.java │ │ ├── DeadEvent.java │ │ ├── ThreadEnforcer.java │ │ ├── EventProducer.java │ │ ├── EventHandler.java │ │ ├── AnnotatedHandlerFinder.java │ │ └── Bus.java │ └── classes │ └── LruCache.java ├── 10. RemoteViews.md └── README.md /md/android/base/images/1. 生命周期-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaMnter/SaveAndroidResources/HEAD/md/android/base/images/1. 生命周期-1.gif -------------------------------------------------------------------------------- /md/android/base/images/7. 顶级 View 的: DecorView 的结构-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaMnter/SaveAndroidResources/HEAD/md/android/base/images/7. 顶级 View 的: DecorView 的结构-2.jpg -------------------------------------------------------------------------------- /md/android/base/images/7. View 绘制 - performTraversals 工作原理-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaMnter/SaveAndroidResources/HEAD/md/android/base/images/7. View 绘制 - performTraversals 工作原理-1.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # Android Studio Navigation editor temp files 29 | .navigation/ 30 | 31 | # Android Studio captures folder 32 | captures/ 33 | -------------------------------------------------------------------------------- /md/android/test/1. 为什么要做单元测试.md: -------------------------------------------------------------------------------- 1 | 1. 为什么要做单元测试 2 | == 3 | 4 |
5 | 6 | ## 1.1 后期重构 7 | 8 | 用单元测试最好覆盖程序中的每一项功能的正确性,这样就算到了开发的后期,也可以有保障地添加功能或修改程序结构,而不用担心这个过程中会破坏原来的功能。因为,**单元测试为代码的重构提供了保障,只要重构代码之后单元测试全部运行通过**。那么,在很大成都上表示这次重构没有引入新的 **Bug**,当然这是建立在完整、有效的单元测试覆盖率的基础上。 9 | 10 |
11 | 12 | ## 1.2 优化设计 13 | 14 | 编写单元测试将使用户从调用者的角度观察、思考,特别是使用 TDD(测试驱动开发)的方式,**迫使设计者把程序设计成易于调用和可测性,并且进行了解耦**。 15 | 16 |
17 | 18 | ## 1.3 文档记录 19 | 20 | 单元测试是一种无价的文档,它是**展示方法函数和类如何使用的最佳文档**。这份文档是**可编译、可运行的**,并且它保持**最新,永远和代码同步**。 21 | 22 |
23 | 24 | ## 1.4 具有回归性 25 | 26 | 自动化的单元测试避免了代码出现回归,编写完成之后,可以随时**快速运行测试代码**。而**不是将新代码部署到各个需要的地方,也不是要部署运行在设备上**,然后再手动覆盖业务执行逻辑路径,这样的行为效率**低下、浪费时间**。 -------------------------------------------------------------------------------- /md/android/base/1. 生命周期.md: -------------------------------------------------------------------------------- 1 | 1. 生命周期 2 | == 3 | 4 | 5 |
6 | 7 | 8 | **onCreate:** 表示 Activity 正在被创建,生命周期的第一个方法。可以走一些初始化的工作( `setContentView(...)` 等 )。 9 | 10 |
11 | 12 | **onRestart:** 表示 Activity 正在重新启动。从 不可见状态 >> 可见状态。 13 | 14 |
15 | 16 | **onStart:** 表示 Activity 正在启动。此时,已经可见了,但是没有出现在前台,无法进行交互。已经显示了,但看不到。 17 | 18 |
19 | 20 | **onResume:** 表示 Activity 已经可见了。效果已经出现在前台。 21 | 22 |
23 | 24 | **onPause:** 表示 Activity 正在停止。紧接着 **onStop** 就会被调用。注意:这里可以做一些存储数据、停止动画等工作,但是不能**耗时**。会影响 新 Activity 的显示,因为 **onPause** 必须执行完,新的 Activity 的 **onResume** 才会执行。 25 | 26 |
27 | 28 | **onStop:** 表示 Activity 即将停止,可以做一些 **稍微重量级的回收工作**,也不能太耗时。 29 | 30 |
31 | 32 | **onDestory:** 表示 Activity 即将被销毁。生命周期的最后一个方法,可做 回收工作和最终的资源释放。 33 | -------------------------------------------------------------------------------- /md/android/base/5. View 滑动相关.md: -------------------------------------------------------------------------------- 1 | 5. View 滑动相关 2 | == 3 | 4 |
5 | 6 | ## 1. View 滑动方式 7 | 8 |
9 | 10 | ### 1.1 scrollTo / scrollBy 11 | 12 | **特点:** 只能将 View 的内容进行移动,并不能将 View 本身进行移动。 13 | 14 |
15 | 16 | ## 1.2 动画 17 | 18 | 通过 View 动画 或者 属性动画。但是,**要兼容 3.0 以下的问题 还需要解决( nineoldandroids )**。 19 | 20 |
21 | 22 | ## 1.3 修改布局参数 23 | 24 | ```java 25 | MarginLayoutParams params = (MarginLayoutParams)button.getLayoutParams(); 26 | ``` 27 | 28 |
29 | 30 | ## 1.4 方式对比 31 | 32 | - **scrollTo / scrollBy:** 操作简单,适合对 View 内容滑动。 33 | - **动画:** 操作简单,主要适用于没有交互的 View 和实现复杂的动画效果。 34 | - **修改布局参数:** 操作复杂,适合有交互的 View 35 | 36 |
37 | 38 | # 2. View 弹性滑动方式 39 | 40 | - **Scroller** 41 | - **动画:** ObjectAnimator, ValueAnimator 等 ... 42 | - **延时策略:** Handler 的 delayed 等 ... -------------------------------------------------------------------------------- /md/android/base/7. View 的滑动冲突.md: -------------------------------------------------------------------------------- 1 | 7. View 的滑动冲突 2 | == 3 | 4 | 5 |
6 | 7 | ## 1. 滑动冲突场景 8 | 9 | - **外部滑动方向和内部滑动方向不一致。** 这种场景主要是在 **ViewPager + Fragment** 组合的页面滑动效果,`Fragment` 中往往放的是一个 `ListView` 或者 `RecyclerView`。由于,`ViewPager` 内部已经处理了这种滑动冲突,所以无需关心。但是换成 `ScrollView` 的话,就有问题了。 10 | 11 | - **外部滑动方向和内部滑动方向一致。** 这种场景发生在 **ScrollView + EditText** 的场景。`ScrollView` 会拦截事件,造成 `EditText` 无法滚动。 12 | 13 | - **以上两种情况的嵌套。** 内层存在 **第一种冲突**,外层存在 **第二种冲突**。 14 | 15 | 16 | 17 |
18 | 19 | ## 2. 滑动冲突的解决办法 20 | 21 |
22 | 23 | ### 2.1 外部拦截法 24 | 25 | **外部拦截法:** 点击事件都经过 **父容器** 的拦截处理。 26 | 27 | 需要覆写 **父容器** 的 `onInterceptTouchEvent` 方法。 28 | 29 |
30 | 31 | ## 2.2 内部拦截法 32 | 33 | **内部拦截法:** 是 **父容器** 不拦截任何事件,所有的事件都传递给 **子元素**,如果 **子元素** 需要此事件就直接消费掉,否则就交给 **父容器** 进行处理。 34 | 35 | 需要配合 `requestDisallowInterceptTouchEvent` 方法才能进行处理。同时要覆写 子元素的 `dispatchTouchEvent` 方法 。 -------------------------------------------------------------------------------- /md/android/test/2. 测试的内容.md: -------------------------------------------------------------------------------- 1 | 2. 测试的内容 2 | == 3 | 4 | > 测试无非是保证程序的正确性。 5 | 6 |
7 | 8 | 9 | ## 边界条件 10 | 11 |
12 | 13 | ### 一致性 14 | 15 | 值是否和期望一致。可以理解为当输入并不是期望的标准数据时,被测试方法是否可以正确输出期望结果或者抛出异常。 16 | 17 | 例如:要实现一个加法功能,有两个 **EditText** 分别输入两个整型数字,但当用户输入的不是整型而是文字时如何处理? 18 | 19 |
20 | 21 | ### 有序性 22 | 23 | 值是否像期望一样是无序或者有序的。 24 | 25 |
26 | 27 | ### 区间性 28 | 29 | 值是否位于合理的最小值和最大值之间。 30 | 31 | 例如:园的角度为 1~360 度,当输入 400 度或者 0 度的时候如何处理? 32 | 33 |
34 | 35 | ### 依赖性 36 | 37 | 代码是否引用了一些不在代码本身控制范围之内的外部资源,当这些外部资源存在或不存在的时候,代码是否可以产生相应的预期结果。 38 | 39 | 例如:需要将图片缓存到 **SD** 卡中,如果 **SD** 卡被移除或者没有 **SD** 卡,如何处理? 40 | 41 |
42 | 43 | ### 存在性 44 | 45 | 值是否存在,测试方法是否可以处理值不存在的情况。 46 | 47 | 例如:对象为 **null** ,如何处理? 48 | 49 |
50 | 51 | ### 基数性 52 | 53 | 是否恰好有足够的值。这里的基数指的是计数,测试方法是否可以正确计数,并检查最后的计数值。 54 | 55 |
56 | 57 | ### 时间性 58 | 59 | 所有事情的发生是否是有序的、是否在正确的时刻、是否恰好及时。与时间相关问题有:相对时间(时间上的顺序)、绝对时间(消耗的时间和时钟上的时间)、并发问题。 60 | 61 | 例如:方法的调用顺序、代码超时、不同的本地时间、多线程同步等。 62 | 63 |
64 |
65 | 66 | ## 覆盖执行路径 67 | 68 | >软件是由多条执行路径组成的。 69 | 70 | 基本上,不存在只有一条执行路径的软件。**覆盖所有执行路径是保证程序正确性的重要一环**。 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yuanyu Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /md/android/base/3. 跨进程通信 方式 及 问题.md: -------------------------------------------------------------------------------- 1 | 3. 跨进程通信 方式 及 问题 2 | == 3 | 4 |
5 | 6 | ## 方式 7 | 8 | - **1. Intent** 9 | - **2. 共享文件** 10 | - **3. Messenger** 11 | - **5. AIDL** 12 | - **6. ContentProvider** 13 | - **7. Socket** 14 | 15 |
16 | 17 | ## 问题 18 | 19 | - **1. 静态成员 和 单例模式 完全失效** 20 | - **2. 线程同步机制 完全失效** 21 | - **3. SharedPreferences 可能性下降**:SharedPreferences 的读写有缓存机制,内存中会有 SharedPreferences 文件的缓存。在多进程的情况下,读写会不可靠。并且高并发的情况下,很大几率丢失数据。 22 | - **4. Application 会创建多次**:一个组建跑在独立的进程中,系统要为其创建新的进程的同时,分配独立的虚拟机。**实质上,就是启动一个应用的过程,自然会创建 Application**( **运行在不同进程中的组件是属于两个不同的虚拟机和 Application 的** )。 23 | 24 |
25 | 26 | ## 区别 27 | 28 | | 方式 | 优点 | 缺点 | 适用场景 | 29 | | :--- | :--- | :--- | :--- | 30 | | Bundle | 简单易用 | 只能传输 Bundle 支持的数据类型 | 四大组件之间的进程通信 | 31 | | 共享文件 | 简单易用 | 不适合高并发场景,并且无法做到进程间的即使通信 | 没有并发的环境,交换简单的数据实时性不高的场景 | 32 | | ADIL | 功能强大,支持一对多的并发通信,也支持实时通信 | 使用稍微复杂,需要处理好线程同步 | 一对多的通信,并且有 RPC 需求 | 33 | | Messenger | 功能一般,支持一对多串行通信,支持实时通信 | 不能很好处理高并发场景,也不支持 RPC ,数据通过 Message 进行传输。所以,只传输 Bundle 支持的数据类型 | 低并发的一对多即使通信,无 RPC 需求,或者无须要返回结果的 RPC 需求 | 34 | | ContentProvider | 在数据源访问方面功能强大,支持一对多并发数据共享,可以通过 Call 方法扩展其他操作 | “受约束的 AIDL”,主要提供数据源的 “增删改查” 操作 | 一对多的进程间的数据共享 | 35 | | Socket | 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 | 实现细节繁琐,不能支持直接的 RPC | 网络数据交换 | 36 | -------------------------------------------------------------------------------- /md/android/base/4. Socket.md: -------------------------------------------------------------------------------- 1 | 4. Socket 2 | == 3 | 4 | > Socket 成为 "套接字"。 5 | 6 |
7 | 8 | ## 1. Android 与 服务端的 通信 9 | 10 | 11 | Android 与 服务端的 通信主要分为两种: 12 | - **Http** 。 13 | - **Socket** 。 14 | 15 | ### 1.1 Http 与 Socket 的 区别 16 | 17 | **Http:** 连接使用的是 **“请求 - 响应”** 的 方式。在请求时简历连接通道,当客户端向服务端发送请求后,服务端才能向客户端返回数据。 18 | 19 | > Socket 是一种抽象层。通过 Socket 来发送和接受数据,可以将应用程序添加到网络中,与处在同一网络内的其他应用程序进行通信。总结,Socket 提供了 程序内部 与 外界通信 的端口,并为通信双方的提供了数据传输通道。 20 | > Socket 通信的话,会在程序内部提供与外部通信的端口,称为 端口通信。通过建立 Socket 连接,为通信双方的数据传输提供通道。 21 | > Socket 特点:数据丢失率低,简单,易于移植。 22 | 23 | **Socket:** 在双方建立连接后,就可以直接进行数据的传输。连接时,可以实现 **服务端** 信息的 **主动推送**,而不需要每次由 客户端 向 服务端 发送请求。 24 | 25 |
26 | 27 | ## Socket 分为 TCP 和 UDP 28 | 29 | **TCP 协议:** **面向连接** 的协议,提供 **稳定** 的 **双向** 通信功能,TCP 连接的建立需要经过 **“三次握手”** 才能完成,为了提供稳定数据传输功能,其本身也有 **超时重传机制**。因此,具有很高的稳定性。 **对应的 Java 类**:**Socket** 。 30 | 31 | **UDP 协议:** **无连接** 的协议,提供 **不稳定** 的 单向 通信功能,额外开销小,效率高。UDP 也能实现双向通信功能。**性能** 上,UDP 具有 **更好的效率**。**缺点**:**不能保证数据一定能传输到,特别是在网络环境差的情况下**。**对应的 Java 类**:**DatagramSocket** 。 32 | 33 |
34 | 35 | ## 总结 36 | 37 | 在 **UDP 方式** 下,**Android 端** 和 **服务端** 的接收可以看出:**Android 端** 和 **服务端** 的发送和接收大庭相径( 端口号 和 IP 对应了,通信就没问题 )。 38 | 39 | 40 | **TCP** 使用的是 **流方式** 发送。 41 | **UDP** 使用的是 **(数据报)包方式** 发送。 -------------------------------------------------------------------------------- /code/android/framework/otto/Produce.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.camnter.otto; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Marks a method as an instance producer, as used by {@link AnnotatedHandlerFinder} and {@link Bus}. 26 | *

27 | * Otto infers the instance type from the annotated method's return type. Producer methods may return null when there is 28 | * no appropriate value to share. The calling {@link Bus} ignores such returns and posts nothing. 29 | * 30 | * @author Jake Wharton 31 | */ 32 | @Retention(RetentionPolicy.RUNTIME) 33 | @Target(ElementType.METHOD) 34 | public @interface Produce { 35 | } 36 | -------------------------------------------------------------------------------- /code/android/framework/otto/HandlerFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.camnter.otto; 18 | 19 | import java.util.Map; 20 | import java.util.Set; 21 | 22 | /** 23 | * Finds producer and subscriber methods. 24 | */ 25 | interface HandlerFinder { 26 | 27 | Map, EventProducer> findAllProducers(Object listener); 28 | 29 | Map, Set> findAllSubscribers(Object listener); 30 | 31 | 32 | HandlerFinder ANNOTATED = new HandlerFinder() { 33 | @Override 34 | public Map, EventProducer> findAllProducers(Object listener) { 35 | return AnnotatedHandlerFinder.findAllProducers(listener); 36 | } 37 | 38 | @Override 39 | public Map, Set> findAllSubscribers(Object listener) { 40 | return AnnotatedHandlerFinder.findAllSubscribers(listener); 41 | } 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /code/android/framework/otto/Subscribe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Guava Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.camnter.otto; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Marks a method as an event handler, as used by {@link AnnotatedHandlerFinder} and {@link Bus}. 26 | *

27 | *

The method's first (and only) parameter defines the event type. 28 | *

If this annotation is applied to methods with zero parameters or more than one parameter, the object containing 29 | * the method will not be able to register for event delivery from the {@link Bus}. Otto fails fast by throwing 30 | * runtime exceptions in these cases. 31 | * 32 | * @author Cliff Biffle 33 | */ 34 | @Retention(RetentionPolicy.RUNTIME) 35 | @Target(ElementType.METHOD) 36 | public @interface Subscribe { 37 | } 38 | -------------------------------------------------------------------------------- /code/android/framework/otto/DeadEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Guava Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.camnter.otto; 18 | 19 | /** 20 | * Wraps an event that was posted, but which had no subscribers and thus could not be delivered. 21 | * 包装发布一个事件,但是没有订阅者,因此它不能被传递 22 | *

23 | *

Subscribing a DeadEvent handler is useful for debugging or logging, as it can detect misconfigurations in a 24 | * system's event distribution. 25 | * 26 | * @author Cliff Biffle 27 | */ 28 | public class DeadEvent { 29 | 30 | public final Object source; 31 | public final Object event; 32 | 33 | /** 34 | * Creates a new DeadEvent. 35 | * 36 | * @param source object broadcasting the DeadEvent (generally the {@link Bus}). 37 | * @param event the event that could not be delivered. 38 | */ 39 | public DeadEvent(Object source, Object event) { 40 | this.source = source; 41 | this.event = event; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /md/android/framework/Android 系统特点.md: -------------------------------------------------------------------------------- 1 | Android 系统特点 2 | == 3 | 4 | ## 1.Android 内核系统 5 | 6 | Android 是一个 **Linux** 系统,是基于 **Linux** 内核开发的系统。 7 | 8 |
9 | 10 | ## 2. Android 系统与 Linux 系统的区别 11 | 12 | **Android 系统使用的 Linux 内核,其实与传统的 Linux 内核并无多大区别。** 13 | 14 | **区别:** 15 | 16 | - **1.** Android 在传统的 **Linux 内核** 中以模块的形式加入了一些专用的驱动,例如 **日志驱动 Logger、匿名共享内存驱动 Ashmem、进程间通信驱动 Binder**。 17 | 18 | - **2.** **Android 系统自己有一套独特的用户空间运行时,也就是应用程序框架。** Android 系统将在传统的 Linux 内核实现的硬件驱动程序划分成了两个模块,一块是 **内核实现**,另一块是 **用户空间实现**,也就是 **硬件抽象层 HAL**。这样划分,**是出于商业考虑,而不是技术考虑**。因为 **Linux 内核** 使用的 **GPL** 许可协议,驱动全部放在内核实现就意味着需要全部开源代码,**用户空间** 使用的是 Apache License,可以不开源代码。通过这样的方式,就可以保护厂家的商业利益,因为这些代码通常都会包含有硬件的相关参数。 19 | 20 |
21 | 22 | ## 3. Android 开源工程 23 | 24 | Android 应用程序框架中,包含了大量开源工程。**浏览器用的 内核 WebKit**、**管理 Wi-Fi 网络的 wap_supplicant**、**播放音乐视频的 StageFright**、**Android 系统专用的渲染 UI 的 SurfaceFlinger**、**播放声音的 AudioFlinger**,**实现了最基本的功能,都是用 C/C++ 写的**。 25 | 26 |
27 | 28 | ## 4. Android 守护进程服务 29 | 30 | 上面的 Android 开源工程实现了最基本的功能,这些功能被 Java 层的一些守护进程服务用到。比如:**组件管理服务 ActivityManagerService**、**应用程序安装服务 PackageManagerService**、**网络连接服务 ConnectivityService** 等。 31 | 32 |
33 | 34 | ### 5. Android 系统层次结构 35 | 36 | >Android 系统 = Android = Linux Kernel + C/C++ Runtime Framework + Java Runtime Framework + Virtual Machine ( Davik or Art ) 37 | 38 | >调用 Android SDK 提供的一个 API 后,这个 API 交给 Java Runtime Framework 处理,Java Runtime Framework 又会将这个 API 交给 C/C++ Runtime Framework 处理,最后 C/C++ Runtime Framework 又有可能接着将这个 API 调用交给 Linux 内核来处理。 39 | 40 | Java 层只是位于 Android 最上面的一层编程接口。**除了 Android 自带的 SDK 外,还能使用 NDK 绕开 Java Runtime Framework,将 API 交给 C/C++Runtime Framework 处理。** 41 | 42 |
43 | -------------------------------------------------------------------------------- /md/android/base/8. View 的工作原理.md: -------------------------------------------------------------------------------- 1 | 8. View 的工作原理 2 | == 3 | 4 | 5 | 6 | 7 |
8 | 9 | ## 1. ViewRoot 10 | 11 | **ViewRoot** 对应于 **ViewRootImpl** 类,是连接 **WindowManager** 和 **DecorView** 的纽带,**View** 的三大流程都通过 **ViewRoot** 来完成的。在 **ActivityThread** 中,当 **Activity** 对象被创建完毕后,会将 **DecorView** 添加到 **Window** 中,同时会创建 **ViewRootImpl** 对象,然后将 **ViewRootImpl** 与 **DecorView** 关联起来。 12 | 13 |
14 | 15 | ## 2. View 的绘制流程 16 | 17 | **View** 的绘制流程是从 `performTraversals` 方法开始的。经过了 `measure`,`layout` 和 `draw` 三个过程才能最终将一个 View 绘制出来。 18 | 19 | - **measure:** 用来测量 View 的宽高。 20 | 21 | - **layout:** 用来确定 View 在父容器中的放置位置。 22 | 23 | - **draw:** 负责将 View 绘制在屏幕上。 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | `performTraversals` 方法会依次调用 `performMesaue`,`performLayout` 和 `performDraw` 方法,去完成 **顶层 View** 的 `measure`,`layout` 和 `draw` 方法。 32 | 33 | `performMesaue` 中会调用 `measure` 去进行基本测量,然后调用到 `onMeasure` 方法里,`onMeasure` 方法中则会对所有 **子元素** 进行 `measure` 过程。如此反反复复下去,就完成了整个 **View 树** 的遍历。 34 | 35 | `performLayout` 方法和上面类似。唯独 `performDraw` 的传递过程是在 `draw` 方法内通过 `dispatchDraw` 来绘制子元素。 36 | 37 | 38 | 39 |
40 | 41 | ## 3. 顶级 View 的 DecorView 42 | 43 | Activity 中的 `setContentView` 方法。会将布局添加到 `android.R.id.content` 这个 `FrameLayout` 上。 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /md/java/sort/冒泡排序.md: -------------------------------------------------------------------------------- 1 | 冒泡排序 2 | == 3 | 4 | 5 | ## 前言 6 | 7 | **冒泡排序应该是最经典的排序算法了**,连我大学的C语言老师上课讲的第一个排序算法就是冒泡排序,略坑的我,当时听的还稀里糊涂的。 8 | 9 | 10 | 11 | ## 实现思路 12 | 13 | **实现思路也是非常非常简单:**两个for循环,然后分别判断相邻元素的大小: 14 | 15 | - 如果要升序排,那么就判断后者是否小于前者,是就交换元素。 16 | - 如果要降序排,那么就判断后者是否大于前者,是就交换元素。 17 | 18 | 19 | 20 | 21 | ## 实现代码 22 | 23 | ```Java 24 | public > T[] bubbleSorting(T[] array, boolean ascend) { 25 | int len = array.length; 26 | for (int i = 0; i < len; i++) { 27 | for (int j = i + 1; j < len; j++) { 28 | /** 29 | * 比较后者(j)与前者(i)的关系 30 | * 如果后者比前者小就交换 31 | */ 32 | int compare = array[j].compareTo(array[i]); 33 | if (compare < 0) { 34 | T temp = array[j]; 35 | array[j] = array[i]; 36 | array[i] = temp; 37 | } 38 | } 39 | System.out.print("i = " + i + " array = "); 40 | for (T data : array) { 41 | System.out.print(data + " "); 42 | } 43 | System.out.println(""); 44 | } 45 | return array; 46 | } 47 | ``` 48 | 49 | 50 | ## 运行 51 | 52 | ```Java 53 | public static void main(String args[]) { 54 | // 冒牌排序 55 | BubbleSort bubbleSorting = new BubbleSort(); 56 | Integer[] object_3 = {24, 19, 26, 39, 36, 7, 31, 29, 38, 23}; 57 | System.out.println("\n冒泡排序\n"); 58 | Integer[] result_3 = bubbleSorting.bubbleSorting(object_3, true); 59 | System.out.println("\n冒泡排序\n"); 60 | for (int i : result_3) { 61 | System.out.print(i + " "); 62 | } 63 | } 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /md/android/base/2. Activity 启动模式.md: -------------------------------------------------------------------------------- 1 | 2. Activity 启动模式 2 | == 3 | 4 |
5 | 6 | **standard:** **标准模式**。也是 Activity 的 **默认模式**。每次启动 Activity 都会重新创建一个新的实例,不管是否存在。 7 | 8 |
9 | 10 | **singleTop:** **栈顶复用模式**。如果 要启动的 Activity 已经位于任务栈的 **栈顶**,那么就不会创建 这个 Activity ,它的 `onNewIntent(...)` 方法会被回调。如果 要启动的 Activity 不位于栈顶,就会跟 **standard** 一样,重新创建。 11 | 12 |
13 | 14 | **singleTask:** **栈内复用模式**。如果 要启动的 Activity 已经位于任务栈的 **栈内**,那么就不会创建 这个 Activity ,它的 `onNewIntent(...)` 方法会被回调( **与 singleTop 类似,区别在于 singleTask 只要求在 栈内** ) 15 | 16 | 假如 要启动 一个 **singleTask** 模式的 Activity 叫 **S** 。会存在 **两种** 情况: 17 | 18 | - 如果存在 S **需要的任务栈存在**。 19 | 20 | + 如果 S **实例存在**。会把 S 置于栈顶,然后调用其 `onNewIntent(...)` 方法。**注意**:**singleTask** 具有 **默认的 clearTop 功能**。如果任务栈内是: **ASBC**,启动 S 的话,就变为:**AS**。 21 | 22 | + 如果 S **实例不存在**。会创建 S 实例,然后放入栈内,置于栈顶。 23 | 24 | - 如果存在 S **需要的任务栈不存在**。会 **创建对应的任务栈**,然后创建 S 实例化,放入任务栈内,置于栈顶。 25 | 26 |
27 | 28 | **singleInstance:** **单例模式**。一种**加强版的 singleTask 模式**。系统会为 **singleInstance** 的 Activity 创建一个独有的 **任务栈**,由于复用特性,就不会再创建 这个 Activity。 29 | 30 |
31 |
32 |
33 |
34 | 35 | **android:taskAffinity 可以为 Activity 指定任务栈:** taskAffinity 翻译为 **“任务相关性”**。默认情况下,**所有的 Activity 所需的任务栈的名字都是应用的包名**。 36 | 37 |
38 | 39 | **android:taskAffinity 除了和 singleTask 配对外,还和 allowTaskReparenting 配对使用:** 如果一个应用 A 启动了 B 的 Activity 后,这个 Activity 的 **allowTaskReparenting=true** 时,那么 应用 B 被启动后,该 Activity 后从 A 的任务栈转移到 B 的任务栈内。**情景:** A 启动了 B 的 SActivity,然后 HOME 键 回到桌面,点开 B 应用,重新显示的不是 B 的 主 Activity,而是重新显示已经被 A 启动的 SActivity。造成 SActivity 从 A任务栈转移到 B 任务栈。**我们理想的情况是:** A 启动了 B 的 SActivity,但是 SActivity 应该还在 A 任务栈内,所以应该 打开 B 应用的时候 显示的 是 B 应用的 主 Activity。但是,**这就是 allowTaskReparenting 作用,扭曲了这种理想情况**。 40 | 41 |
42 | 43 | **查看 Activity 任务栈:** `adb shell dumpsys activity` 。 44 | -------------------------------------------------------------------------------- /code/android/framework/otto/ThreadEnforcer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.camnter.otto; 18 | 19 | import android.os.Looper; 20 | 21 | /** 22 | * Enforces a thread confinement policy for methods on a particular event bus. 23 | * 24 | * @author Jake Wharton 25 | */ 26 | public interface ThreadEnforcer { 27 | 28 | /** 29 | * Enforce a valid thread for the given {@code bus}. Implementations may throw any runtime exception. 30 | * 31 | * @param bus Event bus instance on which an action is being performed. 32 | */ 33 | void enforce(Bus bus); 34 | 35 | 36 | /** 37 | * 其他线程的 ThreadEnforcer 38 | * A {@link ThreadEnforcer} that does no verification. 39 | */ 40 | ThreadEnforcer ANY = new ThreadEnforcer() { 41 | @Override 42 | public void enforce(Bus bus) { 43 | // Allow any thread. 44 | } 45 | }; 46 | 47 | /** 48 | * 主线程的 ThreadEnforcer 49 | * A {@link ThreadEnforcer} that confines {@link Bus} methods to the main thread. 50 | */ 51 | ThreadEnforcer MAIN = new ThreadEnforcer() { 52 | @Override 53 | public void enforce(Bus bus) { 54 | if (Looper.myLooper() != Looper.getMainLooper()) { 55 | throw new IllegalStateException("Event bus " + bus + " accessed from non-main thread " + Looper.myLooper()); 56 | } 57 | } 58 | }; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /md/android/base/9. 自定义 View 分类 和 须知.md: -------------------------------------------------------------------------------- 1 | 9. 自定义 View 分类 和 须知 2 | == 3 | 4 | 5 | 6 |
7 | 8 | ## 9.1 自定义 View 分类 9 | 10 |
11 | 12 | ### 9.1.1 继承 View 重写 onDraw 方法 13 | 14 | 这种方式主要用于 **实现一些不规则的效果**。就是目标效果不方便通过布局组件组合的方式去实现。需要 **动态或静态地显示一些不规则的图形**。此时,就需要 **重写 onDraw 方法**。采用这种方式需要自己支持 `wrap_content`,并且 `padding` 也要自己处理。 15 | 16 |
17 | 18 | ### 9.1.2 继承 ViewGroup 19 | 20 | 这种方法用于实现 **自定义的布局**。如果要实现 **流式布局**,就得 **自定义 ViewGroup** 了。采用这种方式稍微复杂,需要合适地处理 **ViewGroup 的测量、布局** 这两个过程,并同时 **处理子元素的测量和布局过程**。 21 | 22 |
23 | 24 | ### 9.1.3 继承 特定的 View ( TextView、EditText ) 25 | 26 | 这种方法比较常见。使用场景也很多,比如一些带动画变换的文字,就得 继承 **TextView**。带 *clear* 按钮的编辑框,就得继承 **EditText**。 27 | 28 |
29 | 30 | ### 9.1.4 继承 特定的 ViewGroup ( LinearLayout ) 31 | 32 | 这种方法比较常见,**当某种效果看起来很像几种 View 组合在一起时**,可以采用这种方法实现。采用这种方法不需要处理 **ViewGroup** 的测量和布局这两个过程。 33 | 34 | 35 |
36 |
37 | 38 | ## 9.2 自定义 View 须知 39 | 40 |
41 | 42 | ### 9.2.1 让 View 支持 wrap_content 43 | 44 | 因为直接继承 **View** 或者 **ViewGroup** 的控件,如果不在 `onMeasure` 中对 `wrap_content` 进行特殊处理,那么当外界在布局中使用了` wrap_content` 时就无法达到预期的效果。 45 | 46 |
47 | 48 | ### 9.2.2 如果有必要,让你的 View 支持 padding 49 | 50 | 因为直接继承 **View** 的控件,如果不在 `draw` 方法中处理 `padding`,那么 `padding` 属性是无法起作用的。另外,直接继承自 **ViewGroup** 的控件需要在 `onMeasure` 和 `onLayout` 中考虑 `padding` 和子元素的 `margin` 对其造成的影响,不然将导致 `padding` 和子元素的 `margin` 失效。 51 | 52 |
53 | 54 | ### 9.2.3 尽量不要在 View 中使用 Handler 55 | 56 | 因为 **View** 内部本身就提供了 `post` 系列的方法,完全可以代替 **Handler** 的作用,让然除非你很明确地要使用 **Handler** 来发送消息。 57 | 58 |
59 | 60 | ### 9.2.4 View 中如果有线程或者动画,需要及时停止,参考 View#onDetachedFromWindow 61 | 62 | 在 **View** 中,如果有线程或者动画需要停止时,那么 `onDetachedFromWindow` 是一个很好的时机。当包含此 **View** 的 **Activity** 退出或者当前 **View** 被 *remove* 时,**View** 的 `onDetachedFromWindow` 方法会被调用,和此方法对应的是 `onAttachedToWindow`,当包含此 **View** 的 **Activity** 启动时,**View** 的 `onAttachedToWindow` 方法会被调用。同时,当 **View** 变得不可见时我们也需要停止线程和动画,如果不及时处理这些问题,有可能会造成内存泄漏。 63 | 64 |
65 | 66 | ### 9.2.5 View 带有滑动嵌套情形时,需要处理好滑动冲突 67 | 68 | 如果有滑动冲突的话,那么要合适地处理滑动冲突,否则会严重影响 **View** 的效果。 -------------------------------------------------------------------------------- /md/java/sort/快速排序.md: -------------------------------------------------------------------------------- 1 | 快速排序 2 | == 3 | 4 | 5 | ## 前言 6 | 7 | **快速排序可以说是所有排序算法里最常用、最优秀、最效率的算法。**它是冒泡排序的改进版。就像它的名字一样,“快速”! 8 | 9 | 10 | 11 | ## 实现思路 12 | 13 | **实现思路是非常有趣的:** 14 | - 1.选一个基准数(一般选择第一个数。即,index = 0)。 15 | - 2.来个 while,从数组最右边开始找比基准数小的数,记录下标为 r。 16 | - 3.在再来个 while,从数组最左边开始找比基准数大的数,记录下边为 l。 17 | - 4.交换两个数的位置。即下标 r 和下标 l 的数交换。 18 | - 5.当找到 r == l 的时候。即,相遇的时候,第一次排序已经完成。此时,必然走到数组的中间的数。 19 | - 6.基准数(index = 0)和 中间数交换。 20 | - 7.此时原来的基准数到了中间,根据原来的基准数切分为两个数组(都不包含原基准素)。 21 | - 8.分别给两个数组做 上诉 1-7 的操作(开始递归)。 22 | 23 | 24 | 25 | ## 实现代码 26 | 27 | ```Java 28 | public > void quickSorting(T[] array, int left, int right) { 29 | int r, l; 30 | if (left > right) return; 31 | // 基准数 32 | T temp = array[left]; 33 | l = left; 34 | r = right; 35 | while (l != r) { 36 | /** 37 | * 要先从右往左找 38 | * 如果比基准数大,就一直找下去,直到找到比基准数小的 39 | */ 40 | while ((array[r].compareTo(temp) >= 0) && l < r) r--; 41 | /** 42 | * 要先从左往右找 43 | * 如果比基准数小,就一直找下去,直到找到比基准数大的 44 | */ 45 | while ((array[l].compareTo(temp) <= 0) && l < r) l++; 46 | if (l < r) { 47 | /** 48 | * 交换 49 | */ 50 | T t = array[l]; 51 | array[l] = array[r]; 52 | array[r] = t; 53 | } 54 | } 55 | /** 56 | * 基准数与中间位置的数字交换 57 | */ 58 | array[left] = array[l]; 59 | array[l] = temp; 60 | /** 61 | * 继续处理左边的,这里是一个递归的过程 62 | */ 63 | quickSorting(array, left, l - 1); 64 | /** 65 | * 继续处理右边的,这里是一个递归的过程 66 | */ 67 | quickSorting(array, l + 1, right); 68 | } 69 | ``` 70 | 71 | 72 | 73 | ## 运行 74 | 75 | ```Java 76 | public static void main(String args[]) { 77 | Integer[] object = {26, 19, 7, 37, 27, 57, 67, 99, 87, 17}; 78 | QuickSort quickSorting = new QuickSort(); 79 | quickSorting.quickSorting(object, 0, object.length - 1); 80 | System.out.println("\n快速排序\n"); 81 | for (int i : object) { 82 | System.out.print(i + " "); 83 | } 84 | } 85 | ``` 86 | 87 | -------------------------------------------------------------------------------- /md/android/framework/Android Framework 概述.md: -------------------------------------------------------------------------------- 1 | Android Framework 概述 2 | == 3 | 4 |
5 | 6 | ## 1. Framework 框架 7 | 8 | > Framework 定义了 客户端组件 和 服务端组件 功能及接口。在 Android 中, "应用程序" 一般是指 ".apk" 程序。 9 | 10 | **Android Framework 框架:** 11 | 12 | - **服务端** 13 | - **客户端** 14 | - **Linux 驱动** 15 | 16 |
17 | 18 | ## 2. 服务端 19 | 20 | 服务端有经常提到的两个重要的服务:**WindowManagerService(WmS)** 和 **ActivityManagerService(AmS)**。 21 | 22 | 23 | ### 2.1 一些服务的作用 24 | 25 | - **WMS 作用:** 为所用应用程序分配窗口,并且管理这些窗口。包括分配窗口的大小,调节各窗口的叠放次序,隐 26 | 藏或者显示窗口。 27 | 28 | - **AMS 作用:** 管理所用应用程序中的 `Activity`。 29 | 30 | 31 | ### 2.2 一些服务端的消息处理类 32 | 33 | - **KeyQ 类:** `WMS` 的 **内部类**,继承于 `KeyInputQueue` 类。`KeyQ` 对象一旦创建了,就立即启动一个线程,该 34 | 线程会不断地读取用户的 UI 操作消息,比如按键、触摸屏、`trackball`、鼠标等,**并把这些消息放在消息队列 35 | QueueEvent 类中**。 36 | 37 | - **InputDispatcherThread 类:** InputDispatcherThread 类的对象一旦创建,也会立即启动一个线程,该线程会不断地从 QueueEvent 38 | 中取出用户的消息,并进行一定的过滤,过滤后,再将这些消息发送给当前活动的客户端程序中。 39 | 40 |
41 | 42 | ## 3. 客户端 43 | 44 | **客户端一些重要的类:** 45 | 46 | - **ActivityThread 类:** 应用程序的主线程类,**所有的 APK 程序都有且仅有一个 ActivityThread 类,程序的入口就是 ActivityThread 类中的 static main() 函数**。 47 | 48 | - **Activity 类:** APK 程序的 **最小运行单元**。一个 APK 程序中可以包含多个 Activity 对象,ActivityThread 主类会根据用户操作选择运行哪个 Activity 对象。 49 | 50 | - **PhoneWindow 类:** 继承于 Window 类,同时,**PhoneWindow 类内部包含了一个 DecorView 对象 。PhoneWindow 是把一个 FrameLayout 进行了一定的包装,并提供了一组通用的的窗口操作接口。** 51 | 52 | - **DecorView 类:** FrameLayout 的子类。并且是 PhoneWindow 中的一个内部类。Decor 的英文是 Decoration,即 "修饰" 的意思,DecorView 就是对普通的 FrameLayout 进行了一定的修饰,比如添加一个通用的 Title bar,并响应特定的按键消息等。 53 | 54 | - **Window 类:** 提供了一组通用的窗口( Window )操作API,这里的窗口仅仅是程序层面上的,**WMS 所管理的窗口并不是 Window 类,而是一个 View 类或者 ViewGroup 类,一般就是指 DecorView 类,即一个 DecorView 就是 WMS 所管理的一个窗口。Window 是一个 abstract 类型。** 55 | 56 | - **ViewRoot( Handler ) 类:** WMS 管理客户端窗口时,需要通知客户端进行某种操作,这些都是通过异步消息完成的,实现方式就是 Handler,ViewRoot 就是继承于 Handler,其作用主要是接收 WMS 的通知。 57 | 58 | - **W( Binder ) 类:** 继承于 Binder,并且是 **ViewRoot 的一个内部类**。 59 | 60 | - **WindowManager 类:** 客户端要申请创建一个窗口,而具体创建窗口的任务是由 WMS 完成的 WindowManager 类就像是一个部门经理,谁有什么需求就告诉它,由它和 WMS 进行交互,客户端不能直接和 WMS 交互。 61 | 62 | 63 | 64 | 65 |
66 | 67 | ## 4. Linux 驱动 68 | 69 | Linux 驱动 和 Framework 相关的主要包含两个部分。分别是 **SurfaceFlingger(SF)和 Binder**。 70 | 71 | **SurfaceFlingger 驱动作用:** 把各个 Surface 显示同一个屏幕上。 72 | 73 | **Binder 驱动作用:** 提供跨进程的消息传递。 74 | -------------------------------------------------------------------------------- /md/android/framework/Android APK 的运行过程.md: -------------------------------------------------------------------------------- 1 | Android APK 的运行过程 2 | == 3 | 4 | **1.** 首先,**ActivityThread** 从 `main()` 函数中开始进行,调用 `prepareMainLooper()` 为 **UI** 线程创建 **Looper** 的同时,也创建一个消息队列( **MessageQueue** )。 5 | 6 | 7 | **2.** 然后,创建一个 **ActivityThread** 对象,在 **ActivityThread** 初始化代码中会创建一个 **H**( **Handler** ) 对象和一个 **ApplicationThread**( **Binder** )对象。其中 **Binder** 负责接收远程 **AMS** 的 **IPC** 调用,接收到调用后,则通过 **Handler** 把消息发送到消息队列( **MessageQueue** ),**UI** 主线程会异步地从消息队列 ( **MessageQueue** ) 中取出消息并执行相应操作,比如 `start()`、`stop()`、`pause()` 等。 8 | 9 | 10 | **3.** 接着 **UI** 主线程调用 `Looper.loop()` 方法进入消息循环体,进入后就会不断从消息队列( **MessageQueue** )中读取并处理消息。 11 | 12 | 13 | ```java 14 | public final class ActivityThread { 15 | 16 | ... 17 | 18 | final ApplicationThread mAppThread = new ApplicationThread(); 19 | final Looper mLooper = Looper.myLooper(); 20 | final H mH = new H(); 21 | 22 | ... 23 | 24 | private class H extends Handler { 25 | 26 | ... 27 | 28 | } 29 | 30 | ... 31 | 32 | // ApplicationThreadNative 继承自 Binder 33 | private class ApplicationThread extends ApplicationThreadNative { 34 | 35 | ... 36 | 37 | } 38 | 39 | ... 40 | 41 | public static void main(String[] args) { 42 | 43 | ... 44 | 45 | Looper.prepareMainLooper(); 46 | 47 | ... 48 | 49 | ActivityThread thread = new ActivityThread(); 50 | 51 | ... 52 | 53 | Looper.loop(); 54 | 55 | ... 56 | 57 | } 58 | 59 | ... 60 | 61 | } 62 | ``` 63 | 64 | 65 | 66 | 67 | **4.** 当 **ActivityThread** 接收到 **AMS** 发送 **start 某个 Activity 的请求** 后,就会创建指定的 **Activity** 对象。**Activity** 又会创建 **PhoneWindow 类 -> DecorView 类 -> 创建相应的 View 或者 ViewGroup**。创建完成后,**Activity** 需要把创建好的界面显示到屏幕上,于是调用 **WindowManager** 类,**WindowManager** 创建一个 **ViewRoot** 对象,实际上创建了 **ViewRoot** 类和 **W** 类,创建 **ViewRoot** 对象后,**WindowManager** 再调用 **WMS** 提供的远程接口完成添加一个窗口并显示到屏幕上。 68 | 69 | 70 | 71 | **5.** 接下来,如果在程序界面上操作。**KeyQ 线程** 不断把用户消息存储到 **QueueEvent 队列** 中 **InputDispatcherThread 线程** 逐个取出消息,然后调用 **WMS** 中的相应函数处理该消息。当 **WMS** 发现该消息属于客户端某个窗口时,就会调用相应窗口的 **W** 接口。 72 | 73 | 74 | **6.** **W** 类 是一个 **Binder**,负责接收 **WMS** 的 **IPC 调用**,并把调用消息传递给 **ViewRoot**,**ViewRoot** 再把消息传递给 **UI** 主线程 **ActivityThread**,**ActivityThread** 解析该消息并做相应的处理。在客户端程序中,首先处理消息的是 **DecorView**,如果是 **DecorView** 不想处理某个消息,则可以将该消息传递给其内部包含在 **子 View 或者 ViewGroup**,如果还没处理,则传递给 **PhoneWindow**,最后再传递给 **Activity**。 75 | -------------------------------------------------------------------------------- /md/android/framework/Android 代码编译和 APK 安装过程.md: -------------------------------------------------------------------------------- 1 | Android 代码编译和 APK 安装过程 2 | == 3 | 4 |
5 | 6 | ## 1. Android 代码编译 7 | 8 | 用 Java 语言开发 Android 程序的话,Java 编译器会将源代码编译成 Java 字节码( **.class** )文件。**.class** 文件接着被 **Android SDK** 提供的 **dx 工具** 转化为 **dex** 字节码,最后打包在 APK里的 **classes.dex** 文件中。 9 | 10 | ### 1.1 Jack ( Java Android Compiler Kit ) 编译 11 | 12 | 如果用 Jack 编译的话,上面的 ` .java + 资源 -> 资源 + .class -> .dex` 流程会变为 ` .java + 资源 -> .jack -> .dex ` 流程。 13 | 14 |
15 | 16 | ## 2. Android APK 安装过程 17 | 18 | **APK** 文件在安装的时候,**Java Runtime Framework** 内的 **PacakgeManagerService** 会对 **APK** 文件进行解析,并且通过 **Socket IPC** 通知 **C/C++ Runtime Framework** 内的 **installd 守护进程** 对 **APK** 里的 **classes.dex** 进行优化,得到另外一个 **classes.odex** 文件。 19 | 20 | 21 | 22 | ### 2.1 APK 从桌面 Launcher 启动的过程 23 | 24 | **APK 从桌面 Launcher 启动的过程:** 从 **Launcher** 点击应用图标的时候,**Launcher** 向 **Java Runtime Framework** 内的 **AMS** 发送一个启动应用的请求。**AMS** 通过 **Socket IPC** 向 **C/C++ Runtime Framework** 内的 **zygote** 守护进程 请求创建一个应用进程。应用进程包含都包含一个虚拟机 ( **Dalvik or art** ),并且应用进程创建启动后,就通过这个虚拟机 ( **Dalvik or art** )去加载 **classes.odex** 文件,这才运行起来。 25 | 26 | 27 | Android APK 运行过程依赖于虚拟机( **Dalvik or art** )。将 **classes.odex** 内的字节码解释成本地机器指令执行。在 APK 运行的过程中,如果通过 `FileInputStream` 或者 `FileOutputStream` 打开一个文件的时候,虚拟机( **Dalvik or art** )会找到 **C/C++ Runtime Framework** 里的 C 库 **bionic** 提供的系统接口 `open`,打开指定文件。 28 | 29 | 30 | 31 | ### 2.2 应用程序界面的绘制和渲染过程 32 | 33 | **应用程序界面的绘制和渲染过程:** 34 | 35 | - **1.** 应用程序通过 **SDK** 提供的 **UI** 类向 **Java Runtime Framework** 内的 **WMS** 申请分配一块图形缓冲区。**WMS** 又通过 **Binder IPC** 向 **C/C++ Runtime Framework** 内的 **SurfaceFlinger** 申请分配图形缓冲区。但是,图形缓冲区不是由 **SurfaceFlinger** 分配的,而是由 显示系统 分配的。可能在显存中,也可能在 **GPU** 中。**SurfaceFlinger** 通过 **HAL** ( **C/C++ Runtime Framework** ) 层面的 **Gralloc** 模块向 **Kernel** 内的显卡或者 **GPU** 驱动申请分配真正的图形缓冲区。 36 | 37 | - **2.** 获得的 **绘制 UI 所需的 图形缓冲区** 之后,就可以绘制用户指定的 **UI** 了。在应用程序使用的是硬件绘制方式,则通过 **C/C++ Runtime Framework** 内的 **OpenGL** 进行绘制。此时,**SDK** 的 **UI** 类的绘制相关方法就通过 虚拟机( **Dalvik or art** )转换成了 **C/C++ Runtime Framework** 内的 **OpenGL** 操作。 38 | 39 | - **3.** 应用程序的 **UI** 绘制完成之后,绘制结果保存在图形缓冲区之中。此时,应该 **将该图形缓冲区渲染到手机屏幕上**,还需要 **Binder IPC** 将该图形缓冲区发送给 **C/C++ Runtime Framework** 内的 **SurfaceFlinger**。**SurfaceFlinger** 通过使用 **OpenGL** 或者 **HWComposer** 将需要渲染到屏幕上的图形缓冲区进行合成后,得到一个主图形缓冲区。最后这个主图形缓冲区又会被 **SurfaceFlinger** 提交给 **Kernel** 显卡驱动,并且在屏幕上进行显示。 40 | 41 | 42 | -------------------------------------------------------------------------------- /md/android/base/6. View 的事件分发机制.md: -------------------------------------------------------------------------------- 1 | 6. View 的事件分发机制 2 | == 3 | 4 | [图:Android Touch 事件传递机制](http://www.open-open.com/lib/view/open1422428386548.html) 5 | 6 |
7 | 8 | ## 1. ViewGroup 上看 9 | 10 | 如果点击一个 **ViewGroup** 的话,点击事件产生之后: 11 | 12 | - 1.1 首先会传递给这个 **ViewGroup** 。 13 | 14 | - 1.2 然后调用该 **ViewGroup** 的 `dispatchTouchEvent `分发事件。 15 | 16 | - 1.3.1 如果 **ViewGroup** 的 `onInterceptTouchEvent` 返回 **true**,则表示 **拦截事件**。接着会将该事件交给该 **ViewGroup** 处理,调用其 `onTouchEvent`。 17 | 18 | - 1.3.2 如果 **ViewGroup** 的 `onInterceptTouchEvent` 返回 **false**,则表示 **不拦截事件**。这个事件就会交给 **ViewGroup 的 子 View** 去处理。接着 在 **子 View** 中 继续循环 **1.1、1.2、1.3.1、1.3.2**。 19 | 20 | 21 | ## 2. onTouchListener > onTouchEvent > onClickListener 22 | 23 | 一个 **View** 需要处理事件的时候,如果设置了 **onTouchListener**。那么,接着这个 **onTouchListener** 的 `onTouch` 方法会别调用。 24 | 25 | - **2.1** 如果 `onTouch` 返回 **true**。那么 `onTouchEvent` 会被调用。 26 | - **2.1.1** 在 `onTouchEvent` 中,如果设置了 **onClickListener**。会调用 `performClick()`,接着就回调了 `OnClickListener.onClick(View v)`。 27 | 28 | 所以: 29 | **onTouchListener > onTouchEvent > onClickListener** 30 | 31 |
32 | 33 | ## 3. Activity 上看 34 | 35 | 一个点击事件触发后,会有如下的传递过程:**Activity >> Window >> View(从上到下)**。 36 | 37 | 如果,中间没有拦截,传到最底下的 **View** 时,会从 **最底下的 View** 开始 调用 `onTouchEvent` 方法,如果一直返回 **false** 的话,就调用 **父 View** 的 `onTouchEvent` 方法 ,直到 **Activity** 为止。所以,如果 `onTouchEvent` 一直返回 **false** 的过程是:**最底层 View.onTouchEvent >> 倒数第二层 View.onTouchEvent >> Activity.onTouchEvent(从下到上)** 38 | 39 |
40 | 41 | ## 一些有用的结论 42 | 43 | - **1.** **同一个序列的事件**:手机按下屏幕到离开屏幕的过程。这个过程以 **down 事件**开始,然后到 N 次 的 **move 事件**,最后以 **up 事件**结束。 44 | 45 | - **2.** 正常情况下,**一个事件序列只能被一个 View 拦截并且消费**。 46 | 47 | - **3.** 一个 **View** 拦截了事件后,**这一序列的事件都会交给该 View 执行处理**。因此,**同一个序列事件不能分别交给两个 View 同时处理**。但是有特殊手段能做到,比如:**在一个 View 的 onTouchEvent 内交给其他 View 处理**。 48 | 49 | - **4.** 在同一序列事件的情况下,如果 View 拦截了事件,那么后续的事件都由该 View 去处理,**并且不会再该序列事件中调用 onInterceptTouchEvent**。 50 | 51 | - **5.** 如果 **View** 不消费 **DOWN** 以外的事件,这个事件消失。父元素的 **onTouchEvent** 也不会调用。并且当前 View 可以持续收到后续事件,**那么之前消失的事件会传递到 Activity 中进行处理**。 52 | 53 | - **6.** **ViewGroup** 默认不拦截事件,源码中 **ViewGroup** 的 **onInterceptTouchEvent** 方法默认返回 **false**。 54 | 55 | - **7.** **View** 没有 **onInterceptTouchEvent** 方法,如果有事件传递给它,那么它的 **onTouchEvent** 方法会被调用。 56 | 57 | - **8.** **View** 的 **onTouchEvent** 事件默认会被消费。除非它被设置为不可点击状态( `clickable or longClickable == false` )。 58 | 59 | - **9.** **View** 的 `enable` 属性不会影响 **onTouchEvent** 方法的返回值。只要 `clickable or longClickable == true`,那么 **onTouchEvent** 方法 就会返回 **true**。 60 | 61 | - **10.** `onClick` 发生的前提是当前 **View** 是可点击的。并且接收到了 **DOWN** 事件。 62 | 63 | - **11.** 事件的传递过程是 **由外向内** 的。事件会从 **Activity >> Window >> View** 传递给父元素,然后一直传递给子元素。在子元素中可以通过 `requestDisallowInterceptTouchEvent` 方法干预父元素的分发。但 **DOWN** 事件除外。 64 | -------------------------------------------------------------------------------- /md/java/sort/希尔排序.md: -------------------------------------------------------------------------------- 1 | 希尔排序 2 | == 3 | 4 | ## 前言 5 | 6 | **希尔排序也称为缩小增量排序,是插入排序算法的一种改进版。** 7 | 8 | **希尔排序应该是最难的排序了** 9 | 10 | 希尔排序的诞生是由于插入排序在处理大规模数组的时候会遇到需要移动太多元素的问题。 11 | 12 | 13 | 14 | ## 实现思路 15 | 16 | 希尔排序的思想是将一个大的数组“分而治之”,将一个大的数组划分为若干个小的数组。以 gap (间隔)来划分,比如数组 [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] ,如果以间隔 gap = 2划分,可以划分为 [ 1 , 3 , 5 , 7 ] 和 [ 2 , 4 , 6 , 8 ] 两个数组;对应的,若间隔 gap = 3 ,则划分的数组为: [ 1 , 4 , 7 ] 、 [ 2, 5 , 8 ] 、 [ 3 , 6 ] 。然后分别对这些子数组进行正统的插入排序,排序好后将数组回收到大数组里,再减少间隔 gap 的值重复进行之前的操作,直到间隔 gap = 1时,此时子数组只有一个,就是整个大数组,此时其实都快排好序了,在对这唯一的子数组进行最后一次插入排序的时候,移动的数会很少很少,从而解决了插入排序在处理大规模数组时较多移动次数的问题。 17 | 18 | 19 | 20 | ## 模拟走位 21 | 22 | **初始数组:[ 26 , 19 , 7 , 37 , 27 , 57 , 67 , 99 , 87 , 17 ]** 23 | 24 | **第一次:** 25 | 26 | 初次计算间隔 gap = 10 / 2 = 5 27 | 28 | *( 26 走五步是57,以此类推 )* 29 | 30 | 26 19 7 37 27 31 | 57 67 99 87 17 32 | 33 | *进行列排序( 比如,26和57是一列,以此类推 )* 34 | 35 | 26 19 7 37 17 36 | 57 67 99 87 27 37 | 38 | *按行取回放入大数组里:[ 26 , 19 , 7 , 37 , 17 , 57 , 67 , 99 , 87, 27 ]* 39 | 40 | **第二次:** 41 | 42 | gap = 5 / 2 = 2 43 | 44 | *( 26 走两步是7,以此类推 )* 45 | 46 | 26 19 47 | 7 37 48 | 17 57 49 | 67 99 50 | 87 27 51 | 52 | *进行列排序( 比如,26、7、17、67、87 是一列,以此类推 )* 53 | 54 | 7 19 55 | 17 27 56 | 26 37 57 | 67 57 58 | 87 99 59 | 60 | *按行取回放入大数组里:[ 7 , 19 , 17 , 27 , 26 , 37 , 67 , 57 , 87 , 99 ]* 61 | 62 | **第三次:** 63 | 64 | gap = 2 / 2 = 1 65 | 66 | *( 7 开始一步一步走 )* 67 | 68 | 7 69 | 19 70 | 17 71 | 27 72 | 26 73 | 37 74 | 67 75 | 57 76 | 87 77 | 99 78 | 79 | *进行列排序* 80 | 81 | 7 82 | 17 83 | 19 84 | 26 85 | 27 86 | 37 87 | 57 88 | 67 89 | 87 90 | 99 91 | 92 | *按行取回放入大数组里:[ 7 , 17 , 19 , 26 , 27 , 37 , 57 , 67 , 87 , 99 ]* 93 | 94 | 95 | 96 | **总结:**其实很有节奏和规律,不算复杂。 97 | 98 | 99 | 100 | ## 实现代码 101 | 102 | ```Java 103 | public > void shellSorting(T[] array) { 104 | int len = array.length; 105 | int i, j, gap; 106 | // 逐渐减小步长 107 | for (gap = len / 2; gap > 0; gap /= 2) { 108 | // 布置行数据 109 | for (i = 0; i < gap; i++) { 110 | // 布置列数据 111 | for (j = gap; j < len; j += gap) { 112 | if (array[j - gap + i].compareTo(array[j + i]) > 0) { 113 | T temp = array[j - gap + i]; 114 | array[j - gap + i] = array[j + i]; 115 | array[j + i] = temp; 116 | } 117 | } 118 | } 119 | } 120 | } 121 | ``` 122 | 123 | 124 | 125 | 126 | ## 运行 127 | 128 | ```Java 129 | public static void main(String args[]) { 130 | Integer[] object = {26, 19, 7, 37, 27, 57, 67, 99, 87, 17}; 131 | System.out.println("\n希尔排序\n"); 132 | ShellSort shellSorting = new ShellSort(); 133 | shellSorting.shellSorting(object); 134 | System.out.println("\n希尔排序\n"); 135 | for (int i : object) { 136 | System.out.print(i + " "); 137 | } 138 | } 139 | ``` 140 | 141 | -------------------------------------------------------------------------------- /md/android/framework/Binder.md: -------------------------------------------------------------------------------- 1 | Binder 2 | == 3 | 4 | > Binder 机制 则是 Android 系统实现进程间通信(IPC)的机制之一。 5 | 6 | 7 |
8 | 9 | ## 1. 背景 10 | 11 | Android 系统启动时候,**Zygote 进程 孵化出第一个子进程叫 SystemServer**。在 SystemServer 进程中,很多系统提供的服务,比如 ActivityManagerService, PowerManagerService 等,都在此进程中的某一条线程上运行。 12 | 13 | 用户开发的 App,由于 Android 系统安全的考虑,安装 App 到系统中,会被**分配一个 UID**,并且**运行在一个 独立的进程 里**。 14 | 15 | 由于 **Linux 系统用户安全机制**,普通 App 是无法访问系统服务的。 16 | 17 | 18 |
19 | 20 | ## 2. Binder 机制四大概念 21 | 22 | **Binder 机制四大概念**: 23 | 24 | - **Server:**提供服务。 25 | 26 | - **Client:**获取服务。 27 | 28 | - **ServiceManager:** 和 Zygote 进程一样,属于 Android系统的守护进程之一。**作用:** 辅助 Android 系统去维护许多 Service 列表。**形象化:** 就像一个电话本,想要找到某人( Service ),就必须找到电话本。 29 | 30 | - **Binder 驱动:** 一段运行在内核空间的代码,整个 Binder 机制的核心,Binder 才能实现进程间的通信。 31 | 32 | 33 |
34 | 35 | ## 3. Binder 机制四大概念的 运行空间 36 | 37 | **Linux 内核空间:** 只有 **Binder 驱动**。 38 | 39 | **Linux 用户空间:** 40 | 41 | - **Server** 42 | 43 | - **Client** 44 | 45 | - **ServiceManager** 46 | 47 | 48 |
49 | 50 | ## 4. Binder 驱动 51 | 52 | **整个 Binder 机制的核心,Binder 才能实现进程间的通信。** 53 | 54 | > Binder 驱动跟硬件没有联系,是一段运行在 内核空间 的代码,通过 /dev/binder 的文件在 内核空间 和 用户空间 之间 读写数据。 55 | 56 | Server, Client 和 ServiceManager 是运行在 **用户空间** 中的 **不同的进程**,彼此是 **不能互相访问** 的。所以,**不同进程之间的数据传递,只能通过内核空间来操作。由于只有 Binder 驱动在内核空间,只能由 Binder 驱动来进行,所以 Binder 驱动是 Binder 机制核心。** 57 | 58 | **Binder 驱动主要工作职责:** 59 | 60 | - **Binder 节点的建立。** 61 | 62 | - **Binder 进程间传递。** 63 | 64 | - **Binder 引用的计数管理。** 65 | 66 | - **进程间传输数据包。** 67 | 68 | 进程间通信的大部分工作都是通过 `open()`, `mmap()` 和 `ioctl()`等文件操作,在内核空间与用户空间中进进出出,来实现的。 69 | 70 | 71 |
72 | 73 | ## 5. ServiceManager 74 | 75 | > ServiceManager 是 Android 系统的一个守护进程。 76 | 77 | **Android 启动 ServiceManager 进程的时候:** 78 | 79 | - **1.** 打开 "/dev/binder" 设备文件,进行内存映射。 80 | 81 | - **2.** 该进程将 **BINDER_SET_CONTEXT_MGR** 命令传给 Binder 驱动,Binder 驱动 就会为其在内核空间中创建一个节点(binder_node),这个节点就是 `binder_context_mgr_node`,也就是ServiceManager的 Binder 实体,**将该进程变为上下文管理者(ServiceManager)** 。 82 | 83 | - **3.** 进入无限循环,等待 Client 请求。 84 | 85 | 在整个 Android 系统中,只会有一个 `binder_context_mgr_node` ,所以只会有一个 ServiceManager 的进程。 86 | 87 | ### 5.1 扩展-句柄 88 | 89 | > 句柄理解上就是一个区分不同服务的标识。在 Android 系统中,只有跨进程的通信,才会用到句柄这个术语,因为没有办法直接访问。 90 | 91 | ### 5.2 句柄与引用的区别 92 | 93 | 一个是远程访问(跨进程),一个是本地访问( 相同进程 )。 94 | 95 | 每个进程在系统中的都有定义好的句柄,**ServiceManager 的句柄为 0**。 96 | 97 | 98 |
99 | 100 | ## 6. Binder 机制的实现 101 | 102 | ### 6.1 Binder Server 103 | 104 | - **1.** Binder 驱动 在 **内核空间** 里创建一个 Binder 节点,属于 **Server 进程**。 105 | 106 | - **2.** Binder 驱动 为节点分配一个句柄,将 **句柄** 和 **Name** 发送给 **ServiceManager** 实现注册。 107 | 108 | **Binder Server 创建后会启动一个 隐藏线程,同时会创建 Binder 驱动 中的 Binder 服务端 的 远程 mRemote 对象,提供给客户端调用。** 109 | 110 | 111 | ### 6.2 Binder Client 112 | 113 | - **1.** Binder Client 通过 transact 方法, Client 线程进入到 Binder 驱动 中。 114 | 115 | - **2.** 将 **想要服务 + 句柄=0**,封装成数据包,打开 **dev/binder** 设备文件,发给 Binder 驱动。 116 | 117 | - **3.** Binder 驱动 接收到句柄0,找到 ServiceManager。 118 | 119 | - **4.** ServiceManager 根据 **服务名称->句柄的 mapping** 找到服务句柄。 120 | 121 | - **5.** ServiceManager 将想要服务的句柄返回。 122 | 123 | **然后调用 Binder 驱动 中对应 Binder Server 的 mRemote 对象去访问 Binder Server,Binder Server 向 Binder驱动 发送一个notify消息,从而 Client 线程从 Binder 驱动代码区返回到 Server 代码区。** 124 | -------------------------------------------------------------------------------- /md/java/sort/插入排序.md: -------------------------------------------------------------------------------- 1 | 插入排序 2 | == 3 | 4 | 5 | ## 前言 6 | 7 | **插入排序的效果就像将一个数组模拟成两个数组的在选择、插入的过程。** 8 | 9 | 10 | 11 | ## 实现思路 12 | 13 | 假设在下标 i 之前的是数都已经排好了顺序( 0 ... i-1 ),那么此次需要找到 i 位置的数的正确位置 k 。在寻找这个位置 k 的过程中,逐个比较 i 之前排好顺序的数组,先比较最大的 i-1 位置的数(因为有序,所以 i 以前的数, i-1 是最大的),在寻找的过程中,比较的数大于当前 i 位置的数 ,那么比较的数置后一个位置,那么在有序数组中会一直“腾位置”,那么一直一直找,当找到比 i 位置小的数时,插入到这个数的后面即可,完成一趟插入。 14 | 15 | --- 16 | 17 | ## 模拟走位 18 | 19 | 上面说的很麻烦,直接看走位比较好 。 20 | 21 | **初始数组:**[ 11 , 31 , 12 , 5 , 34 , 30 , 26 , 38 , 36 , 18 ] 22 | 23 | 第一趟:[ 11 , 31 , 12 , 5 , 34 , 30 , 26 , 38 , 36 , 18 ] ( 无移动的元素 ) 24 | 25 | 第二趟:[ 11 , 12 , 31 , 5 , 34 , 30 , 26 , 38 , 36 , 18 ] ( 31 向后移动 ) 26 | 27 | 第三趟:[ 5 , 11 , 12 , 31 , 34 , 30 , 26 , 38 , 36 , 18 ] ( 11 , 12 , 31 皆向后移动 ) 28 | 29 | 第四趟:[ 5 , 11 , 12 , 31 , 34 , 30 , 26 , 38 , 36 , 18 ] ( 无移动的元素 ) 30 | 31 | 第五趟:[ 5 , 11 , 12 , 30 , 31 , 34 , 26 , 38 , 36 , 18 ] ( 31 , 34 向后移动 ) 32 | 33 | 第六趟:[ 5 , 11 , 12 , 26 , 30 , 31 , 34 , 38 , 36 , 18 ] ( 30 , 31 , 34 向后移动 ) 34 | 35 | 第七趟:[ 5 , 11 , 12 , 26 , 30 , 31 , 34 , 38 , 36 , 18 ] ( 无移动的元素 ) 36 | 37 | 第八趟:[ 5 , 11 , 12 , 26 , 30 , 31 , 34 , 36 , 38 , 18 ] ( 38 向后移动 ) 38 | 39 | 第九趟:[ 5 , 11 , 12 , 18 , 26 , 30 , 31 , 34 , 36 , 38 ] ( 26 , 30 , 31, 34 , 36 , 38 向后移动 ) 40 | 41 | 42 | **总结:**插入排序会比选择排序更优秀,以为它能利用排序过程中部分数有序的优势,减少一些不必要的比较,是否优秀取决于数组的初始顺序。 43 | 44 | 如果给定的数组是倒序,那么就是最坏的情况下了,比较次数:1 + 2 + ... ( n-1 ) + n = ( n+1 ) / 2。是一个不稳定的排序,依赖着数组的初始顺序。 45 | 46 | 47 | 48 | ## 实现代码 49 | 50 | ```Java 51 | public > T[] insertSorting(T[] array) { 52 | int len = array.length; 53 | // 每个元素选取出来作为插入元素 54 | for (int i = 0; i < len; i++) { 55 | T toInsert = array[i]; 56 | int j = i; 57 | for (; j > 0; j--) { 58 | /** 59 | * 原来顺序系列里从最大的数开始 和 现在选择的比较 60 | * 如果找到比选择的数小的 break 61 | * 此时已经记下了 坐标 保存到 j 中 62 | */ 63 | if (array[j - 1].compareTo(toInsert) <= 0) { 64 | break; 65 | } 66 | /** 67 | * 如果一直没找到j的位置 那么就是 选择到的元素一直比较小 68 | * 那么一直腾位置 69 | */ 70 | array[j] = array[j - 1]; 71 | } 72 | //如果此时 j = 0 说明了 顺序序列里没有比选择元素小的 73 | array[j] = toInsert; 74 | System.out.print("i = " + i + " array = "); 75 | for (T data : array) { 76 | System.out.print(data + " "); 77 | } 78 | System.out.println(""); 79 | } 80 | return array; 81 | } 82 | ``` 83 | 84 | 85 | ## 运行 86 | 87 | ```Java 88 | public static void main(String args[]) { 89 | /** 90 | * 插入排序 91 | */ 92 | InsertSort insertSort = new InsertSort(); 93 | System.out.println("\n插入排序\n"); 94 | Integer[] object = {11, 31, 12, 5, 34, 30, 26, 38, 36, 18}; 95 | Integer[] result = insertSort.insertSorting(object); 96 | System.out.println(""); 97 | System.out.println("\n插入排序\n"); 98 | for (int i : result) { 99 | System.out.print(i + " "); 100 | } 101 | System.out.println(""); 102 | System.out.println("\n插入排序\n"); 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /md/java/sort/选择排序.md: -------------------------------------------------------------------------------- 1 | 选择排序 2 | == 3 | 4 | 5 | ## 前言 6 | 7 | **选择排序的含义就是因为每一次最内层for走一趟都会选择出一个最大或最小的数。** 8 | 9 | 10 | 11 | 12 | ## 实现思路 13 | 14 | *比如有 N个数,开始先选取第一个数( i= 0 )作为基准数。然后抛开这个数以以前的数,右边的数( i+1 To n-1 )走一趟 for,找出最小(最大也行,看你是要升序还是降序),然后拿到这个最小的数,和基准数交换。然后下一次又重新定位基准数 i + 1,继续走右边的数一趟,找到最值,继续交换.......如此反复。* 15 | 16 | 17 | 18 | ## 模拟走位 19 | 20 | **初始数组: [38, 17, 16, 16, 7, 31, 39, 32, 2, 11]** 21 | 22 | i = 0 :[ 2 , 17 , 16 , 16 , 7 , 31 , 39 , 32 , 38 , 11 ]( 0th [38]<->8th [2] ) 23 | 24 | i = 1 :[ 2 , 7 , 16 , 16 , 17 , 31 , 39 , 32 , 38 , 11 ]( 1st [17]<->4th [7] ) 25 | 26 | i = 2 :[ 2 , 7 , 11 , 16 , 17 , 31 , 39 , 32 , 38 , 16 ]( 2nd [11]<->9th [16] ) 27 | 28 | i = 3 :[ 2 , 7 , 11 , 16 , 17 , 31 , 39 , 32 , 38 , 16 ]( 无需交换 ) 29 | 30 | i = 4 :[ 2 , 7 , 11 , 16 , 16 , 31 , 39 , 32 , 38 , 17 ]( 4th [17]<->9th [16] ) 31 | 32 | i = 5 :[ 2 , 7 , 11 , 16 , 16 , 17 , 39 , 32 , 38 , 31 ]( 5th [31]<->9th [17] ) 33 | 34 | i = 6 :[ 2 , 7 , 11 , 16 , 16 , 17 , 31 , 32 , 38 , 39 ]( 6th [39]<->9th [31] ) 35 | 36 | i = 7 :[ 2 , 7 , 11 , 16 , 16 , 17 , 31 , 32 , 38 , 39 ]( 无需交换 ) 37 | 38 | i = 8 :[ 2 , 7 , 11 , 16 , 16 , 17 , 31 , 32 , 38 , 39 ]( 无需交换 ) 39 | 40 | i = 9 :[ 2 , 7 , 11 , 16 , 16 , 17 , 31 , 32 , 38 , 39 ]( 无需交换 ) 41 | 42 | 43 | **总结:**选择排序随着 i 的 增大,比较次数也相应的减少了,无论数组是否有序,都会从 数组的开始 到 数组结束进行一次比较。次数也是固定的 n + (n-1) + ... + 2 + 1,而交换的次数也和初始化数组的排序有关。 44 | 45 | **所以:**最坏的情况是,n次;最好的情况是,0次。 46 | 47 | 48 | 49 | ## 实现代码 50 | 51 | ```Java 52 | public > T[] selectionSorting(T[] array, boolean ascend) { 53 | // 取得数组长度 54 | int len = array.length; 55 | //逐个选择比较 56 | for (int i = 0; i < len; i++) { 57 | int selected = i; 58 | /** 59 | * 与选择后的 其他元素比较 60 | * Comparable.compareTo() 方法 61 | * -1 : 小于 62 | * 0 : 等于 63 | * 1 : 大于 64 | */ 65 | for (int j = i + 1; j < len; j++) { 66 | int compare = array[j].compareTo(array[selected]); 67 | /** 68 | * 如果 69 | * array[j] != array[selected] 70 | * 并且 71 | * array[j] 小于 array[selected] 72 | * 都成立的时候 73 | */ 74 | if ((compare != 0 && compare < 0) == ascend) { 75 | selected = j; 76 | } 77 | } 78 | 79 | /** 80 | * 基准数 和 被选择的交换 81 | * 当然要是没找到选择的数,那么被选择数一直没变是i 82 | * 也就是基准数自己和自己的交换 83 | */ 84 | T t = array[i]; 85 | array[i] = array[selected]; 86 | array[selected] = t; 87 | System.out.print("i = " + i + " array = "); 88 | for (T data : array) { 89 | System.out.print(data + " "); 90 | } 91 | System.out.println(""); 92 | } 93 | return array; 94 | } 95 | ``` 96 | 97 | 98 | 99 | 100 | ## 运行 101 | 102 | ```Java 103 | public static void main(String[] args) { 104 | /** 105 | * 选择排序 106 | */ 107 | SelectionSort selectionSorting = new SelectionSort(); 108 | Integer[] object = {38, 17, 16, 16, 7, 31, 39, 32, 2, 11}; 109 | System.out.println("\n选择排序\n"); 110 | Integer[] result = selectionSorting.selectionSorting(object, true); 111 | System.out.println("\n选择排序\n"); 112 | for (int i : result) { 113 | System.out.print(i + " "); 114 | } 115 | 116 | } 117 | ``` 118 | 119 | -------------------------------------------------------------------------------- /code/android/framework/otto/EventProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Square, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.camnter.otto; 18 | 19 | import java.lang.reflect.InvocationTargetException; 20 | import java.lang.reflect.Method; 21 | 22 | /** 23 | * Wraps a 'producer' method on a specific object. 24 | *

25 | *

This class only verifies the suitability of the method and event type if something fails. 26 | * Callers are expected 27 | * to verify their uses of this class. 28 | * 29 | * @author Jake Wharton 30 | */ 31 | class EventProducer { 32 | 33 | /** 34 | * Object sporting the producer method. 35 | */ 36 | final Object target; 37 | /** 38 | * Producer method. 39 | */ 40 | private final Method method; 41 | /** 42 | * Object hash code. 43 | */ 44 | private final int hashCode; 45 | /** 46 | * Should this producer produce events? 47 | * 标识该producer可否创建事件? 48 | */ 49 | private boolean valid = true; 50 | 51 | EventProducer(Object target, Method method) { 52 | if (target == null) { 53 | throw new NullPointerException("EventProducer target cannot be null."); 54 | } 55 | if (method == null) { 56 | throw new NullPointerException("EventProducer method cannot be null."); 57 | } 58 | 59 | this.target = target; 60 | this.method = method; 61 | 62 | /** 63 | * 取消 Java语言访问检查,提高反射速度 64 | */ 65 | method.setAccessible(true); 66 | 67 | /** 68 | * 计算hashCode 69 | */ 70 | // Compute hash code eagerly since we know it will be used frequently and we cannot estimate the runtime of the 71 | // target's hashCode call. 72 | final int prime = 31; 73 | hashCode = (prime + method.hashCode()) * prime + target.hashCode(); 74 | } 75 | 76 | public boolean isValid() { 77 | return valid; 78 | } 79 | 80 | /** 81 | * If invalidated, will subsequently refuse to produce events. 82 | *

83 | * Should be called when the wrapped object is unregistered from the Bus. 84 | */ 85 | public void invalidate() { 86 | valid = false; 87 | } 88 | 89 | /** 90 | * Invokes the wrapped producer method. 91 | * 92 | * @throws java.lang.IllegalStateException if previously invalidated. 93 | * @throws java.lang.reflect.InvocationTargetException if the wrapped method throws any {@link 94 | * Throwable} that is not 95 | * an {@link Error} ({@code Error}s are propagated as-is). 96 | */ 97 | public Object produceEvent() throws InvocationTargetException { 98 | /** 99 | * 检查是否可以创建事件 100 | */ 101 | if (!valid) { 102 | throw new IllegalStateException( 103 | toString() + " has been invalidated and can no longer produce events."); 104 | } 105 | 106 | /** 107 | * 调用target类里的 108 | * method方法 109 | * 创建一个event 110 | * 111 | * 这里的 112 | * target = listener 113 | * method = @Produce方法 114 | */ 115 | try { 116 | return method.invoke(target); 117 | } catch (IllegalAccessException e) { 118 | throw new AssertionError(e); 119 | } catch (InvocationTargetException e) { 120 | if (e.getCause() instanceof Error) { 121 | throw (Error) e.getCause(); 122 | } 123 | throw e; 124 | } 125 | } 126 | 127 | @Override 128 | public String toString() { 129 | return "[EventProducer " + method + "]"; 130 | } 131 | 132 | @Override 133 | public int hashCode() { 134 | return hashCode; 135 | } 136 | 137 | @Override 138 | public boolean equals(Object obj) { 139 | /** 140 | * 地址一样 视为相同 141 | */ 142 | if (this == obj) { 143 | return true; 144 | } 145 | 146 | /** 147 | * obj 为 null 148 | * 想都不想为false 149 | */ 150 | if (obj == null) { 151 | return false; 152 | } 153 | 154 | /** 155 | * 判断是否是一个Class 156 | */ 157 | if (getClass() != obj.getClass()) { 158 | return false; 159 | } 160 | 161 | final EventProducer other = (EventProducer) obj; 162 | 163 | /** 164 | * 到这里,是地址不一样,就意味着hashcode不一样 165 | * 但是如果method 和 target相同的话 166 | * 那么视为相同的 EventProducer 167 | */ 168 | return method.equals(other.method) && target == other.target; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /code/android/framework/otto/EventHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Square, Inc. 3 | * Copyright (C) 2007 The Guava Authors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.camnter.otto; 19 | 20 | import java.lang.reflect.InvocationTargetException; 21 | import java.lang.reflect.Method; 22 | 23 | /** 24 | * Wraps a single-argument 'handler' method on a specific object. 25 | *

26 | *

This class only verifies the suitability of the method and event type if something fails. Callers are expected t 27 | * verify their uses of this class. 28 | *

29 | *

Two EventHandlers are equivalent when they refer to the same method on the same object (not class). This 30 | * property is used to ensure that no handler method is registered more than once. 31 | * 32 | * @author Cliff Biffle 33 | */ 34 | class EventHandler { 35 | 36 | /** 37 | * Object sporting the handler method. 38 | */ 39 | private final Object target; 40 | /** 41 | * Handler method. 42 | */ 43 | private final Method method; 44 | /** 45 | * Object hash code. 46 | */ 47 | private final int hashCode; 48 | /** 49 | * Should this handler receive events? 50 | * 标识该Handler可否接受事件? 51 | */ 52 | private boolean valid = true; 53 | 54 | EventHandler(Object target, Method method) { 55 | if (target == null) { 56 | throw new NullPointerException("EventHandler target cannot be null."); 57 | } 58 | if (method == null) { 59 | throw new NullPointerException("EventHandler method cannot be null."); 60 | } 61 | 62 | this.target = target; 63 | this.method = method; 64 | 65 | /** 66 | * 取消 Java语言访问检查,提高反射速度 67 | */ 68 | method.setAccessible(true); 69 | 70 | /** 71 | * 计算hashCode 72 | */ 73 | // Compute hash code eagerly since we know it will be used frequently and we cannot estimate the runtime of the 74 | // target's hashCode call. 75 | final int prime = 31; 76 | hashCode = (prime + method.hashCode()) * prime + target.hashCode(); 77 | } 78 | 79 | public boolean isValid() { 80 | return valid; 81 | } 82 | 83 | /** 84 | * If invalidated, will subsequently refuse to handle events. 85 | *

86 | * Should be called when the wrapped object is unregistered from the Bus. 87 | * 当Bus.unregister() 的时候调用 88 | */ 89 | public void invalidate() { 90 | valid = false; 91 | } 92 | 93 | /** 94 | * Invokes the wrapped handler method to handle {@code event}. 95 | * 调用被封装的Handler方法去处理event 96 | * 97 | * @param event event to handle 98 | * @throws java.lang.IllegalStateException if previously invalidated. 99 | * @throws java.lang.reflect.InvocationTargetException if the wrapped method throws any {@link Throwable} that is not 100 | * an {@link Error} ({@code Error}s are propagated as-is). 101 | */ 102 | public void handleEvent(Object event) throws InvocationTargetException { 103 | /** 104 | * 检查是否可以接收事件 105 | */ 106 | if (!valid) { 107 | throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events."); 108 | } 109 | 110 | /** 111 | * 调用target类里的 112 | * method方法 113 | * 并传入event作为参数 114 | * 115 | * 这里的 116 | * target = listener 117 | * method = @Subscribe方法 118 | * event = 自定义的事件 119 | */ 120 | try { 121 | method.invoke(target, event); 122 | } catch (IllegalAccessException e) { 123 | throw new AssertionError(e); 124 | } catch (InvocationTargetException e) { 125 | if (e.getCause() instanceof Error) { 126 | throw (Error) e.getCause(); 127 | } 128 | throw e; 129 | } 130 | } 131 | 132 | @Override 133 | public String toString() { 134 | return "[EventHandler " + method + "]"; 135 | } 136 | 137 | @Override 138 | public int hashCode() { 139 | return hashCode; 140 | } 141 | 142 | /** 143 | * 判断两个 EventHandler 是否一样 144 | * 145 | * @param obj obj 146 | * @return boolean 147 | */ 148 | @Override 149 | public boolean equals(Object obj) { 150 | /** 151 | * 地址一样 视为相同 152 | */ 153 | if (this == obj) { 154 | return true; 155 | } 156 | 157 | /** 158 | * obj 为 null 159 | * 想都不想为false 160 | */ 161 | if (obj == null) { 162 | return false; 163 | } 164 | 165 | /** 166 | * 判断是否是一个Class 167 | */ 168 | if (getClass() != obj.getClass()) { 169 | return false; 170 | } 171 | 172 | final EventHandler other = (EventHandler) obj; 173 | 174 | /** 175 | * 到这里,是地址不一样,就意味着hashcode不一样 176 | * 但是如果method 和 target相同的话 177 | * 那么视为相同的 EventHandler 178 | */ 179 | return method.equals(other.method) && target == other.target; 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /10. RemoteViews.md: -------------------------------------------------------------------------------- 1 | 10. RemoteViews 2 | == 3 | 4 |
5 | 6 | ## 1. RemoteViews 原理 7 | 8 | **RemoteViews** 在实际开发中,主要用在通知栏和桌面小部件的开发过程中。**RemoteViews** 在通知栏上的应用中,主要是通过 **NotificationManager** 的 `notify` 方法来实现的,它除了默认效果外,还可以自定义布局。 9 | 10 | 桌面小部件则是通过 **AppWidgetProvider** 来实现的,**AppWidgetProvider 本质上是一个广播**。通知栏和桌面小部件的开发过程中都会用到 **RemoteViews**,**它们在更新界面时无法像在 Activity 里面那样去直接更新 View,这是因为二者的界面都运行在其他进程中,确切的说,是系统的 SystemServer 进程**。 11 | 12 | 为了跨进程更新界面,**RemoteViews** 提供了一系列 `set` 方法,并且这些方法只是 **View** 全部方法的子集,另外 **RemoteViews** 中所支持的 **View** 类型也是有限的。 13 | 14 | 15 |
16 | 17 | 18 | ## RemoteViews 在通知栏上的应用 19 | 20 | ```java 21 | // 普通 Notification 的创建 22 | Notification notification = new NotificationCompat.Builder(this) 23 | .setLargeIcon(BitmapUtils.drawableToBitmap( 24 | ResourcesCompat.getDrawable(this.getResources(), R.drawable.ic_camnter, 25 | this.getTheme()))) 26 | .setSmallIcon(R.drawable.ic_send_light_small) 27 | .setTicker("Save you from anything") 28 | .setWhen(System.currentTimeMillis()).build(); 29 | notification.flags = Notification.FLAG_AUTO_CANCEL; 30 | // 跳转到 RemoteViewsActivity 31 | Intent intent = new Intent(this, RemoteViewsActivity.class); 32 | PendingIntent pendingIntent = PendingIntent.getActivities(this, 0, new Intent[] { intent }, 33 | PendingIntent.FLAG_UPDATE_CURRENT); 34 | // 自定义 Notification 的布局 35 | RemoteViews remoteViews = new RemoteViews(this.getPackageName(), 36 | R.layout.notification_remote_normal); 37 | // 特定的 set 方法去修改规定的 View 38 | remoteViews.setImageViewResource(R.id.remote_icon, R.drawable.ic_camnter); 39 | remoteViews.setTextViewText(R.id.remote_text, "Save you from anything"); 40 | // Notification 设置上 RemoteViews 和 PendingIntent 41 | notification.contentView = remoteViews; 42 | notification.contentIntent = pendingIntent; 43 | NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this); 44 | managerCompat.notify(2, notification); 45 | ``` 46 | 47 |
48 | 49 | 50 | ## 2. RemoteViews 在桌面小部件的上的应用 51 | 52 | ### 2.1 小部件界面 53 | 54 | **@layout/appwidget.xml** 55 | 56 | ```xml 57 | 58 | 62 | 63 | 68 | 69 | 70 | ``` 71 | 72 |
73 | 74 | ### 2.2 小部件配置信息 75 | 76 | **@xml/appwidget_provider_info.xml** 77 | 78 | ```xml 79 | 80 | 86 | 87 | 88 | ``` 89 | 90 |
91 | 92 | ### 2.3 实现类 93 | 94 | ```java 95 | public class RemoteViewsAppWidgetProvider extends AppWidgetProvider { 96 | 97 | public static final String TAG = "RemoteViewsAppWidgetProvider"; 98 | public static final String ACTION = "com.camnter.newlife.widget.RemoteViewsAppWidgetProvider"; 99 | 100 | 101 | public RemoteViewsAppWidgetProvider() { 102 | super(); 103 | } 104 | 105 | 106 | @SuppressLint("LongLogTag") @Override 107 | public void onReceive(final Context context, Intent intent) { 108 | super.onReceive(context, intent); 109 | Log.i(TAG, "onReceive : action = " + intent.getAction()); 110 | 111 | // 这里判断是自己的 Action,做自己的事情,比如小工具被点击了要干啥,这里是做一个动画效果 112 | if (intent.getAction().equals(ACTION)) { 113 | Toast.makeText(context, 114 | "RemoteViewsAppWidgetProvider.Action = com.camnter.newlife.widget.RemoteViewsAppWidgetProvider", 115 | Toast.LENGTH_SHORT).show(); 116 | new Thread(new Runnable() { 117 | @Override public void run() { 118 | Bitmap srcBitmap = BitmapFactory.decodeResource(context.getResources(), 119 | R.drawable.ic_camnter); 120 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 121 | for (int i = 0; i < 37; i++) { 122 | float degree = (i * 10) % 360; 123 | RemoteViews remoteViews = new RemoteViews(context.getPackageName(), 124 | R.layout.appwidget); 125 | remoteViews.setImageViewBitmap(R.id.appwidget_image, 126 | rotateBitmap(srcBitmap, degree)); 127 | Intent clickIntent = new Intent(); 128 | clickIntent.setAction(ACTION); 129 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 130 | clickIntent, 0); 131 | remoteViews.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent); 132 | appWidgetManager.updateAppWidget( 133 | new ComponentName(context, RemoteViewsAppWidgetProvider.class), 134 | remoteViews); 135 | SystemClock.sleep(60); 136 | } 137 | 138 | } 139 | }); 140 | } 141 | } 142 | 143 | 144 | /** 145 | * 每次窗口小部件被点击更新都调用一次该方法 146 | */ 147 | @SuppressLint("LongLogTag") @Override 148 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, 149 | int[] appWidgetIds) { 150 | super.onUpdate(context, appWidgetManager, appWidgetIds); 151 | Log.i(TAG, "onUpdate"); 152 | final int counter = appWidgetIds.length; 153 | Log.i(TAG, "counter = " + counter); 154 | for (int appWidgetId : appWidgetIds) { 155 | this.onWidgetUpdate(context, appWidgetManager, appWidgetId); 156 | } 157 | 158 | } 159 | 160 | 161 | /** 162 | * 窗口小部件更新 163 | */ 164 | @SuppressLint("LongLogTag") private void onWidgetUpdate(Context context, 165 | AppWidgetManager appWidgetManger, 166 | int appWidgetId) { 167 | 168 | Log.i(TAG, "appWidgetId = " + appWidgetId); 169 | RemoteViews remoteViews = new RemoteViews(context.getPackageName(), 170 | R.layout.appwidget); 171 | 172 | // "窗口小部件"点击事件发送的Intent广播 173 | Intent intentClick = new Intent(); 174 | intentClick.setAction(ACTION); 175 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 176 | intentClick, 0); 177 | remoteViews.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent); 178 | appWidgetManger.updateAppWidget(appWidgetId, remoteViews); 179 | } 180 | 181 | 182 | private Bitmap rotateBitmap(Bitmap srcBitmap, float degree) { 183 | Matrix matrix = new Matrix(); 184 | matrix.reset(); 185 | matrix.setRotate(degree); 186 | return Bitmap.createBitmap(srcBitmap, 0, 0, 187 | srcBitmap.getWidth(), srcBitmap.getHeight(), matrix, true); 188 | } 189 | 190 | } 191 | ``` 192 | 193 |
194 | 195 | ### 2.4 AndroidManifest.xml 配置 196 | 197 | ```xml 198 | 199 | 202 | 203 | 204 | 205 | 206 | 207 | ``` 208 | 209 |
210 | 211 | ## 3. RemoteViews 工作过程 212 | 213 | 通知栏 和 桌面小部件分别由 **NotificationManager** 和 **AppWidgetManager** 管理。而 **NotificationManager** 和 **AppWidgetManager** 通过 **Binder** 分别和 **SystemServer** 进程中的 **NotificationManagerService** 以及 **AppWidgetManager** 进行 **通信**。由此可见,**通知栏 和 桌面小部件中的布局文件实际上是在 NotificationManagerService 以及 AppWidgetManager 中被加载的,而它们运行在系统的 SystemServer 中**,这就是 **跨进程通信** 的场景。 214 | 215 | 216 |
217 | 218 | ## 4. RemoteViews 工作原理 219 | 220 | ### 4.1 通过 Binder 传递到 SystemServer 进程 221 | 222 | 首先 **RemoteViews** 会通过 **Binder** 传递到 **SystemServer** 进程,这是因为 **RemoteViews** 实现了 **Parcelable** 接口,因此它可以 进程传输,系统会 **根据 RemoteViews 中的包名等信息去得到该应用的资源**。 223 | 224 |
225 | 226 | ### 4.2 LayoutInflater 加载 RemoteViews 227 | 228 | 然后会 **通过 LayoutInflater 去加载 RemoteViews 中的布局文件**。在 **SystemServer** 进程中加载后的布局文件是一个普通的 View,只不过相对于我们的进程它是一个 **RemoteViews**。 229 | 230 |
231 | 232 | ### 4.3 set 方法进行更新 233 | 234 | 接着 **系统会对 View 执行一系列界面更新任务**,这些任务就是之前通过 **set** 方法来提交的。**set** 方法对 View 所做的更新 **并不是立刻执行的**,在 **RemoteViews** 内部 **会记录所有的更新操作**,具体的执行时机要等到 **RemoteViews 被加载以后才执行**。 235 | 236 |
237 | 238 | ### 4.4 RemoteViews 显示 239 | 240 | 通过以上步骤后,**RemoteViews 就可以在 SystemServer 进程中显示了**,这就是我们所看到的通知栏消息或者桌面小部件。当需要更新 **RemoteViews** 时,我们需要调用一系列 **set** 方法并通过 **NotificationManagerService** 和 **AppWidgetManager** 来提交更新任务,具体的更新操作也是在 **SystemServer** 进程中完成的。 241 | 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /md/android/base/10. RemoteViews.md: -------------------------------------------------------------------------------- 1 | 10. RemoteViews 2 | == 3 | 4 |
5 | 6 | ## 1. RemoteViews 原理 7 | 8 | **RemoteViews** 在实际开发中,主要用在通知栏和桌面小部件的开发过程中。**RemoteViews** 在通知栏上的应用中,主要是通过 **NotificationManager** 的 `notify` 方法来实现的,它除了默认效果外,还可以自定义布局。 9 | 10 | 桌面小部件则是通过 **AppWidgetProvider** 来实现的,**AppWidgetProvider 本质上是一个广播**。通知栏和桌面小部件的开发过程中都会用到 **RemoteViews**,**它们在更新界面时无法像在 Activity 里面那样去直接更新 View,这是因为二者的界面都运行在其他进程中,确切的说,是系统的 SystemServer 进程**。 11 | 12 | 为了跨进程更新界面,**RemoteViews** 提供了一系列 `set` 方法,并且这些方法只是 **View** 全部方法的子集,另外 **RemoteViews** 中所支持的 **View** 类型也是有限的。 13 | 14 | 15 |
16 | 17 | 18 | ## RemoteViews 在通知栏上的应用 19 | 20 | ```java 21 | // 普通 Notification 的创建 22 | Notification notification = new NotificationCompat.Builder(this) 23 | .setLargeIcon(BitmapUtils.drawableToBitmap( 24 | ResourcesCompat.getDrawable(this.getResources(), R.drawable.ic_camnter, 25 | this.getTheme()))) 26 | .setSmallIcon(R.drawable.ic_send_light_small) 27 | .setTicker("Save you from anything") 28 | .setWhen(System.currentTimeMillis()).build(); 29 | notification.flags = Notification.FLAG_AUTO_CANCEL; 30 | // 跳转到 RemoteViewsActivity 31 | Intent intent = new Intent(this, RemoteViewsActivity.class); 32 | PendingIntent pendingIntent = PendingIntent.getActivities(this, 0, new Intent[] { intent }, 33 | PendingIntent.FLAG_UPDATE_CURRENT); 34 | // 自定义 Notification 的布局 35 | RemoteViews remoteViews = new RemoteViews(this.getPackageName(), 36 | R.layout.notification_remote_normal); 37 | // 特定的 set 方法去修改规定的 View 38 | remoteViews.setImageViewResource(R.id.remote_icon, R.drawable.ic_camnter); 39 | remoteViews.setTextViewText(R.id.remote_text, "Save you from anything"); 40 | // Notification 设置上 RemoteViews 和 PendingIntent 41 | notification.contentView = remoteViews; 42 | notification.contentIntent = pendingIntent; 43 | NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this); 44 | managerCompat.notify(2, notification); 45 | ``` 46 | 47 |
48 | 49 | 50 | ## 2. RemoteViews 在桌面小部件的上的应用 51 | 52 | ### 2.1 小部件界面 53 | 54 | **@layout/appwidget.xml** 55 | 56 | ```xml 57 | 58 | 62 | 63 | 68 | 69 | 70 | ``` 71 | 72 |
73 | 74 | ### 2.2 小部件配置信息 75 | 76 | **@xml/appwidget_provider_info.xml** 77 | 78 | ```xml 79 | 80 | 86 | 87 | 88 | ``` 89 | 90 |
91 | 92 | ### 2.3 实现类 93 | 94 | ```java 95 | public class RemoteViewsAppWidgetProvider extends AppWidgetProvider { 96 | 97 | public static final String TAG = "RemoteViewsAppWidgetProvider"; 98 | public static final String ACTION = "com.camnter.newlife.widget.RemoteViewsAppWidgetProvider"; 99 | 100 | 101 | public RemoteViewsAppWidgetProvider() { 102 | super(); 103 | } 104 | 105 | 106 | @SuppressLint("LongLogTag") @Override 107 | public void onReceive(final Context context, Intent intent) { 108 | super.onReceive(context, intent); 109 | Log.i(TAG, "onReceive : action = " + intent.getAction()); 110 | 111 | // 这里判断是自己的 Action,做自己的事情,比如小工具被点击了要干啥,这里是做一个动画效果 112 | if (intent.getAction().equals(ACTION)) { 113 | Toast.makeText(context, 114 | "RemoteViewsAppWidgetProvider.Action = com.camnter.newlife.widget.RemoteViewsAppWidgetProvider", 115 | Toast.LENGTH_SHORT).show(); 116 | new Thread(new Runnable() { 117 | @Override public void run() { 118 | Bitmap srcBitmap = BitmapFactory.decodeResource(context.getResources(), 119 | R.drawable.ic_camnter); 120 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 121 | for (int i = 0; i < 37; i++) { 122 | float degree = (i * 10) % 360; 123 | RemoteViews remoteViews = new RemoteViews(context.getPackageName(), 124 | R.layout.appwidget); 125 | remoteViews.setImageViewBitmap(R.id.appwidget_image, 126 | rotateBitmap(srcBitmap, degree)); 127 | Intent clickIntent = new Intent(); 128 | clickIntent.setAction(ACTION); 129 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 130 | clickIntent, 0); 131 | remoteViews.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent); 132 | appWidgetManager.updateAppWidget( 133 | new ComponentName(context, RemoteViewsAppWidgetProvider.class), 134 | remoteViews); 135 | SystemClock.sleep(60); 136 | } 137 | 138 | } 139 | }); 140 | } 141 | } 142 | 143 | 144 | /** 145 | * 每次窗口小部件被点击更新都调用一次该方法 146 | */ 147 | @SuppressLint("LongLogTag") @Override 148 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, 149 | int[] appWidgetIds) { 150 | super.onUpdate(context, appWidgetManager, appWidgetIds); 151 | Log.i(TAG, "onUpdate"); 152 | final int counter = appWidgetIds.length; 153 | Log.i(TAG, "counter = " + counter); 154 | for (int appWidgetId : appWidgetIds) { 155 | this.onWidgetUpdate(context, appWidgetManager, appWidgetId); 156 | } 157 | 158 | } 159 | 160 | 161 | /** 162 | * 窗口小部件更新 163 | */ 164 | @SuppressLint("LongLogTag") private void onWidgetUpdate(Context context, 165 | AppWidgetManager appWidgetManger, 166 | int appWidgetId) { 167 | 168 | Log.i(TAG, "appWidgetId = " + appWidgetId); 169 | RemoteViews remoteViews = new RemoteViews(context.getPackageName(), 170 | R.layout.appwidget); 171 | 172 | // "窗口小部件"点击事件发送的Intent广播 173 | Intent intentClick = new Intent(); 174 | intentClick.setAction(ACTION); 175 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 176 | intentClick, 0); 177 | remoteViews.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent); 178 | appWidgetManger.updateAppWidget(appWidgetId, remoteViews); 179 | } 180 | 181 | 182 | private Bitmap rotateBitmap(Bitmap srcBitmap, float degree) { 183 | Matrix matrix = new Matrix(); 184 | matrix.reset(); 185 | matrix.setRotate(degree); 186 | return Bitmap.createBitmap(srcBitmap, 0, 0, 187 | srcBitmap.getWidth(), srcBitmap.getHeight(), matrix, true); 188 | } 189 | 190 | } 191 | ``` 192 | 193 |
194 | 195 | ### 2.4 AndroidManifest.xml 配置 196 | 197 | ```xml 198 | 199 | 202 | 203 | 204 | 205 | 206 | 207 | ``` 208 | 209 |
210 | 211 | ## 3. RemoteViews 工作过程 212 | 213 | 通知栏 和 桌面小部件分别由 **NotificationManager** 和 **AppWidgetManager** 管理。而 **NotificationManager** 和 **AppWidgetManager** 通过 **Binder** 分别和 **SystemServer** 进程中的 **NotificationManagerService** 以及 **AppWidgetManager** 进行 **通信**。由此可见,**通知栏 和 桌面小部件中的布局文件实际上是在 NotificationManagerService 以及 AppWidgetManager 中被加载的,而它们运行在系统的 SystemServer 中**,这就是 **跨进程通信** 的场景。 214 | 215 | 216 |
217 | 218 | ## 4. RemoteViews 工作原理 219 | 220 | ### 4.1 通过 Binder 传递到 SystemServer 进程 221 | 222 | 首先 **RemoteViews** 会通过 **Binder** 传递到 **SystemServer** 进程,这是因为 **RemoteViews** 实现了 **Parcelable** 接口,因此它可以 进程传输,系统会 **根据 RemoteViews 中的包名等信息去得到该应用的资源**。 223 | 224 |
225 | 226 | ### 4.2 LayoutInflater 加载 RemoteViews 227 | 228 | 然后会 **通过 LayoutInflater 去加载 RemoteViews 中的布局文件**。在 **SystemServer** 进程中加载后的布局文件是一个普通的 View,只不过相对于我们的进程它是一个 **RemoteViews**。 229 | 230 |
231 | 232 | ### 4.3 set 方法进行更新 233 | 234 | 接着 **系统会对 View 执行一系列界面更新任务**,这些任务就是之前通过 **set** 方法来提交的。**set** 方法对 View 所做的更新 **并不是立刻执行的**,在 **RemoteViews** 内部 **会记录所有的更新操作**,具体的执行时机要等到 **RemoteViews 被加载以后才执行**。 235 | 236 |
237 | 238 | ### 4.4 RemoteViews 显示 239 | 240 | 通过以上步骤后,**RemoteViews 就可以在 SystemServer 进程中显示了**,这就是我们所看到的通知栏消息或者桌面小部件。当需要更新 **RemoteViews** 时,我们需要调用一系列 **set** 方法并通过 **NotificationManagerService** 和 **AppWidgetManager** 来提交更新任务,具体的更新操作也是在 **SystemServer** 进程中完成的。 241 | 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SaveAndroidResources 2 | == 3 | 4 |
5 | 6 | ## Android resources 7 | 8 |
9 | 10 | **Framework:** 11 | - [Android 系统特点](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/framework/Android%20%E7%B3%BB%E7%BB%9F%E7%89%B9%E7%82%B9.md) 12 | 13 | - [Android 代码编译和APK安装过程](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/framework/Android%20%E4%BB%A3%E7%A0%81%E7%BC%96%E8%AF%91%E5%92%8CAPK%E5%AE%89%E8%A3%85%E8%BF%87%E7%A8%8B.md) 14 | 15 | - [Android Framework 概述](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/framework/Android%20Framework%E6%A6%82%E8%BF%B0.md) 16 | 17 | - [Android APK的运行过程](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/framework/Android%20APK%E7%9A%84%E8%BF%90%E8%A1%8C%E8%BF%87%E7%A8%8B.md) 18 | 19 | - [Looper Handler MessageQueue](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/framework/Looper%20Handler%20MessageQueue.md) 20 | 21 | - [Binder](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/framework/Binder.md) 22 | 23 | - [Binder AIDL](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/framework/Binder%20AIDL.md) 24 | 25 |
26 | 27 | **Base:** 28 | - [1. 生命周期](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/base/1.%20%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.md) 29 | 30 | - [2. Activity 启动模式](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/2.%20Activity%20%E5%90%AF%E5%8A%A8%E6%A8%A1%E5%BC%8F.md) 31 | 32 | - [3. 跨进程通信 方式 及 问题](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/3.%20%E8%B7%A8%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1%20%E6%96%B9%E5%BC%8F%20%E5%8F%8A%20%E9%97%AE%E9%A2%98.md) 33 | 34 | - [4. Socket](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/4.%20Socket.md) 35 | 36 | - [5. View 滑动相关](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/5.%20View%20%E6%BB%91%E5%8A%A8%E7%9B%B8%E5%85%B3.md) 37 | 38 | - [6. View 的事件分发机制](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/6.%20View%20%E7%9A%84%E4%BA%8B%E4%BB%B6%E5%88%86%E5%8F%91%E6%9C%BA%E5%88%B6.md) 39 | 40 | - [7. View 的滑动冲突](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/7.%20View%20%E7%9A%84%E6%BB%91%E5%8A%A8%E5%86%B2%E7%AA%81.md) 41 | - [8. View 的工作原理](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/8.%20View%20%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.md) 42 | 43 | - [9. 自定义 View 分类 和 须知](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/9.%20%E8%87%AA%E5%AE%9A%E4%B9%89%20View%20%E5%88%86%E7%B1%BB%20%E5%92%8C%20%E9%A1%BB%E7%9F%A5.md) 44 | 45 | - [10. RemoteViews.md](https://github.com/CaMnter/SaveAndroidResources/blob/master/md/android/base/10.%20RemoteViews.md) 46 | 47 | 48 |
49 | 50 | **Analysis:** 51 | - [LruCache 源码解析](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/android/LruCache%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md) 52 | 53 | - [AsyncTask 源码分析](https://github.com/white37/AndroidSdkSourceAnalysis/blob/master/article/AsyncTask%E5%92%8CAsyncTaskCompat%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md) 54 | 55 | - [CoordinatorLayout 源码分析](https://github.com/desmond1121/AndroidSdkSourceAnalysis/blob/master/article/CoordinatorLayout%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md) 56 | - [Binder 源码分析](https://github.com/xdtianyu/SourceAnalysis/blob/master/Binder%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md) 57 | 58 |
59 | 60 | **Extensions** 61 | 62 | - [AndFix](https://github.com/alibaba/AndFix) 63 | 64 | - [HotFix](https://github.com/dodola/HotFix) 65 | 66 | - [RocooFix](https://github.com/dodola/RocooFix) 67 | 68 | - [Android 热补丁动态修复框架小结](http://blog.csdn.net/lmj623565791/article/details/49883661) 69 | 70 | - [Tinker_imitator](https://github.com/zzz40500/Tinker_imitator) 71 | 72 | - [Tinker_imitator 原理篇](http://www.jianshu.com/p/620c2b0490ec#rd) 73 | 74 | - [Android 热更新方案 Robust](http://tech.meituan.com/android_robust.html) 75 | 76 |
77 | 78 | - [为什么 Android 开发者应该使用 FlatBuffers 替代 JSON ?](http://blog.chengdazhi.com/index.php/201#rd?sukey=3997c0719f1515200399a26940a285f0580d23b92efa03e8d623f14c1ef31d7f793b0db2bae819daa39ff5c52ccfcb14) 79 | 80 | - [google/flatbuffers](https://github.com/google/flatbuffers) 81 | 82 | - [FlatBuffer](https://github.com/amitshekhariitbhu/FlatBuffer) 83 | 84 | - [Google FlatBuffers 使用教程](http://www.tuicool.com/articles/meyaiu3) 85 | 86 | - [FlatBuffers 体验](http://www.race604.com/flatbuffers-intro/) 87 | 88 | - [FlatBuffs](https://github.com/frogermcs/FlatBuffs) 89 | 90 |
91 | 92 | - [DynamicAPK](https://github.com/CtripMobile/DynamicAPK) 93 | 94 | - [携程 Android App 插件化和动态加载实践](http://www.infoq.com/cn/articles/ctrip-android-dynamic-loading) 95 | 96 | - [Android 插件化:从入门到放弃](http://www.infoq.com/cn/articles/android-plug-ins-from-entry-to-give-up?utm_campaign=rightbar_v2&utm_source=infoq&utm_medium=articles_link&utm_content=link_text) 97 | 98 | - [ZeusPlugin](https://github.com/iReaderAndroid/ZeusPlugin) 99 | 100 |
101 | 102 | - [Android 动态加载系列索引](http://kaedea.com/2016/02/05/android-dynamical-loading-00-index/) 103 | 104 | - [Android 动态加载简单易懂的介绍方式](http://kaedea.com/2016/02/06/android-dynamical-loading-01-introduction/) 105 | 106 | - [ClassLoader 工作机制](http://kaedea.com/2016/02/07/android-dynamical-loading-02-classloader/) 107 | 108 | - [加载 SD 卡中的 SO 库](http://kaedea.com/2016/06/01/android-dynamical-loading-03-so-in-sdcard/) 109 | 110 | - [使用 SO 库时要注意的一些问题](http://kaedea.com/2016/06/04/android-dynamical-loading-04-so-problems/) 111 | 112 | - [简单的动态加载模式](http://kaedea.com/2016/06/05/android-dynamical-loading-05-simple-mode/) 113 | 114 | - [代理 Activity 模式](http://kaedea.com/2016/06/10/android-dynamical-loading-06-proxy-activity/) 115 | 116 | - [动态创建 Activity 模式](http://kaedea.com/2016/06/14/android-dynamical-loading-07-dynamic-activity/) 117 | 118 |
119 | 120 | **WeMobileDev:** 121 | - [Android N 混合编译与对热补丁影响深度解析](https://github.com/WeMobileDev/article/blob/master/Android_N%E6%B7%B7%E5%90%88%E7%BC%96%E8%AF%91%E4%B8%8E%E5%AF%B9%E7%83%AD%E8%A1%A5%E4%B8%81%E5%BD%B1%E5%93%8D%E8%A7%A3%E6%9E%90.md) 122 | 123 | - [IPv6 socket 编程](https://github.com/WeMobileDev/article/blob/master/IPv6%20socket%E7%BC%96%E7%A8%8B.md) 124 | 125 | - [基于 TLS1.3 的微信安全通信协议 mmtls 介绍](https://github.com/WeMobileDev/article/blob/master/%E5%9F%BA%E4%BA%8ETLS1.3%E7%9A%84%E5%BE%AE%E4%BF%A1%E5%AE%89%E5%85%A8%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AEmmtls%E4%BB%8B%E7%BB%8D.md) 126 | 127 | - [微信 Android 热补丁实践演进之路 一 ](https://github.com/WeMobileDev/article/blob/master/%E5%BE%AE%E4%BF%A1Android%E7%83%AD%E8%A1%A5%E4%B8%81%E5%AE%9E%E8%B7%B5%E6%BC%94%E8%BF%9B%E4%B9%8B%E8%B7%AF.md) 128 | 129 | - [微信 Android 热补丁实践演进之路 二 ](http://dev.qq.com/topic/57ad7a70eaed47bb2699e68e) 130 | 131 |
132 | 133 | **Bugly:** 134 | - [Android 进程保活招式大全](http://dev.qq.com/topic/57ac4a0ea374c75371c08ce8) 135 | 136 | - [Android 界面性能调优手册](https://androidtest.org/android-graphics-performance-pattens/#11) 137 | 138 | - [微信 Android 资源混淆打包工具](http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=2&extra=page%3D3) 139 | 140 | - [Android Dex 文件格式详解](http://dev.qq.com/topic/578c6e97a9e8335e4b1fc8a0) 141 | 142 | - [内存泄露从入门到精通三部曲](http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=125&extra=page%3D1) 143 | 144 |
145 | 146 | **Other:** 147 | - [AndroidInterview-Q-A](https://github.com/JackyAndroid/AndroidInterview-Q-A/blob/master/README-CN.md) 148 | 149 |
150 | 151 | 152 | ## Java resources 153 | 154 |
155 | 156 | **Sort:** 157 | - [冒泡排序](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/java/sort/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F.md) 158 | 159 | - [快速排序](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/java/sort/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F.md) 160 | 161 | - [选择排序](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/java/sort/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F.md) 162 | 163 | - [插入排序](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/java/sort/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F.md) 164 | 165 | - [希尔排序](https://github.com/CaMnter/EasyAndroidResources/blob/master/md/java/sort/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F.md) 166 | 167 |
168 | 169 | **Network:** 170 | - [TCP/IP 协议三次握手与四次握手流程解析](http://www.2cto.com/net/201310/251896.html) 171 | 172 | - [详解 Https 是如何确保安全的?](http://www.wxtlife.com/2016/03/27/%E8%AF%A6%E8%A7%A3https%E6%98%AF%E5%A6%82%E4%BD%95%E7%A1%AE%E4%BF%9D%E5%AE%89%E5%85%A8%E7%9A%84%EF%BC%9F) 173 | - [SSL/TLS 协议运行机制的概述](http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html) 174 | 175 | 176 |
177 | 178 | **Other:** 179 | - [ArrayList 的实现原理](http://zhangshixi.iteye.com/blog/674856) 180 | 181 | - [HashMap 的实现原理](http://zhangshixi.iteye.com/blog/672697) 182 | 183 | - [Java 中 volatile 关键字的含义](http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html) 184 | 185 | - [Effective-JAVA-Summary](https://github.com/HugoMatilla/Effective-JAVA-Summary) 186 | 187 |
188 | 189 | 190 | ## Annotated source code 191 | 192 |
193 | 194 | **Android:** 195 | - [LruCache](https://github.com/CaMnter/EasyAndroidResources/blob/master/code/android/classes/LruCache.java) 196 | 197 | - [Otto](https://github.com/CaMnter/EasyAndroidResources/tree/master/code/android/framework/otto) 198 | 199 | - [agera](https://github.com/CaMnter/AndroidLife/tree/master/agera-1.0.0/src/main/java/com/google/android/agera) 200 | -------------------------------------------------------------------------------- /md/android/framework/Binder AIDL.md: -------------------------------------------------------------------------------- 1 | Binder AIDL 2 | == 3 | 4 |
5 | 6 | ## 1. 简介 7 | 8 | > AIDL: Android Interface Definition Language, 即 Android 接口定义语言。 9 | 10 | >什么是 AIDL? 11 | >为了使其他的应用程序也可以访问本应用程序提供的服务,Android 系统采用了远程过程调用( Remote Procedure Call, RPC )方式来实现。与很多其他的基于 RPC 的解决方案一样,Android 使用一种接口定义语言( Interface Definition Language, IDL )来公开服务的接口。我们知道 4个 Android 应用程序组件中的3个(Activity、BroadcastReceiver 和 ContentProvider)都可以进行跨进程访问,另外一个 Android 应用程序组件 Service 同样可以。因此,可以将这种可以跨进程访问的服务称为 AIDL( Android Interface Definition Language )服务。 12 | 13 |
14 | 15 | ## 2. 建立 AIDL 服务的步骤 16 | 17 | - **1.** Java包目录中建立一个扩展名为 .aidl 的文件。该文件的语法类似于 Java 代码,但会稍有不同。 18 | 19 | - **2.** 如果 aidl 文件的内容是正确的,ADT 会自动生成一个 Java 接口文件( .java )。 20 | 21 | - **3.** 建立一个服务类( Service 的子类 )。 22 | 23 | - **4.** 实现由 aidl 文件生成的 Java 接口。 24 | 25 | - **5.** 在 AndroidManifest.xml 文件中配置 AIDL 服务,尤其要注意的是,`` 标签中 `android:name` 的属性值就是客户端要引用该服务的 ID,也就是 Intent 类的参数值。 26 | 27 | 28 | ## 2.1 AIDL 原理 29 | 30 | **为什么 .aidl 文件生成的 java 代码能够进行 IPC (跨进程通信) 呢?** 31 | 32 | **答:**因为 生成的 java 代码**定义了 Binder 机制中 作为 Server 的 Binder 对象和 Client 中要使用的 Proxy 对象**。 Binder Server 创建后会启动一个隐藏线程,同时会创建 Binder 驱动中的 Binder Server 的 远程 mRemote 对象。**mRemote 其实就是一个 Proxy 对象** 。Client 通过 这个 Proxy 对象去和 Server 通信。 33 | 34 |
35 | 36 | ## 3. AIDL 实例 37 | 38 | ### 3.1 定义一个 .aidl 文件 39 | 40 | ```java 41 | // IPushMessage.aidl 42 | package com.camnter.newlife.aidl; 43 | 44 | // Declare any non-default types here with import statements 45 | 46 | interface IPushMessage { 47 | /** 48 | * Demonstrates some basic types that you can use as parameters 49 | * and return values in AIDL. 50 | */ 51 | void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, 52 | double aDouble, String aString); 53 | 54 | String onMessage(); 55 | 56 | } 57 | ``` 58 | 59 | ### 3.2 生成对应的 .java 文件 60 | 61 | ```java 62 | /* 63 | * This file is auto-generated. DO NOT MODIFY. 64 | */ 65 | package com.camnter.newlife.aidl; 66 | // Declare any non-default types here with import statements 67 | 68 | public interface IPushMessage extends android.os.IInterface { 69 | /** Local-side IPC implementation stub class. */ 70 | public static abstract class Stub extends android.os.Binder 71 | implements com.camnter.newlife.aidl.IPushMessage { 72 | private static final java.lang.String DESCRIPTOR = "com.camnter.newlife.aidl.IPushMessage"; 73 | 74 | 75 | /** Construct the stub at attach it to the interface. */ 76 | public Stub() { 77 | this.attachInterface(this, DESCRIPTOR); 78 | } 79 | 80 | 81 | /** 82 | * Cast an IBinder object into an com.camnter.newlife.aidl.IPushMessage interface, 83 | * generating a proxy if needed. 84 | */ 85 | public static com.camnter.newlife.aidl.IPushMessage asInterface(android.os.IBinder obj) { 86 | if ((obj == null)) { 87 | return null; 88 | } 89 | android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 90 | if (((iin != null) && (iin instanceof com.camnter.newlife.aidl.IPushMessage))) { 91 | return ((com.camnter.newlife.aidl.IPushMessage) iin); 92 | } 93 | return new com.camnter.newlife.aidl.IPushMessage.Stub.Proxy(obj); 94 | } 95 | 96 | 97 | @Override public android.os.IBinder asBinder() { 98 | return this; 99 | } 100 | 101 | 102 | @Override 103 | public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 104 | throws android.os.RemoteException { 105 | switch (code) { 106 | case INTERFACE_TRANSACTION: { 107 | reply.writeString(DESCRIPTOR); 108 | return true; 109 | } 110 | case TRANSACTION_basicTypes: { 111 | data.enforceInterface(DESCRIPTOR); 112 | int _arg0; 113 | _arg0 = data.readInt(); 114 | long _arg1; 115 | _arg1 = data.readLong(); 116 | boolean _arg2; 117 | _arg2 = (0 != data.readInt()); 118 | float _arg3; 119 | _arg3 = data.readFloat(); 120 | double _arg4; 121 | _arg4 = data.readDouble(); 122 | java.lang.String _arg5; 123 | _arg5 = data.readString(); 124 | this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); 125 | reply.writeNoException(); 126 | return true; 127 | } 128 | case TRANSACTION_onMessage: { 129 | data.enforceInterface(DESCRIPTOR); 130 | java.lang.String _result = this.onMessage(); 131 | reply.writeNoException(); 132 | reply.writeString(_result); 133 | return true; 134 | } 135 | } 136 | return super.onTransact(code, data, reply, flags); 137 | } 138 | 139 | 140 | private static class Proxy implements com.camnter.newlife.aidl.IPushMessage { 141 | private android.os.IBinder mRemote; 142 | 143 | 144 | Proxy(android.os.IBinder remote) { 145 | mRemote = remote; 146 | } 147 | 148 | 149 | @Override public android.os.IBinder asBinder() { 150 | return mRemote; 151 | } 152 | 153 | 154 | public java.lang.String getInterfaceDescriptor() { 155 | return DESCRIPTOR; 156 | } 157 | 158 | 159 | /** 160 | * Demonstrates some basic types that you can use as parameters 161 | * and return values in AIDL. 162 | */ 163 | @Override 164 | public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 165 | throws android.os.RemoteException { 166 | android.os.Parcel _data = android.os.Parcel.obtain(); 167 | android.os.Parcel _reply = android.os.Parcel.obtain(); 168 | try { 169 | _data.writeInterfaceToken(DESCRIPTOR); 170 | _data.writeInt(anInt); 171 | _data.writeLong(aLong); 172 | _data.writeInt(((aBoolean) ? (1) : (0))); 173 | _data.writeFloat(aFloat); 174 | _data.writeDouble(aDouble); 175 | _data.writeString(aString); 176 | mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); 177 | _reply.readException(); 178 | } finally { 179 | _reply.recycle(); 180 | _data.recycle(); 181 | } 182 | } 183 | 184 | 185 | @Override public java.lang.String onMessage() throws android.os.RemoteException { 186 | android.os.Parcel _data = android.os.Parcel.obtain(); 187 | android.os.Parcel _reply = android.os.Parcel.obtain(); 188 | java.lang.String _result; 189 | try { 190 | _data.writeInterfaceToken(DESCRIPTOR); 191 | mRemote.transact(Stub.TRANSACTION_onMessage, _data, _reply, 0); 192 | _reply.readException(); 193 | _result = _reply.readString(); 194 | } finally { 195 | _reply.recycle(); 196 | _data.recycle(); 197 | } 198 | return _result; 199 | } 200 | } 201 | 202 | static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 203 | static final int TRANSACTION_onMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 204 | } 205 | 206 | /** 207 | * Demonstrates some basic types that you can use as parameters 208 | * and return values in AIDL. 209 | */ 210 | public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 211 | throws android.os.RemoteException; 212 | 213 | public java.lang.String onMessage() throws android.os.RemoteException; 214 | } 215 | 216 | ``` 217 | 218 | ## 4. 解析 AIDL 生成的 Java 文件 219 | 220 | 生成的 Java 文件 是**一个接口**。 221 | 222 | ### 4.1 aidl 文件生成了三个模块 223 | - **1.** 本 `Ixx` 接口,并且定义了 aidl 文件里描述的方法。 224 | - **2.** 静态抽象类 Stub,继承了 `android.os.Binder` ,并且实现了 aidl 生成的 `Ixx` 接口。 225 | - **3.** 静态内部类 Stub.Proxy,也实现了 aidl 生成的 `Ixx` 接口。 226 | 227 | ### 4.2 Stub 228 | 继承了 Binder,实现了我们在 aidl 生成的接口,定义了需要跨进程通信的方法。作为 Server 存在着。 229 | 230 | ### 4.3 Stub.Proxy 231 | 也实现了 aidl 生成的接口,Cilent 可以与 Proxy 通信,间接地和 Server 端进行了通信。 `private android.os.IBinder mRemote;` 是 Proxy 类 里的唯一属性。Proxy 实现的 aidl 接口方法中,都调用了 `mRemote.transact` 去与 Server 进行通信。 232 | 233 | ### 4.3 Proxy 与 Stub 的 Code 对应 234 | **以上面实例中生成的接口为例:** 235 | Proxy 类定义了 两个 code 去区分接口方法:`TRANSACTION_basicTypes` 和 `TRANSACTION_onMessage`。 236 | 同时,Stub 类的 `onTransact` 方法里也有对应 `TRANSACTION_basicTypes` 和 `TRANSACTION_onMessage` code的分发处理。 237 | 238 | ### 4.4 Stub.onTransact 的作用? 239 | > onTransact 方法主要是在 用户空间 和 内核空间 中进行数据的交换,实现进程间数据的交互。 240 | 241 | ### 4.5 asInterface 方法将 IBinder 对象转换成对应接口 242 | ```java 243 | /** 244 | * Cast an IBinder object into an com.camnter.newlife.aidl.IPushMessage interface, 245 | * generating a proxy if needed. 246 | */ 247 | public static com.camnter.newlife.aidl.IPushMessage asInterface(android.os.IBinder obj) { 248 | if ((obj == null)) { 249 | return null; 250 | } 251 | android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 252 | if (((iin != null) && (iin instanceof com.camnter.newlife.aidl.IPushMessage))) { 253 | return ((com.camnter.newlife.aidl.IPushMessage) iin); 254 | } 255 | return new Stub.Proxy(obj); 256 | } 257 | ``` 258 | 259 | 260 | ### 4.6 为什么要用 asInterface 方法将 IBinder 对象进行转换? 261 | **答:** 因为通信的话,**假如当前进程处在 Server 的进程,那么就不需要 Proxy 对象进行通信;假如你当前进程不在 Server 进程,就需要 Proxy 对象进行 IPC 通信**,证明这句话的代码就是上述的,如果 `queryLocalInterface` 查询本地接口,存在的话,就返回该接口(此时就是处在 Server 进程的情况);没找到,直接 `new` 一个 Proxy 提供 IPC 通信功能。 262 | -------------------------------------------------------------------------------- /code/android/framework/otto/AnnotatedHandlerFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Square, Inc. 3 | * Copyright (C) 2007 The Guava Authors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.camnter.otto; 19 | 20 | import java.lang.reflect.Method; 21 | import java.lang.reflect.Modifier; 22 | import java.util.HashMap; 23 | import java.util.HashSet; 24 | import java.util.Map; 25 | import java.util.Set; 26 | import java.util.concurrent.ConcurrentHashMap; 27 | import java.util.concurrent.ConcurrentMap; 28 | 29 | /** 30 | * Helper methods for finding methods annotated with {@link Produce} and {@link Subscribe}. 31 | * 32 | * @author Cliff Biffle 33 | * @author Louis Wasserman 34 | * @author Jake Wharton 35 | */ 36 | final class AnnotatedHandlerFinder { 37 | 38 | /** 39 | * Cache event bus producer methods for each class. 40 | * 缓存 @Produce 方法 41 | */ 42 | private static final ConcurrentMap, Map, Method>> PRODUCERS_CACHE = 43 | new ConcurrentHashMap, Map, Method>>(); 44 | 45 | /** 46 | * Cache event bus subscriber methods for each class. 47 | * 缓存 @Subscribe 方法 48 | */ 49 | private static final ConcurrentMap, Map, Set>> SUBSCRIBERS_CACHE = 50 | new ConcurrentHashMap, Map, Set>>(); 51 | 52 | private static void loadAnnotatedProducerMethods(Class listenerClass, 53 | Map, Method> producerMethods) { 54 | Map, Set> subscriberMethods = new HashMap, Set>(); 55 | loadAnnotatedMethods(listenerClass, producerMethods, subscriberMethods); 56 | } 57 | 58 | private static void loadAnnotatedSubscriberMethods(Class listenerClass, 59 | Map, Set> subscriberMethods) { 60 | Map, Method> producerMethods = new HashMap, Method>(); 61 | loadAnnotatedMethods(listenerClass, producerMethods, subscriberMethods); 62 | } 63 | 64 | /** 65 | * Load all methods annotated with {@link Produce} or {@link Subscribe} into their respective caches for the 66 | * specified class. 67 | * 读取目标类的 所有 @Produce 和 @Subscribe方法 68 | */ 69 | private static void loadAnnotatedMethods(Class listenerClass, 70 | Map, Method> producerMethods, Map, Set> subscriberMethods) { 71 | for (Method method : listenerClass.getDeclaredMethods()) { 72 | // The compiler sometimes creates synthetic bridge methods as part of the 73 | // type erasure process. As of JDK8 these methods now include the same 74 | // annotations as the original declarations. They should be ignored for 75 | // subscribe/produce. 76 | 77 | /** 78 | * 判断方式是否桥接,跟泛型有关 79 | */ 80 | if (method.isBridge()) { 81 | continue; 82 | } 83 | 84 | /************* 85 | * Subscribe * 86 | *************/ 87 | 88 | /** 89 | * 如果方法的注解是 @Subscribe 90 | */ 91 | if (method.isAnnotationPresent(Subscribe.class)) { 92 | 93 | /** 94 | * 拿到方法的参数 95 | */ 96 | Class[] parameterTypes = method.getParameterTypes(); 97 | 98 | /** 99 | * 方法参数是否为 1 ,为 1 抛出异常 100 | */ 101 | if (parameterTypes.length != 1) { 102 | throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires " 103 | + parameterTypes.length + " arguments. Methods must require a single argument."); 104 | } 105 | 106 | /** 107 | * 参数是否是接口 ,是接口抛出异常 108 | */ 109 | Class eventType = parameterTypes[0]; 110 | if (eventType.isInterface()) { 111 | throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType 112 | + " which is an interface. Subscription must be on a concrete class type."); 113 | } 114 | 115 | /** 116 | * 方法是否public , 不是public抛出异常 117 | */ 118 | if ((method.getModifiers() & Modifier.PUBLIC) == 0) { 119 | throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType 120 | + " but is not 'public'."); 121 | } 122 | 123 | /** 124 | * 通过类去获取原来该类所有的 @Subscribe 注解的方法 125 | */ 126 | Set methods = subscriberMethods.get(eventType); 127 | 128 | /** 129 | * 如果原本没有一个方法Set,创建一个新的Set再,直接add 130 | * 有Set,则直接add 131 | */ 132 | if (methods == null) { 133 | methods = new HashSet(); 134 | subscriberMethods.put(eventType, methods); 135 | } 136 | methods.add(method); 137 | } else if (method.isAnnotationPresent(Produce.class)) { 138 | 139 | /*********** 140 | * Produce * 141 | ***********/ 142 | 143 | /** 144 | * 拿到方法的参数 145 | */ 146 | Class[] parameterTypes = method.getParameterTypes(); 147 | 148 | /** 149 | * 方法参数是否为 1 ,为 1 抛出异常 150 | */ 151 | if (parameterTypes.length != 0) { 152 | throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires " 153 | + parameterTypes.length + " arguments. Methods must require zero arguments."); 154 | } 155 | 156 | /** 157 | * 方法返回值是否为 Void 类 ,为 Void 类 抛出异常 158 | */ 159 | if (method.getReturnType() == Void.class) { 160 | throw new IllegalArgumentException("Method " + method 161 | + " has a return type of void. Must declare a non-void type."); 162 | } 163 | 164 | /** 165 | * 拿到方法返回类型 166 | */ 167 | Class eventType = method.getReturnType(); 168 | 169 | /** 170 | * 返回类型是否是一个接口 , 是接口则抛出异常 171 | */ 172 | if (eventType.isInterface()) { 173 | throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType 174 | + " which is an interface. Producers must return a concrete class type."); 175 | } 176 | 177 | /** 178 | * 返回类型是否 为 Void 类型 179 | */ 180 | if (eventType.equals(Void.TYPE)) { 181 | throw new IllegalArgumentException("Method " + method + " has @Produce annotation but has no return type."); 182 | } 183 | 184 | /** 185 | * 方法是否public , 不是public抛出异常 186 | */ 187 | if ((method.getModifiers() & Modifier.PUBLIC) == 0) { 188 | throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType 189 | + " but is not 'public'."); 190 | } 191 | 192 | /** 193 | * 如果已经保存 相同 返回类型 @Produce 方法 194 | * 则抛出异常 195 | */ 196 | if (producerMethods.containsKey(eventType)) { 197 | throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered."); 198 | } 199 | 200 | /** 201 | * 走到这,通过上面的条件 202 | * 则add 203 | */ 204 | producerMethods.put(eventType, method); 205 | } 206 | } 207 | /** 208 | * 缓存所有 @Produce 209 | * 缓存所有 @Subscribe 210 | */ 211 | PRODUCERS_CACHE.put(listenerClass, producerMethods); 212 | SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods); 213 | } 214 | 215 | /** 216 | * This implementation finds all methods marked with a {@link Produce} annotation. 217 | * 寻找所有 @Produce 方法,封装成 EventProducer( @Produce方法 和 一些数据 ) 218 | */ 219 | static Map, EventProducer> findAllProducers(Object listener) { 220 | 221 | /** 222 | * 拿到目标类 223 | */ 224 | final Class listenerClass = listener.getClass(); 225 | Map, EventProducer> handlersInMethod = new HashMap, EventProducer>(); 226 | 227 | /** 228 | * 拿到 @Produce 缓存Map 229 | */ 230 | Map, Method> methods = PRODUCERS_CACHE.get(listenerClass); 231 | 232 | /** 233 | * 开始添加那些没被缓存的 @Produce 方法 234 | */ 235 | if (null == methods) { 236 | methods = new HashMap, Method>(); 237 | loadAnnotatedProducerMethods(listenerClass, methods); 238 | } 239 | 240 | /** 241 | * 缓存 @Produce Map 有内容 242 | * 243 | * 逐个将对应的类和Produce方法放入 EventProducer 和 handlersInMethod内 244 | */ 245 | if (!methods.isEmpty()) { 246 | for (Map.Entry, Method> e : methods.entrySet()) { 247 | EventProducer producer = new EventProducer(listener, e.getValue()); 248 | handlersInMethod.put(e.getKey(), producer); 249 | } 250 | } 251 | 252 | return handlersInMethod; 253 | } 254 | 255 | /** 256 | * This implementation finds all methods marked with a {@link Subscribe} annotation. 257 | * 寻找所有 @Subscribe 方法,封装成 EventProducer( @Subscribe方法 和 一些数据 ) 258 | */ 259 | static Map, Set> findAllSubscribers(Object listener) { 260 | 261 | /** 262 | * 拿到目标类 263 | */ 264 | Class listenerClass = listener.getClass(); 265 | Map, Set> handlersInMethod = new HashMap, Set>(); 266 | 267 | /** 268 | * 拿到 @Subscribe 缓存Map 269 | */ 270 | Map, Set> methods = SUBSCRIBERS_CACHE.get(listenerClass); 271 | 272 | /** 273 | * 开始添加那些没被缓存的 @Subscribe 方法 274 | */ 275 | if (null == methods) { 276 | methods = new HashMap, Set>(); 277 | loadAnnotatedSubscriberMethods(listenerClass, methods); 278 | } 279 | 280 | /** 281 | * 缓存 @Subscribe Map 有内容 282 | * 283 | * 创建一个EventHandler集合,缓存类对应的 @Subscribe 方法 284 | * 从这个可以看得出 一个类能有 N个 @Subscribe 方法 285 | */ 286 | if (!methods.isEmpty()) { 287 | for (Map.Entry, Set> e : methods.entrySet()) { 288 | Set handlers = new HashSet(); 289 | for (Method m : e.getValue()) { 290 | handlers.add(new EventHandler(listener, m)); 291 | } 292 | handlersInMethod.put(e.getKey(), handlers); 293 | } 294 | } 295 | 296 | return handlersInMethod; 297 | } 298 | 299 | private AnnotatedHandlerFinder() { 300 | // No instances. 301 | } 302 | 303 | } 304 | -------------------------------------------------------------------------------- /code/android/classes/LruCache.java: -------------------------------------------------------------------------------- 1 | 2 | import java.util.LinkedHashMap; 3 | import java.util.Map; 4 | 5 | /** 6 | * A cache that holds strong references to a limited number of values. Each time 7 | * a value is accessed, it is moved to the head of a queue. When a value is 8 | * added to a full cache, the value at the end of that queue is evicted and may 9 | * become eligible for garbage collection. 10 | * 11 | *

If your cached values hold resources that need to be explicitly released, 12 | * override {@link #entryRemoved}. 13 | * 14 | *

If a cache miss should be computed on demand for the corresponding keys, 15 | * override {@link #create}. This simplifies the calling code, allowing it to 16 | * assume a value will always be returned, even when there's a cache miss. 17 | * 18 | *

By default, the cache size is measured in the number of entries. Override 19 | * {@link #sizeOf} to size the cache in different units. For example, this cache 20 | * is limited to 4MiB of bitmaps: 21 | *

   {@code
 22 |  *   int cacheSize = 4 * 1024 * 1024; // 4MiB
 23 |  *   LruCache bitmapCache = new LruCache(cacheSize) {
 24 |  *       protected int sizeOf(String key, Bitmap value) {
 25 |  *           return value.getByteCount();
 26 |  *       }
 27 |  *   }}
28 | * 29 | *

This class is thread-safe. Perform multiple cache operations atomically by 30 | * synchronizing on the cache:

   {@code
 31 |  *   synchronized (cache) {
 32 |  *     if (cache.get(key) == null) {
 33 |  *         cache.put(key, value);
 34 |  *     }
 35 |  *   }}
36 | * 37 | *

This class does not allow null to be used as a key or value. A return 38 | * value of null from {@link #get}, {@link #put} or {@link #remove} is 39 | * unambiguous: the key was not in the cache. 40 | * 41 | *

This class appeared in Android 3.1 (Honeycomb MR1); it's available as part 42 | * of Android's 43 | * Support Package for earlier releases. 44 | */ 45 | public class LruCache { 46 | private final LinkedHashMap map; 47 | 48 | /** 49 | * 缓存大小的单位。不规定元素的数量。 50 | */ 51 | // 已经存储的数据大小 52 | private int size; 53 | // 最大存储大小 54 | private int maxSize; 55 | 56 | // 调用put的次数 57 | private int putCount; 58 | // 调用create的次数 59 | private int createCount; 60 | // 收回的次数 (如果出现) 61 | private int evictionCount; 62 | // 命中的次数(取出数据的成功次数) 63 | private int hitCount; 64 | // 丢失的次数(取出数据的丢失次数) 65 | private int missCount; 66 | 67 | 68 | /** 69 | * LruCache的构造方法:需要传入最大缓存个数 70 | */ 71 | public LruCache(int maxSize) { 72 | // 最大缓存个数小于0,会抛出IllegalArgumentException 73 | if (maxSize <= 0) { 74 | throw new IllegalArgumentException("maxSize <= 0"); 75 | } 76 | this.maxSize = maxSize; 77 | /* 78 | * 初始化LinkedHashMap 79 | * 第一个参数:initialCapacity,初始大小 80 | * 第二个参数:loadFactor,负载因子=0.75f 81 | * 第三个参数:accessOrder=true,基于访问顺序;accessOrder=false,基于插入顺序 82 | */ 83 | this.map = new LinkedHashMap(0, 0.75f, true); 84 | } 85 | 86 | 87 | /** 88 | * 设置缓存的大小。 89 | */ 90 | public void resize(int maxSize) { 91 | if (maxSize <= 0) { 92 | throw new IllegalArgumentException("maxSize <= 0"); 93 | } 94 | // 防止外部多线程的情况下设置缓存大小造成的线程不安全 95 | synchronized (this) { 96 | this.maxSize = maxSize; 97 | } 98 | // 重整数据 99 | trimToSize(maxSize); 100 | } 101 | 102 | 103 | /** 104 | * 根据key查询缓存,如果存在于缓存或者被create方法创建了。 105 | * 如果值返回了,那么它将被移动到双向循环链表的的尾部。 106 | * 如果如果没有缓存的值,则返回null。 107 | */ 108 | public final V get(K key) { 109 | if (key == null) { 110 | throw new NullPointerException("key == null"); 111 | } 112 | 113 | V mapValue; 114 | synchronized (this) { 115 | // LinkHashMap 如果设置按照访问顺序的话,这里每次get都会重整数据顺序 116 | mapValue = map.get(key); 117 | // 计算 命中次数 118 | if (mapValue != null) { 119 | hitCount++; 120 | return mapValue; 121 | } 122 | // 计算 丢失次数 123 | missCount++; 124 | } 125 | 126 | /* 127 | * 官方解释: 128 | * 尝试创建一个值,这可能需要很长时间,并且Map可能在create()返回的值时有所不同。如果在create()执行的时 129 | * 候,一个冲突的值被添加到Map,我们在Map中删除这个值,释放被创造的值。 130 | */ 131 | V createdValue = create(key); 132 | if (createdValue == null) { 133 | return null; 134 | } 135 | 136 | /*************************** 137 | * 不覆写create方法走不到下面 * 138 | ***************************/ 139 | 140 | /* 141 | * 正常情况走不到这里 142 | * 走到这里的话 说明 实现了自定义的 create(K key) 逻辑 143 | * 因为默认的 create(K key) 逻辑为null 144 | */ 145 | synchronized (this) { 146 | // 记录 create 的次数 147 | createCount++; 148 | // 将自定义create创建的值,放入LinkedHashMap中,如果key已经存在,会返回 之前相同key 的值 149 | mapValue = map.put(key, createdValue); 150 | 151 | // 如果之前存在相同key的value,即有冲突。 152 | if (mapValue != null) { 153 | /* 154 | * 有冲突 155 | * 所以 撤销 刚才的 操作 156 | * 将 之前相同key 的值 重新放回去 157 | */ 158 | map.put(key, mapValue); 159 | } else { 160 | // 拿到键值对,计算出在容量中的相对长度,然后加上 161 | size += safeSizeOf(key, createdValue); 162 | } 163 | } 164 | 165 | // 如果上面 判断出了 将要放入的值发生冲突 166 | if (mapValue != null) { 167 | /* 168 | * 刚才create的值被删除了,原来的 之前相同key 的值被重新添加回去了 169 | * 告诉 自定义 的 entryRemoved 方法 170 | */ 171 | entryRemoved(false, key, createdValue, mapValue); 172 | return mapValue; 173 | } else { 174 | // 上面 进行了 size += 操作 所以这里要重整长度 175 | trimToSize(maxSize); 176 | return createdValue; 177 | } 178 | } 179 | 180 | 181 | /** 182 | * 给对应key缓存value,该value将被移动到队头。 183 | */ 184 | public final V put(K key, V value) { 185 | if (key == null || value == null) { 186 | throw new NullPointerException("key == null || value == null"); 187 | } 188 | 189 | V previous; 190 | synchronized (this) { 191 | // 记录 put 的次数 192 | putCount++; 193 | // 拿到键值对,计算出在容量中的相对长度,然后加上 194 | size += safeSizeOf(key, value); 195 | /* 196 | * 放入 key value 197 | * 如果 之前存在key 则返回 之前key 的value 198 | * 记录在 previous 199 | */ 200 | previous = map.put(key, value); 201 | // 如果存在冲突 202 | if (previous != null) { 203 | // 计算出 冲突键值 在容量中的相对长度,然后减去 204 | size -= safeSizeOf(key, previous); 205 | } 206 | } 207 | 208 | // 如果上面发生冲突 209 | if (previous != null) { 210 | /* 211 | * previous值被剔除了,此次添加的 value 已经作为key的 新值 212 | * 告诉 自定义 的 entryRemoved 方法 213 | */ 214 | entryRemoved(false, key, previous, value); 215 | } 216 | trimToSize(maxSize); 217 | return previous; 218 | } 219 | 220 | 221 | /** 222 | * 删除最旧的数据直到剩余的数据的总数以下要求的大小。 223 | */ 224 | public void trimToSize(int maxSize) { 225 | /* 226 | * 这是一个死循环, 227 | * 1.只有 扩容 的情况下能立即跳出 228 | * 2.非扩容的情况下,map的数据会一个一个删除,直到map里没有值了,就会跳出 229 | */ 230 | while (true) { 231 | K key; 232 | V value; 233 | synchronized (this) { 234 | // 在重新调整容量大小前,本身容量就为空的话,会出异常的。 235 | if (size < 0 || (map.isEmpty() && size != 0)) { 236 | throw new IllegalStateException( 237 | getClass().getName() + ".sizeOf() is reporting inconsistent results!"); 238 | } 239 | // 如果是 扩容 或者 map为空了,就会中断,因为扩容不会涉及到丢弃数据的情况 240 | if (size <= maxSize || map.isEmpty()) { 241 | break; 242 | } 243 | 244 | Map.Entry toEvict = map.entrySet().iterator().next(); 245 | key = toEvict.getKey(); 246 | value = toEvict.getValue(); 247 | map.remove(key); 248 | // 拿到键值对,计算出在容量中的相对长度,然后减去。 249 | size -= safeSizeOf(key, value); 250 | // 添加一次收回次数 251 | evictionCount++; 252 | } 253 | /* 254 | * 将最后一次删除的最少访问数据回调出去 255 | */ 256 | entryRemoved(true, key, value, null); 257 | } 258 | } 259 | 260 | 261 | /** 262 | * 如果对应key的entry存在,则删除。 263 | */ 264 | public final V remove(K key) { 265 | if (key == null) { 266 | throw new NullPointerException("key == null"); 267 | } 268 | 269 | V previous; 270 | synchronized (this) { 271 | // 移除对应 键值对 ,并将移除的value 存放在 previous 272 | previous = map.remove(key); 273 | if (previous != null) { 274 | // 拿到键值对,计算出在容量中的相对长度,然后减去。 275 | size -= safeSizeOf(key, previous); 276 | } 277 | } 278 | 279 | // 如果 Map 中存在 该key ,并且成功移除了 280 | if (previous != null) { 281 | /* 282 | * 会通知 自定义的 entryRemoved 283 | * previous 已经被删除了 284 | */ 285 | entryRemoved(false, key, previous, null); 286 | } 287 | 288 | return previous; 289 | } 290 | 291 | 292 | /** 293 | * 1.当被回收或者删掉时调用。该方法当value被回收释放存储空间时被remove调用 294 | * 或者替换条目值时put调用,默认实现什么都没做。 295 | * 2.该方法没用同步调用,如果其他线程访问缓存时,该方法也会执行。 296 | * 3.evicted=true:如果该条目被删除空间 (表示 进行了trimToSize or remove) evicted=false:put冲突后 或 get里成功create后 297 | * 导致 298 | * 4.newValue!=null,那么则被put()或get()调用。 299 | */ 300 | protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) { 301 | } 302 | 303 | 304 | /** 305 | * 1.缓存丢失之后计算相应的key的value后调用。 306 | * 返回计算后的值,如果没有value可以计算返回null。 307 | * 默认的实现返回null。 308 | * 2.该方法没用同步调用,如果其他线程访问缓存时,该方法也会执行。 309 | * 3.当这个方法返回的时候,如果对应key的value存在缓存内,被创建的value将会被entryRemoved()释放或者丢弃。 310 | * 这情况可以发生在多线程在同一时间上请求相同key(导致多个value被创建了),或者单线程中调用了put()去创建一个 311 | * 相同key的value 312 | */ 313 | protected V create(K key) { 314 | return null; 315 | } 316 | 317 | 318 | /** 319 | * 计算 该 键值对 的相对长度 320 | * 如果不覆写 sizeOf 实现特殊逻辑的话,默认长度是1。 321 | */ 322 | private int safeSizeOf(K key, V value) { 323 | int result = sizeOf(key, value); 324 | if (result < 0) { 325 | throw new IllegalStateException("Negative size: " + key + "=" + value); 326 | } 327 | return result; 328 | } 329 | 330 | 331 | /** 332 | * 返回条目在用户定义单位的大小。默认实现返回1,这样的大小是条目的数量并且最大的大小是条目的最大数量。 333 | * 一个条目的大小必须不能在缓存中改变 334 | */ 335 | protected int sizeOf(K key, V value) { 336 | return 1; 337 | } 338 | 339 | 340 | /** 341 | * Clear the cache, calling {@link #entryRemoved} on each removed entry. 342 | * 清理缓存 343 | */ 344 | public final void evictAll() { 345 | trimToSize(-1); // -1 will evict 0-sized elements 346 | } 347 | 348 | 349 | /** 350 | * 对于这个缓存,如果不覆写sizeOf()方法,这个方法返回的是条目的在缓存中的数量。但是对于其他缓存,返回的是 351 | * 条目在缓存中大小的总和。 352 | */ 353 | public synchronized final int size() { 354 | return size; 355 | } 356 | 357 | 358 | /** 359 | * 对于这个缓存,如果不覆写sizeOf()方法,这个方法返回的是条目的在缓存中的最大数量。但是对于其他缓存,返回的是 360 | * 条目在缓存中最大大小的总和。 361 | */ 362 | public synchronized final int maxSize() { 363 | return maxSize; 364 | } 365 | 366 | 367 | /** 368 | * Returns the number of times {@link #get} returned a value that was 369 | * already present in the cache. 370 | * 返回的次数{@link #get}这是返回一个值在缓存中已经存在。 371 | */ 372 | public synchronized final int hitCount() { 373 | return hitCount; 374 | } 375 | 376 | 377 | /** 378 | * 返回的次数{@link #get}返回null或需要一个新的要创建价值。 379 | */ 380 | public synchronized final int missCount() { 381 | return missCount; 382 | } 383 | 384 | 385 | /** 386 | * 返回的次数{@link #create(Object)}返回一个值。 387 | */ 388 | public synchronized final int createCount() { 389 | return createCount; 390 | } 391 | 392 | 393 | /** 394 | * 返回put的次数。 395 | */ 396 | public synchronized final int putCount() { 397 | return putCount; 398 | } 399 | 400 | 401 | /** 402 | * 返回被收回的value数量。 403 | */ 404 | public synchronized final int evictionCount() { 405 | return evictionCount; 406 | } 407 | 408 | 409 | /** 410 | * 返回当前缓存内容的一个副本,从最近很少访问到最最近经常访问。 411 | */ 412 | public synchronized final Map snapshot() { 413 | return new LinkedHashMap(map); 414 | } 415 | 416 | 417 | @Override public synchronized final String toString() { 418 | int accesses = hitCount + missCount; 419 | int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0; 420 | return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]", maxSize, 421 | hitCount, missCount, hitPercent); 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /md/android/LruCache 源码解析.md: -------------------------------------------------------------------------------- 1 | LruCache 源码解析 2 | == 3 | 4 | ## 1. 简介 5 | 6 | > LRU 是 Least Recently Used 最近最少使用算法。 7 | 8 | >曾经,在各大缓存图片的框架没流行的时候。有一种很常用的内存缓存技术:SoftReference 和 WeakReference(软引用和弱引用)。但是走到了 Android 2.3(Level 9)时代,垃圾回收机制更倾向于回收 SoftReference 或 WeakReference 的对象。后来,又来到了 Android3.0,图片缓存在内容中,因为不知道要在是什么时候释放内存,没有策略,没用一种可以预见的场合去将其释放。这就造成了内存溢出。 9 | 10 | 11 | ## 2. 使用方法 12 | 13 | **当成一个 Map 用就可以了,只不过实现了 LRU 缓存策略**。 14 | 15 | 使用的时候记住几点即可: 16 | - **1.(必填)**你需要提供一个缓存容量作为构造参数。 17 | - **2.(必填)** 覆写 `sizeOf` 方法 ,自定义设计一条数据放进来的容量计算,如果不覆写就无法预知数据的容量,不能保证缓存容量限定在最大容量以内。 18 | - **3.(选填)** 覆写 `entryRemoved` 方法 ,你可以知道最少使用的缓存被清除时的数据( evicted, key, oldValue, newVaule )。 19 | - **4.(记住)**LruCache是线程安全的,在内部的 get、put、remove 包括 trimToSize 都是安全的(因为都上锁了)。 20 | - **5.(选填)** 还有就是覆写 `create` 方法。 21 | 22 | 一般做到 **1、2、3、4就足够了,5可以无视** 。 23 | 24 | 25 | 以下是 一个 **LruCache 实现 Bitmap 小缓存的案例**, `entryRemoved` 里的自定义逻辑可以无视,这里是我的展示 demo 里的自定义 `entryRemoved` 逻辑。 26 | ```java 27 | private static final float ONE_MIB = 1024 * 1024; 28 | // 7MB 29 | private static final int CACHE_SIZE = (int) (7 * ONE_MIB); 30 | private LruCache bitmapCache; 31 | this.bitmapCache = new LruCache(CACHE_SIZE) { 32 | protected int sizeOf(String key, Bitmap value) { 33 | return value.getByteCount(); 34 | } 35 | 36 | 37 | /** 38 | * 1.当被回收或者删掉时调用。该方法当value被回收释放存储空间时被remove调用 39 | * 或者替换条目值时put调用,默认实现什么都没做。 40 | * 2.该方法没用同步调用,如果其他线程访问缓存时,该方法也会执行。 41 | * 3.evicted=true:如果该条目被删除空间 (表示 进行了trimToSize or remove) evicted=false:put冲突后 或 get里成功create后 导致 42 | * 4.newValue!=null,那么则被put()或get()调用。 43 | */ 44 | @Override 45 | protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) { 46 | mEntryRemovedInfoText.setText( 47 | String.format(Locale.getDefault(), LRU_CACHE_ENTRY_REMOVED_INFO_FORMAT, 48 | evicted, key, oldValue != null ? oldValue.hashCode() : "null", 49 | newValue != null ? newValue.hashCode() : "null")); 50 | // 见上述 3. 51 | if (evicted) { 52 | // 进行了trimToSize or remove (一般是溢出了 或 key-value被删除了 ) 53 | if (recentList.contains(key)) { 54 | recentList.remove(key); 55 | refreshText(mRecentInfoText, LRU_CACHE_RECENT_FORMAT, recentList); 56 | } 57 | } else { 58 | // put冲突后 或 get里成功create 后 59 | recentList.remove(key); 60 | refreshText(mRecentInfoText, LRU_CACHE_RECENT_FORMAT, recentList); 61 | } 62 | if (cacheList.contains(key)) { 63 | cacheList.remove(key); 64 | refreshText(mCacheDataText, LRU_CACHE_CACHE_DATA_FORMAT, cacheList); 65 | } 66 | } 67 | }; 68 | ``` 69 | 70 | ## 3. 效果展示 71 | 72 | ### 3.1 效果一(验证 LRU,最近没访问的,在溢出时优先被清理) 73 | 74 | 75 | 76 | **前提:** 设置 LruCache 最大容量为 7MB,把图1、2、3放入了,此时占用容量为:1.87+0.38+2.47=4.47MB。 77 | 78 | **执行操作**: 79 | - **1.**然后点 get 图3一共16次(**证明访问次数和 LRU 没关系,只有访问顺序有关系**)。Recent visit显示了图3。 80 | - **2.**先 get 图2,再 get 图1,**制造最近访问顺序为:<1> <2> <3>**。 81 | - **3.** put 图4,预算容量需要4.47+2.47=7.19MB。会溢出。 82 | - **4.**溢出了,删除最近没访问的图3。 83 | - **5.**观察 `entryRemoved` 数据 图三被移除了(对照hashcode)。 84 | 85 | --- 86 | 87 | ### 3.2 效果二(验证 entryRemoved 的 evicted=false,可以验证冲突) 88 | 89 | 90 | **前提:**执行了效果一,put 了图4,删除了最近没访问的图3。 91 | 92 | **执行操作**:再一次 put 图4,发生冲突,拿到 key、冲突 value 以及 put 的 value,这里我放到是同一个 hashcode 的 bitmap,所以 hashcode 一样,但是无关紧要吧。 93 | 94 | 95 | ## 4. 源码分析 96 | 97 | ### 4.1 LruCache 原理概要解析 98 | 99 | LruCache 就是 **利用 LinkedHashMap 的一个特性( accessOrder=true 基于访问顺序 )再加上对 LinkedHashMap 的数据操作上锁实现的缓存策略**。 100 | 101 | **LruCache 的数据缓存是内存中的**。 102 | 103 | - 1.首先设置了内部 `LinkedHashMap` 构造参数 `accessOrder=true`, 实现了数据排序按照访问顺序。 104 | 105 | - 2.然后在每次 `LruCache.get(K key)` 方法里都会调用 `LinkedHashMap.get(Object key)`。 106 | 107 | - 3.如上述设置了 `accessOrder=true` 后,每次 `LinkedHashMap.get(Object key)` 都会进行 `LinkedHashMap.makeTail(LinkedEntry e)`。 108 | 109 | - 4.`LinkedHashMap` 是双向循环链表,然后每次 `LruCache.get` -> `LinkedHashMap.get` 的数据就被放到最末尾了。 110 | 111 | - 5.在 `put` 和 `trimToSize` 的方法执行下,如果发成数据量移除了,会优先移除掉最前面的数据(因为最新访问的数据在尾部)。 112 | 113 | **具体解析在:** *4.2*、*4.3*、*4.4*、*4.5* 。 114 | 115 | 116 | ### 4.2 LruCache 的唯一构造方法 117 | ```java 118 | /** 119 | * LruCache的构造方法:需要传入最大缓存个数 120 | */ 121 | public LruCache(int maxSize) { 122 | // 最大缓存个数小于0,会抛出IllegalArgumentException 123 | if (maxSize <= 0) { 124 | throw new IllegalArgumentException("maxSize <= 0"); 125 | } 126 | this.maxSize = maxSize; 127 | /* 128 | * 初始化LinkedHashMap 129 | * 第一个参数:initialCapacity,初始大小 130 | * 第二个参数:loadFactor,负载因子=0.75f 131 | * 第三个参数:accessOrder=true,基于访问顺序;accessOrder=false,基于插入顺序 132 | */ 133 | this.map = new LinkedHashMap(0, 0.75f, true); 134 | } 135 | ``` 136 | 第一个参数 `initialCapacity` 用于初始化该 LinkedHashMap 的大小。 137 | 138 | 先简单介绍一下 第二个参数 `loadFactor`,这个其实的 HashMap 里的构造参数,涉及到**扩容问题**,比如 HashMap 的最大容量是100,那么这里设置0.75f的话,到75容量的时候就会扩容。 139 | 140 | 主要是第三个参数 `accessOrder=true` ,**这样的话 LinkedHashMap 数据排序就会基于数据的访问顺序,从而实现了 LruCache 核心工作原理**。 141 | 142 | ### 4.3 LruCache.get(K key) 143 | ```java 144 | /** 145 | * 根据 key 查询缓存,如果存在于缓存或者被 create 方法创建了。 146 | * 如果值返回了,那么它将被移动到双向循环链表的的尾部。 147 | * 如果如果没有缓存的值,则返回 null。 148 | */ 149 | public final V get(K key) { 150 | 151 | ... 152 | 153 | V mapValue; 154 | synchronized (this) { 155 | // LinkHashMap 如果设置按照访问顺序的话,这里每次get都会重整数据顺序 156 | mapValue = map.get(key); 157 | // 计算 命中次数 158 | if (mapValue != null) { 159 | hitCount++; 160 | return mapValue; 161 | } 162 | // 计算 丢失次数 163 | missCount++; 164 | } 165 | 166 | /* 167 | * 官方解释: 168 | * 尝试创建一个值,这可能需要很长时间,并且Map可能在create()返回的值时有所不同。如果在create()执行的时 169 | * 候,一个冲突的值被添加到Map,我们在Map中删除这个值,释放被创造的值。 170 | */ 171 | V createdValue = create(key); 172 | if (createdValue == null) { 173 | return null; 174 | } 175 | 176 | /*************************** 177 | * 不覆写create方法走不到下面 * 178 | ***************************/ 179 | 180 | /* 181 | * 正常情况走不到这里 182 | * 走到这里的话 说明 实现了自定义的 create(K key) 逻辑 183 | * 因为默认的 create(K key) 逻辑为null 184 | */ 185 | synchronized (this) { 186 | // 记录 create 的次数 187 | createCount++; 188 | // 将自定义create创建的值,放入LinkedHashMap中,如果key已经存在,会返回 之前相同key 的值 189 | mapValue = map.put(key, createdValue); 190 | 191 | // 如果之前存在相同key的value,即有冲突。 192 | if (mapValue != null) { 193 | /* 194 | * 有冲突 195 | * 所以 撤销 刚才的 操作 196 | * 将 之前相同key 的值 重新放回去 197 | */ 198 | map.put(key, mapValue); 199 | } else { 200 | // 拿到键值对,计算出在容量中的相对长度,然后加上 201 | size += safeSizeOf(key, createdValue); 202 | } 203 | } 204 | 205 | // 如果上面 判断出了 将要放入的值发生冲突 206 | if (mapValue != null) { 207 | /* 208 | * 刚才create的值被删除了,原来的 之前相同key 的值被重新添加回去了 209 | * 告诉 自定义 的 entryRemoved 方法 210 | */ 211 | entryRemoved(false, key, createdValue, mapValue); 212 | return mapValue; 213 | } else { 214 | // 上面 进行了 size += 操作 所以这里要重整长度 215 | trimToSize(maxSize); 216 | return createdValue; 217 | } 218 | } 219 | ``` 220 | 上述的 `get` 方法表面并没有看出哪里有实现了 LRU 的缓存策略。主要在 `mapValue = map.get(key)`;里,**调用了 LinkedHashMap 的 get 方法,再加上 LruCache 构造里默认设置 LinkedHashMap 的 accessOrder=true**。 221 | 222 | 223 | ### 4.4 LinkedHashMap.get(Object key) 224 | ```java 225 | /** 226 | * Returns the value of the mapping with the specified key. 227 | * 228 | * @param key 229 | * the key. 230 | * @return the value of the mapping with the specified key, or {@code null} 231 | * if no mapping for the specified key is found. 232 | */ 233 | @Override public V get(Object key) { 234 | /* 235 | * This method is overridden to eliminate the need for a polymorphic 236 | * invocation in superclass at the expense of code duplication. 237 | */ 238 | if (key == null) { 239 | HashMapEntry e = entryForNullKey; 240 | if (e == null) 241 | return null; 242 | if (accessOrder) 243 | makeTail((LinkedEntry) e); 244 | return e.value; 245 | } 246 | 247 | int hash = Collections.secondaryHash(key); 248 | HashMapEntry[] tab = table; 249 | for (HashMapEntry e = tab[hash & (tab.length - 1)]; 250 | e != null; e = e.next) { 251 | K eKey = e.key; 252 | if (eKey == key || (e.hash == hash && key.equals(eKey))) { 253 | if (accessOrder) 254 | makeTail((LinkedEntry) e); 255 | return e.value; 256 | } 257 | } 258 | return null; 259 | } 260 | ``` 261 | 其实仔细看 `if (accessOrder)` 的逻辑即可,如果 `accessOrder=true` 那么每次 `get` 都会执行 N 次 `makeTail(LinkedEntry e)` 。 262 | 263 | 接下来看看 264 | 265 | ### 4.5 LinkedHashMap.makeTail(LinkedEntry e) 266 | ```java 267 | /** 268 | * Relinks the given entry to the tail of the list. Under access ordering, 269 | * this method is invoked whenever the value of a pre-existing entry is 270 | * read by Map.get or modified by Map.put. 271 | */ 272 | private void makeTail(LinkedEntry e) { 273 | // Unlink e 274 | e.prv.nxt = e.nxt; 275 | e.nxt.prv = e.prv; 276 | 277 | // Relink e as tail 278 | LinkedEntry header = this.header; 279 | LinkedEntry oldTail = header.prv; 280 | e.nxt = header; 281 | e.prv = oldTail; 282 | oldTail.nxt = header.prv = e; 283 | modCount++; 284 | } 285 | ``` 286 | 287 | *// Unlink e* 288 | 289 | 290 | *// Relink e as tail* 291 | 292 | 293 | LinkedHashMap 是双向循环链表,然后此次 **LruCache.get -> LinkedHashMap.get** 的数据就被放到最末尾了。 294 | 295 | **以上就是 LruCache 核心工作原理**。 296 | 297 | --- 298 | 299 | 接下来介绍 **LruCache 的容量溢出策略**。 300 | 301 | 上述展示场景中,7M的容量,我添加三张图后,不会溢出,put<4>后必然会超过7MB。 302 | 303 | ### 4.6 LruCache.put(K key, V value) 304 | ```java 305 | public final V put(K key, V value) { 306 | ... 307 | synchronized (this) { 308 | ... 309 | // 拿到键值对,计算出在容量中的相对长度,然后加上 310 | size += safeSizeOf(key, value); 311 | ... 312 | } 313 | ... 314 | trimToSize(maxSize); 315 | return previous; 316 | } 317 | ``` 318 | 记住几点: 319 | - **1.**put 开始的时候确实是把值放入 LinkedHashMap 了,**不管超不超过你设定的缓存容量**。 320 | - **2.**然后根据 `safeSizeOf` 方法计算 此次添加数据的容量是多少,并且加到 `size` 里 。 321 | - **3.**说到 `safeSizeOf` 就要讲到 `sizeOf(K key, V value)` 会计算出此次添加数据的大小 (像上面的 Demo,我的容量是7MB,我每次添加进来的 Bitmap 要是不覆写 sizeOf 方法的话,会视为该 bitmap 的容量计算为默认的容量计算 return 1。如此一来,这样的话 7MB 的 LruCache 容量可以放7x1024x1024张图片?明显这样的逻辑是不对的!)。 322 | - **4.**直到 put 要结束时,进行了 `trimToSize` 才判断 `size` 是否 大于 `maxSize` 然后进行最近很少访问数据的移除。 323 | 324 | ### 4.7 LruCache.trimToSize(int maxSize) 325 | ```java 326 | public void trimToSize(int maxSize) { 327 | /* 328 | * 这是一个死循环, 329 | * 1.只有 扩容 的情况下能立即跳出 330 | * 2.非扩容的情况下,map的数据会一个一个删除,直到map里没有值了,就会跳出 331 | */ 332 | while (true) { 333 | K key; 334 | V value; 335 | synchronized (this) { 336 | // 在重新调整容量大小前,本身容量就为空的话,会出异常的。 337 | if (size < 0 || (map.isEmpty() && size != 0)) { 338 | throw new IllegalStateException( 339 | getClass().getName() + ".sizeOf() is reporting inconsistent results!"); 340 | } 341 | // 如果是 扩容 或者 map为空了,就会中断,因为扩容不会涉及到丢弃数据的情况 342 | if (size <= maxSize || map.isEmpty()) { 343 | break; 344 | } 345 | 346 | Map.Entry toEvict = map.entrySet().iterator().next(); 347 | key = toEvict.getKey(); 348 | value = toEvict.getValue(); 349 | map.remove(key); 350 | // 拿到键值对,计算出在容量中的相对长度,然后减去。 351 | size -= safeSizeOf(key, value); 352 | // 添加一次收回次数 353 | evictionCount++; 354 | } 355 | /* 356 | * 将最后一次删除的最少访问数据回调出去 357 | */ 358 | entryRemoved(true, key, value, null); 359 | } 360 | } 361 | ``` 362 | 简单描述:会判断之前 `size` 是否大于 `maxSize` 。是的话,直接跳出后什么也不做。不是的话,证明已经溢出容量了。由 `makeTail` 图已知,最近经常访问的数据在最末尾。拿到一个存放 key 的 Set,然后一直一直从头开始删除,删一个判断是否溢出,直到没有溢出。 363 | 364 | --- 365 | 366 | 最后看看: 367 | 368 | ### 4.8 覆写 entryRemoved 的作用 369 | 370 | entryRemoved被LruCache调用的场景: 371 | - **1.(put)** put 发生 key 冲突时被调用,**evicted=false,key=此次 put 的 key,oldValue=被覆盖的冲突 value,newValue=此次 put 的 value**。 372 | - **2.(trimToSize)** trimToSize 的时候,只会被调用一次,就是最后一次被删除的最少访问数据带回来。**evicted=true,key=最后一次被删除的 key,oldValue=最后一次被删除的 value,newValue=null(此次没有冲突,只是 remove)**。 373 | - **3.(remove)** remove的时候,存在对应 key,并且被成功删除后被调用。**evicted=false,key=此次 put的 key,oldValue=此次删除的 value,newValue=null(此次没有冲突,只是 remove)**。 374 | - **4.(get后半段,查询丢失后处理情景,不过建议忽略)** get 的时候,正常的话不实现自定义 `create` 的话,代码上看 get 方法只会走一半,如果你实现了自定义的 `create(K key)` 方法,并且在 你 create 后的值放入 LruCache 中发生 key 冲突时被调用,**evicted=false,key=此次 get 的 key,oldValue=被你自定义 create(key)后的 value,newValue=原本存在 map 里的 key-value**。 375 | 376 | 解释一下第四点吧:**<1>.**第四点是这样的,先 get(key),然后没拿到,丢失。**<2>.**如果你提供了 自定义的 `create(key)` 方法,那么 LruCache 会根据你的逻辑自造一个 value,但是当放入的时候发现冲突了,但是已经放入了。**<3>.**此时,会将那个冲突的值再让回去覆盖,此时调用上述4.的 entryRemoved。 377 | 378 | 因为 HashMap 在数据量大情况下,拿数据可能造成丢失,导致前半段查不到,你自定义的 `create(key)` 放入的时候发现又查到了**(有冲突)**。然后又急忙把原来的值放回去,此时你就白白create一趟,无所作为,还要走一遍entryRemoved。 379 | 380 | 381 | 综上就如同注释写的一样: 382 | ```java 383 | /** 384 | * 1.当被回收或者删掉时调用。该方法当value被回收释放存储空间时被remove调用 385 | * 或者替换条目值时put调用,默认实现什么都没做。 386 | * 2.该方法没用同步调用,如果其他线程访问缓存时,该方法也会执行。 387 | * 3.evicted=true:如果该条目被删除空间 (表示 进行了trimToSize or remove) evicted=false:put冲突后 或 get里成功create后 388 | * 导致 389 | * 4.newValue!=null,那么则被put()或get()调用。 390 | */ 391 | protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) { 392 | } 393 | ``` 394 | 可以参考我的 demo 里的 `entryRemoved` 。 395 | 396 | ### 4.9 LruCache 局部同步锁 397 | 398 | 在 `get`, `put`, `trimToSize`, `remove` 四个方法里的 `entryRemoved` 方法都不在同步块里。因为 `entryRemoved` 回调的参数都属于方法域参数,不会线程不安全。 399 | 400 | > 本地方法栈和程序计数器是线程隔离的数据区。 401 | 402 | 403 | 404 | 405 | ## 5. 开源项目中的使用 406 | 407 | [square/picasso](https://github.com/square/picasso) 408 | 409 | 410 | ## 6. 总结 411 | 412 | LruCache重要的几点: 413 | 414 | - **1.**LruCache 是通过 LinkedHashMap 构造方法的第三个参数的 `accessOrder=true` 实现了 `LinkedHashMap` 的数据排序**基于访问顺序** (最近访问的数据会在链表尾部),在容量溢出的时候,将链表头部的数据移除。从而,实现了 LRU 数据缓存机制。 415 | 416 | - **2.**LruCache 在内部的get、put、remove包括 trimToSize 都是安全的(因为都上锁了)。 417 | 418 | - **3.**LruCache 自身并没有释放内存,将 LinkedHashMap 的数据移除了,如果数据还在别的地方被引用了,还是有泄漏问题,还需要手动释放内存。 419 | 420 | - **4.**覆写 `entryRemoved` 方法能知道 LruCache 数据移除是是否发生了冲突,也可以去手动释放资源。 421 | 422 | - **5.**`maxSize` 和 `sizeOf(K key, V value)` 方法的覆写息息相关,必须相同单位。( 比如 maxSize 是7MB,自定义的 sizeOf 计算每个数据大小的时候必须能算出与MB之间有联系的单位 )。 423 | 424 | 425 | 426 | 427 | ## 7. 资源 428 | 429 | [LruCacheActivity](https://github.com/CaMnter/AndroidLife/blob/master/app/src/main/java/com/camnter/newlife/views/activity/lrucache/LruCacheActivity.java) 430 | 431 | 432 | [LruCache 注释源码](https://github.com/CaMnter/AndroidLife/blob/master/app/src/main/java/com/camnter/newlife/utils/cache/LruCache.java) 433 | 434 | 435 | 436 | 437 | 438 | -------------------------------------------------------------------------------- /md/android/framework/Looper Handler MessageQueue.md: -------------------------------------------------------------------------------- 1 | Looper Handler MessageQueue 2 | == 3 | 4 |
5 | 6 | ## 1. 异步消息处理线程 7 | 8 | 9 | ### 1.1 区别 10 | 11 | **普通线程:** run() 方法内的代码后线程就结束了。 12 | **异步消息处理线程:** 线程会进入一个无限循环体,**循环一次就从内部的消息队列中取出一个消息,并回调该消息对应的处理方法**。然后继续循环下去,如果消息队列没有消息了,线程会停止;否则只有有新的消息,就继续循环。 13 | 14 | 15 | ### 1.2 需求场景 16 | 17 | > 异步消息处理线程其本质上也是一个线程,只不过是被设计成了在一个无限循环体下,不断读取消息的场景。并且有消息则循环,无消息暂停的模式。 18 | 19 | **需求场景:** 20 | - **1.** 任务需要常驻。比如说用于处理用户交互的任务。 21 | - **2.** 任务需要根据外部传递的消息而执行不同的操作。 22 | 23 | 24 | ### 1.3 简单实现 25 | 26 | 27 | 28 | - **1.** 每个异步线程内部包含一个消息队列(MessageQueue),队列中的消息一般采用排队机制 29 | 即先到达的消息会先得到处理。 30 | 31 | - **2.** 线程的执行体中使用 while ( true ) 进行无限循环,循环体中从消息队列中取出消息,并且根 32 | 据消息的来源,回调其对应的消息处理函数。 33 | 34 | - **3.** 其他外部线程可以向本线程的消息队列中发送消息,消息队列内部的读/写操作必须进行加锁 35 | 即消息队列不能同时进行读/ 写操作。 36 | 37 | 38 |
39 | 40 | ## 2. Android 中的异步消息处理线程 41 | 42 | 43 | 44 | 在线程内部有一个或多个 Handler 对象,外部程序通过该 Handler 对象向线程发送异步消息,消 45 | 息经由 Handler 传递到 MessageQueue 对象中。线程内部只能包含一个 MessageQueue 对象,线 46 | 程主执行函数中从 MessageQueue 中读取消息,并回调 Handler 对象中的回调函数 handleMessage()。 47 | 48 | 49 |
50 | 51 | ## 3. 线程局部存储 52 | 53 | **变量的常见作用域:** 54 | 55 | - **方法内部变量:** 作用域就是该方法内,每次调用该方法时,变量都会重新回到初始值。 56 | 57 | - **类内部的变量:** 其作用域是该类所产生的对象,只要对象没有销毁,则对象内部的变量值 58 | 一直保持。 59 | 60 | - **类内部静态变量:** 其作用域是整个过程,只要在该进程中,则该变量的值就一直保持,无论使用该类构造过多少个对象,该变量只有一个赋值,并一直保持。进程中哪个线程引用该变量,其值总是相同的,因为在编译器内部为静态变量分配了单独的内存空间。 61 | 62 | **线程局部存储:** 63 | > 同一个线程引用变量值相同,不同线程引用则变量值不相同。 64 | 65 | 66 |
67 | 68 | ## 4. Looper 69 | 70 | **Looper 的作用:** 71 | 72 | - **1.** 调用静态方法 `prepare()` 为该线程创建一个 `消息队列` 。 73 | 74 | - **2.** 调用 `loop()`, 线程进入无限循环的异步消息处理流程,从消息队列内读取消息,并处理消息。 75 | 76 | 77 | ### 4.1 prepare() 78 | 79 | ```java 80 | static final ThreadLocal sThreadLocal = new ThreadLocal(); 81 | ... 82 | ... 83 | ... 84 | public static void prepare() { 85 | prepare(true); 86 | } 87 | 88 | private static void prepare(boolean quitAllowed) { 89 | if (sThreadLocal.get() != null) { 90 | throw new RuntimeException("Only one Looper may be created per thread"); 91 | } 92 | sThreadLocal.set(new Looper(quitAllowed)); 93 | } 94 | ``` 95 | 96 | 创建一个 Looper 并将,Looper 存储在 ThreadLocal 内。所以,**相同线程访问的 Looper 是一样的,不同线程访问的 Looper 是不一样的。** 97 | 98 | ### 4.2 Looper 构造方法 99 | 100 | 由上面的 `new Looper(quitAllowed)` 方法。可以看出,调用了 Looper 的构造方法。 101 | 102 | ```java 103 | private Looper(boolean quitAllowed) { 104 | mQueue = new MessageQueue(quitAllowed); 105 | mThread = Thread.currentThread(); 106 | } 107 | ``` 108 | 109 | 创建一个 `MessageQueue` 。 110 | 111 | ### 4.3 loop() 112 | 113 | ```java 114 | public static void loop() { 115 | final Looper me = myLooper(); 116 | if (me == null) { 117 | throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 118 | } 119 | final MessageQueue queue = me.mQueue; 120 | 121 | // Make sure the identity of this thread is that of the local process, 122 | // and keep track of what that identity token actually is. 123 | Binder.clearCallingIdentity(); 124 | final long ident = Binder.clearCallingIdentity(); 125 | 126 | for (;;) { 127 | Message msg = queue.next(); // might block 128 | if (msg == null) { 129 | // No message indicates that the message queue is quitting. 130 | return; 131 | } 132 | 133 | // This must be in a local variable, in case a UI event sets the logger 134 | Printer logging = me.mLogging; 135 | if (logging != null) { 136 | logging.println(">>>>> Dispatching to " + msg.target + " " + 137 | msg.callback + ": " + msg.what); 138 | } 139 | 140 | msg.target.dispatchMessage(msg); 141 | 142 | if (logging != null) { 143 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 144 | } 145 | 146 | // Make sure that during the course of dispatching the 147 | // identity of the thread wasn't corrupted. 148 | final long newIdent = Binder.clearCallingIdentity(); 149 | if (ident != newIdent) { 150 | Log.wtf(TAG, "Thread identity changed from 0x" 151 | + Long.toHexString(ident) + " to 0x" 152 | + Long.toHexString(newIdent) + " while dispatching to " 153 | + msg.target.getClass().getName() + " " 154 | + msg.callback + " what=" + msg.what); 155 | } 156 | 157 | msg.recycleUnchecked(); 158 | } 159 | } 160 | ... 161 | ... 162 | ... 163 | public static @Nullable Looper myLooper() { 164 | return sThreadLocal.get(); 165 | } 166 | ``` 167 | 168 | **主要操作:** 169 | 170 | - **1.** `myLooper()` 拿到当前线程存储的 `Looper` 对象。 171 | 172 | - **2.** 拿到 `MessageQueue` 后,进入到 `for (;;)` 无限循环内。 173 | 174 | - **3.** 再逐个通过 `queue.next()` 取出 `Message`。 175 | 176 | - **4.** 通过 `msg.target.dispatchMessage(msg)` 不难发现 `msg.target` 是一个Handler,执行了该 `msg` 的处理。 177 | 178 | - **5.** 最后,`msg.recycleUnchecked();` 对消息的资源进行回收。对象占用的系统资源。因为 `Message` 类内部使用了一个数据池去保存 `Message` 对象,从而避免不停地创建和删除 `Message` 类对象。因此,每次处理完该消息后,需要将该 `Message` 对象表明为空闲,以便 `Message` 对象可以被重用。 179 | 180 | 181 |
182 | 183 | ## 5. MessageQueue 184 | 185 | > 消息队列采用排队方式对消息进行处理,就是先到的消息会先得到处理,但是如果消息本身指定 186 | 了被处理的时刻,则必须等到该时刻才能处理消息。消息在 MessageQueue 中使用 Message 类表 187 | 示,队列中的消息以链表的结构进行存储,Message 对象内部包含一个 next 变量,该变量指向下一 188 | 个消息。 189 | 190 | **MessageQueue 的主要方法:** 191 | 192 | - **取出消息:** `next()` 。 193 | 194 | - **添加消息:** `enqueueMessage(Message msg, long when)` 。 195 | 196 | ### 5.1 next() 197 | ```java 198 | Message next() { 199 | // Return here if the message loop has already quit and been disposed. 200 | // This can happen if the application tries to restart a looper after quit 201 | // which is not supported. 202 | final long ptr = mPtr; 203 | if (ptr == 0) { 204 | return null; 205 | } 206 | 207 | int pendingIdleHandlerCount = -1; // -1 only during first iteration 208 | int nextPollTimeoutMillis = 0; 209 | for (;;) { 210 | if (nextPollTimeoutMillis != 0) { 211 | Binder.flushPendingCommands(); 212 | } 213 | 214 | nativePollOnce(ptr, nextPollTimeoutMillis); 215 | 216 | synchronized (this) { 217 | // Try to retrieve the next message. Return if found. 218 | final long now = SystemClock.uptimeMillis(); 219 | Message prevMsg = null; 220 | Message msg = mMessages; 221 | if (msg != null && msg.target == null) { 222 | // Stalled by a barrier. Find the next asynchronous message in the queue. 223 | do { 224 | prevMsg = msg; 225 | msg = msg.next; 226 | } while (msg != null && !msg.isAsynchronous()); 227 | } 228 | if (msg != null) { 229 | if (now < msg.when) { 230 | // Next message is not ready. Set a timeout to wake up when it is ready. 231 | nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); 232 | } else { 233 | // Got a message. 234 | mBlocked = false; 235 | if (prevMsg != null) { 236 | prevMsg.next = msg.next; 237 | } else { 238 | mMessages = msg.next; 239 | } 240 | msg.next = null; 241 | if (DEBUG) Log.v(TAG, "Returning message: " + msg); 242 | msg.markInUse(); 243 | return msg; 244 | } 245 | } else { 246 | // No more messages. 247 | nextPollTimeoutMillis = -1; 248 | } 249 | 250 | // Process the quit message now that all pending messages have been handled. 251 | if (mQuitting) { 252 | dispose(); 253 | return null; 254 | } 255 | 256 | // If first time idle, then get the number of idlers to run. 257 | // Idle handles only run if the queue is empty or if the first message 258 | // in the queue (possibly a barrier) is due to be handled in the future. 259 | if (pendingIdleHandlerCount < 0 260 | && (mMessages == null || now < mMessages.when)) { 261 | pendingIdleHandlerCount = mIdleHandlers.size(); 262 | } 263 | if (pendingIdleHandlerCount <= 0) { 264 | // No idle handlers to run. Loop and wait some more. 265 | mBlocked = true; 266 | continue; 267 | } 268 | 269 | if (mPendingIdleHandlers == null) { 270 | mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 271 | } 272 | mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 273 | } 274 | 275 | // Run the idle handlers. 276 | // We only ever reach this code block during the first iteration. 277 | for (int i = 0; i < pendingIdleHandlerCount; i++) { 278 | final IdleHandler idler = mPendingIdleHandlers[i]; 279 | mPendingIdleHandlers[i] = null; // release the reference to the handler 280 | 281 | boolean keep = false; 282 | try { 283 | keep = idler.queueIdle(); 284 | } catch (Throwable t) { 285 | Log.wtf(TAG, "IdleHandler threw exception", t); 286 | } 287 | 288 | if (!keep) { 289 | synchronized (this) { 290 | mIdleHandlers.remove(idler); 291 | } 292 | } 293 | } 294 | 295 | // Reset the idle handler count to 0 so we do not run them again. 296 | pendingIdleHandlerCount = 0; 297 | 298 | // While calling an idle handler, a new message could have been delivered 299 | // so go back and look again for a pending message without waiting. 300 | nextPollTimeoutMillis = 0; 301 | } 302 | } 303 | ``` 304 | 305 | - **1.** `nativePollOnce(ptr, nextPollTimeoutMillis)`, 这是一个 **JNI** 函数,其作用是从消息队列中取出一个消息。`MessageQueue` 类内部**本身并没有保存消息队列,真正的消息队列数据保存在 JNI 中的 C 代码中**,**在 C 环境中创建了一个 NativeMessageQueue 数据对象**,这就是 `nativePollOnce()` 第一个参数的意义。它是一个 `int` 型变量,在C 环境中,该变量将被强制转换为一个 `NativeMessageQueue` 对象。在 C 环境中,如果消息队列中没有消息,将导致当前线程被挂起( wait ) ;如果消息队列中有消息,则 C 代码中将把该消息赋值给 Java 环境中的 mMessages 变量。 306 | 307 | - **2.** 在 `synchronized(this)` 块中,判断消息所指定的执行时间是否到了。如果到了,就返回该消息,并将 `mMessages` 变量置空;如果时间还没有到,则继续等待时间到了。 308 | 309 | - **3.** 如果 `mMessages` 为空,则说明 C 环境中的消息队列没有可执行的消息了, 因此,执行 `mPendingldleHandlers` 列表中的 **"空闲回调函数"**。可以向 `MessageQueue` 中注册一些 **"空闲回调函数"**,从而当线程中没有消息可处理时去执行这些 **"空闲代码"**。 310 | 311 | 312 | ### 5.1 enqueueMessage(Message msg, long when) 313 | 314 | ```java 315 | boolean enqueueMessage(Message msg, long when) { 316 | if (msg.target == null) { 317 | throw new IllegalArgumentException("Message must have a target."); 318 | } 319 | if (msg.isInUse()) { 320 | throw new IllegalStateException(msg + " This message is already in use."); 321 | } 322 | 323 | synchronized (this) { 324 | if (mQuitting) { 325 | IllegalStateException e = new IllegalStateException( 326 | msg.target + " sending message to a Handler on a dead thread"); 327 | Log.w(TAG, e.getMessage(), e); 328 | msg.recycle(); 329 | return false; 330 | } 331 | 332 | msg.markInUse(); 333 | msg.when = when; 334 | Message p = mMessages; 335 | boolean needWake; 336 | if (p == null || when == 0 || when < p.when) { 337 | // New head, wake up the event queue if blocked. 338 | msg.next = p; 339 | mMessages = msg; 340 | needWake = mBlocked; 341 | } else { 342 | // Inserted within the middle of the queue. Usually we don't have to wake 343 | // up the event queue unless there is a barrier at the head of the queue 344 | // and the message is the earliest asynchronous message in the queue. 345 | needWake = mBlocked && p.target == null && msg.isAsynchronous(); 346 | Message prev; 347 | for (;;) { 348 | prev = p; 349 | p = p.next; 350 | if (p == null || when < p.when) { 351 | break; 352 | } 353 | if (needWake && p.isAsynchronous()) { 354 | needWake = false; 355 | } 356 | } 357 | msg.next = p; // invariant: p == prev.next 358 | prev.next = msg; 359 | } 360 | 361 | // We can assume mPtr != 0 because mQuitting is false. 362 | if (needWake) { 363 | nativeWake(mPtr); 364 | } 365 | } 366 | return true; 367 | } 368 | ``` 369 | 370 | - **1.** 通过 `mMessages = msg` 将参数 `msg` 赋值给 `mMessages`。 371 | 372 | - **2.** 调用 `nativeWake(mPtr)`。JNI 函数,其内部会将 `mMessages` 消息添加到 C 环境中 373 | 的消息队列中,并且如果消息线程正处于挂起(wait)状态,则唤醒该线程。 374 | 375 | 376 |
377 | 378 | ## 6. Handler 379 | 380 | 在 `Looper` 的 `loop()` 的无限循环读取消息过程中,MessageQueue 虽然提供了读写消息的方法,但是却没有直接去调用,而是通过 msg.target(Hadnler)去处理消息。 381 | 382 | > 一般使用 Handler 类 MessageQueue 中发送消息,并重载 Handler 类的 handleMessage() 方法添加消息处理代码。 383 | 384 | 385 | ### 6.1 Handler 构造方法 386 | 387 | **Handler 对象只能添加到有 MessageQueue 的线程中,否则会发生异常。** 388 | 389 | **虽然 Handler 的构造方法很多,但是都是基于这两个构造方法去实现的。** 390 | ```java 391 | 392 | ... 393 | 394 | public Handler(Callback callback, boolean async) { 395 | if (FIND_POTENTIAL_LEAKS) { 396 | final Class klass = getClass(); 397 | if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 398 | (klass.getModifiers() & Modifier.STATIC) == 0) { 399 | Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 400 | klass.getCanonicalName()); 401 | } 402 | } 403 | 404 | mLooper = Looper.myLooper(); 405 | if (mLooper == null) { 406 | throw new RuntimeException( 407 | "Can't create handler inside thread that has not called Looper.prepare()"); 408 | } 409 | mQueue = mLooper.mQueue; 410 | mCallback = callback; 411 | mAsynchronous = async; 412 | } 413 | 414 | ... 415 | 416 | public Handler(Looper looper, Callback callback, boolean async) { 417 | mLooper = looper; 418 | mQueue = looper.mQueue; 419 | mCallback = callback; 420 | mAsynchronous = async; 421 | } 422 | ``` 423 | 424 | - **1.** 第一个构造方法,没有在构造参数里指定 `Looper`。所以,通过 `Looper.myLooper()` 去拿**当前线程里存储在 ThreadLocal 内的 Looper**。( Ps:我们知道主线程创建时会默认初始化好一个 `Looper`,并且调用其 `prepare()` `loop()`。这样,参照上面讲的 `Looper` 源码,已经初始化好一个 Looper 存放在主线程的静态 ThreadLocal 内以及一个 `MessageQueue`。)这里如果是子线程,没有手动 `Looper.prepare()` 和 `Looper.loop()` 会抛出异常,因为这里肯定拿不到 `MessageQueue` (一个 Looper 对应一个 MessageQueue) 。 425 | 426 | - **2.** 构造参数指定了 Looper,就必然有 `MessageQueue`。 427 | 428 | 总结这里:**其实 Handler 强制性要拿一个 Looper,也只是为了一个 MessageQueue**。一个 Looper 的初始化,伴随着一个 MessageQueue 的诞生 ( Ps:见上述 Looper 源码分析 )。因为,`Handler` 的主要工作,还是操作 `MessageQueue` 。 429 | 430 | ### 6.3 Handler 发消息 431 | 432 | **虽然 Handler 的 发消息的方法很多,但是都是基于这两个 核心发消息方法 去实现的。** 433 | ```java 434 | 435 | ... 436 | 437 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 438 | MessageQueue queue = mQueue; 439 | if (queue == null) { 440 | RuntimeException e = new RuntimeException( 441 | this + " sendMessageAtTime() called with no mQueue"); 442 | Log.w("Looper", e.getMessage(), e); 443 | return false; 444 | } 445 | return enqueueMessage(queue, msg, uptimeMillis); 446 | } 447 | 448 | ... 449 | 450 | public final boolean sendMessageAtFrontOfQueue(Message msg) { 451 | MessageQueue queue = mQueue; 452 | if (queue == null) { 453 | RuntimeException e = new RuntimeException( 454 | this + " sendMessageAtTime() called with no mQueue"); 455 | Log.w("Looper", e.getMessage(), e); 456 | return false; 457 | } 458 | return enqueueMessage(queue, msg, 0); 459 | } 460 | ``` 461 | 462 | 可以看源码得知,所有的发消息方法都最终会调用这两个方法之一,这两个方法都会调用 `MessageQueue` 的方法去写一个消息进入消息队列内,最终的消息还是被添加到 C 环境中去。 463 | 464 | 465 | ### 6.4 Handler 处理消息 466 | 467 | ```java 468 | 469 | ... 470 | 471 | public void dispatchMessage(Message msg) { 472 | // 1. 让 Handler.post(Runnable r) 处理消息。 473 | if (msg.callback != null) { 474 | handleCallback(msg); 475 | } else { 476 | if (mCallback != null) { 477 | // 2. 初始化 Handler 的时候,指定的 Callback 处理消息。 478 | if (mCallback.handleMessage(msg)) { 479 | return; 480 | } 481 | } 482 | // 3. 让重写的 handleMessage(Message msg) 去处理消息。 483 | handleMessage(msg); 484 | } 485 | } 486 | 487 | ... 488 | 489 | private static void handleCallback(Message message) { 490 | message.callback.run(); 491 | } 492 | 493 | ... 494 | 495 | public final boolean post(Runnable r) { 496 | return sendMessageDelayed(getPostMessage(r), 0); 497 | } 498 | 499 | ... 500 | 501 | private static Message getPostMessage(Runnable r) { 502 | Message m = Message.obtain(); 503 | m.callback = r; 504 | return m; 505 | } 506 | ``` 507 | 508 | 其实在 `Looper.loop()` 源码里,已经看到过 `msg.target.dispatchMessage(msg)`,拿到 `Hander` ( `msg.target` )去处理了消息。 509 | 510 | 511 | **dispatchMessage 内的三种处理方式:** 512 | 513 | - **1.** `handleCallback(msg)` :是在 `post(Runnable r)` 和 `getPostMessage(Runnable r)` 之后,`getPostMessage(Runnable r)` 方法会 `m.callback = r` 将 `Runnable` 赋值给 `Message` 的 callback 属性。 再结合 `handleCallback(msg)` 的源码,会直接调用`Message` 的 callback 属性 ( Runnable ) 。 514 | 515 | - **2.** `mCallback.handleMessage(msg)` :这里的 `mCallback` ,就是 实例化 Handler 的时候需要传入的 `callback`。这也是 Handler 的一种使用方法之一 ( Ps: 在实例化的时候,传入构造参数 Handler.Callback 去处理消息。)。 516 | 517 | - **3.** `handleMessage(msg)` :让重写的 handleMessage(Message msg) 去处理消息。 518 | -------------------------------------------------------------------------------- /code/android/framework/otto/Bus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Square, Inc. 3 | * Copyright (C) 2007 The Guava Authors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.camnter.otto; 19 | 20 | import java.lang.reflect.InvocationTargetException; 21 | import java.util.Collection; 22 | import java.util.HashSet; 23 | import java.util.LinkedList; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.Set; 27 | import java.util.concurrent.ConcurrentHashMap; 28 | import java.util.concurrent.ConcurrentLinkedQueue; 29 | import java.util.concurrent.ConcurrentMap; 30 | import java.util.concurrent.CopyOnWriteArraySet; 31 | 32 | /** 33 | * Dispatches events to listeners, and provides ways for listeners to register themselves.

34 | *

The Bus allows publish-subscribe-style communication between components without requiring the 35 | * components to explicitly register with one another (and thus be aware of each other). It is 36 | * designed exclusively to replace traditional Android in-process event distribution using explicit 37 | * registration or listeners. It is not a general-purpose publish-subscribe system, nor is 38 | * it intended for interprocess communication.

Receiving Events

To receive events, an 39 | * object should:
  1. Expose a public method, known as the event handler, which accepts 40 | * a single argument of the type of event desired;
  2. Mark it with a {@link 41 | * com.squareup.otto.Subscribe} annotation;
  3. Pass itself to an Bus instance's {@link 42 | * #register(Object)} method.

Posting Events

To post an event, simply 43 | * provide the event object to the {@link #post(Object)} method. The Bus instance will determine 44 | * the type of event and route it to all registered listeners.

Events are routed based on 45 | * their type — an event will be delivered to any handler for any type to which the event is 46 | * assignable. This includes implemented interfaces, all superclasses, and all interfaces 47 | * implemented by superclasses.

When {@code post} is called, all registered handlers for an 48 | * event are run in sequence, so handlers should be reasonably quick. If an event may trigger an 49 | * extended process (such as a database load), spawn a thread or queue it for later.

50 | *

Handler Methods

Event handler methods must accept only one argument: the event.

51 | *

Handlers should not, in general, throw. If they do, the Bus will wrap the exception and 52 | * re-throw it.

The Bus by default enforces that all interactions occur on the main thread. 53 | * You can provide an alternate enforcement by passing a {@link ThreadEnforcer} to the constructor. 54 | *

Producer Methods

Producer methods should accept no arguments and return their event 55 | * type. When a subscriber is registered for a type that a producer is also already registered for, 56 | * the subscriber will be called with the return value from the producer.

Dead Events

57 | * If an event is posted, but no registered handlers can accept it, it is considered "dead." To 58 | * give the system a second chance to handle dead events, they are wrapped in an instance of {@link 59 | * com.squareup.otto.DeadEvent} and reposted.

This class is safe for concurrent use. 60 | * 61 | * @author Cliff Biffle 62 | * @author Jake Wharton 63 | */ 64 | public class Bus { 65 | public static final String DEFAULT_IDENTIFIER = "default"; 66 | 67 | /** 68 | * All registered event handlers, indexed by event type. 69 | * 缓存 listener 及其对应的 EventHandler 70 | * 可以看出一个 listener 可以对应多个 EventHandler 71 | */ 72 | private final ConcurrentMap, Set> handlersByType = 73 | new ConcurrentHashMap, Set>(); 74 | 75 | /** 76 | * All registered event producers, index by event type. 77 | * 缓存 listener 及其对应的 EventProducer 78 | * 可以看出一个 listener 对应一个 EventProducer 79 | */ 80 | private final ConcurrentMap, EventProducer> producersByType = 81 | new ConcurrentHashMap, EventProducer>(); 82 | 83 | /** 84 | * Identifier used to differentiate the event bus instance. 85 | * 可是实现多个Bus,就相当于多个事件总线,实例化的时候可以提供一个String类型的id 86 | * 作为每一个Bus的唯一标识。 87 | */ 88 | private final String identifier; 89 | 90 | /** 91 | * Thread enforcer for register, unregister, and posting events. 92 | * ThreadEnforcer去执行 注册、注销和发送事件。 93 | */ 94 | private final ThreadEnforcer enforcer; 95 | 96 | /** 97 | * Used to find handler methods in register and unregister. 98 | * 在注销和注册的时候,HandlerFinder去找一遍全部的EventHandler( @Subscribe ) 99 | */ 100 | private final HandlerFinder handlerFinder; 101 | 102 | /** 103 | * Queues of events for the current thread to dispatch. 104 | * ThreadLocal存放一个ConcurrentLinkedQueue队列 105 | */ 106 | private final ThreadLocal> eventsToDispatch = 107 | new ThreadLocal>() { 108 | @Override 109 | protected ConcurrentLinkedQueue initialValue() { 110 | return new ConcurrentLinkedQueue(); 111 | } 112 | }; 113 | 114 | /** 115 | * True if the current thread is currently dispatching an event. 116 | * ThreadLocal存放isDispatching标识 117 | */ 118 | private final ThreadLocal isDispatching = new ThreadLocal() { 119 | @Override 120 | protected Boolean initialValue() { 121 | return false; 122 | } 123 | }; 124 | 125 | /** 126 | * Creates a new Bus named "default" that enforces actions on the main thread. 127 | * 默认构造Bus 128 | * 唯一标识为 DEFAULT_IDENTIFIER 129 | */ 130 | public Bus() { 131 | this(DEFAULT_IDENTIFIER); 132 | } 133 | 134 | /** 135 | * Creates a new Bus with the given {@code identifier} that enforces actions on the main thread. 136 | * 默认构造Bus 137 | * 唯一标识为 自定义 138 | * ThreadEnforcer为 主线程 139 | * 140 | * @param identifier a brief name for this bus, for debugging purposes. Should be a valid Java 141 | * identifier. 142 | */ 143 | public Bus(String identifier) { 144 | this(ThreadEnforcer.MAIN, identifier); 145 | } 146 | 147 | /** 148 | * Creates a new Bus named "default" with the given {@code enforcer} for actions. 149 | * 默认构造Bus 150 | * 唯一标识为 DEFAULT_IDENTIFIER 151 | * ThreadEnforcer为 自定义 152 | * 153 | * @param enforcer Thread enforcer for register, unregister, and post actions. 154 | */ 155 | public Bus(ThreadEnforcer enforcer) { 156 | this(enforcer, DEFAULT_IDENTIFIER); 157 | } 158 | 159 | /** 160 | * Creates a new Bus with the given {@code enforcer} for actions and the given {@code 161 | * identifier}. 162 | * 默认构造Bus 163 | * 唯一标识为 自定义 164 | * ThreadEnforcer为 自定义 165 | * HandlerFinder为 HandlerFinder.ANNOTATED 166 | * 167 | * @param enforcer Thread enforcer for register, unregister, and post actions. 168 | * @param identifier A brief name for this bus, for debugging purposes. Should be a valid Java 169 | * identifier. 170 | */ 171 | public Bus(ThreadEnforcer enforcer, String identifier) { 172 | this(enforcer, identifier, HandlerFinder.ANNOTATED); 173 | } 174 | 175 | /** 176 | * Test constructor which allows replacing the default {@code HandlerFinder}. 177 | * 默认构造Bus 178 | * 唯一标识为 自定义 179 | * ThreadEnforcer为 自定义 180 | * HandlerFinder为 自定义 181 | * 182 | * @param enforcer Thread enforcer for register, unregister, and post actions. 183 | * @param identifier A brief name for this bus, for debugging purposes. Should be a valid Java 184 | * identifier. 185 | * @param handlerFinder Used to discover event handlers and producers when 186 | * registering/unregistering an object. 187 | */ 188 | Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) { 189 | this.enforcer = enforcer; 190 | this.identifier = identifier; 191 | this.handlerFinder = handlerFinder; 192 | } 193 | 194 | @Override 195 | public String toString() { 196 | return "[Bus \"" + identifier + "\"]"; 197 | } 198 | 199 | /** 200 | * Registers all handler methods on {@code object} to receive events and producer methods to 201 | * provide events.

If any subscribers are registering for types which already have a producer 202 | * they will be called immediately with the result of calling that producer.

If any producers 203 | * are registering for types which already have subscribers, each subscriber will be called with 204 | * the value from the result of calling the producer. 205 | * 206 | * @param object object whose handler methods should be registered. 207 | * @throws NullPointerException if the object is null. 208 | */ 209 | public void register(Object object) { 210 | if (object == null) { 211 | throw new NullPointerException("Object to register must not be null."); 212 | } 213 | 214 | /********************* 215 | * 处理 @Produce 逻辑 * 216 | *********************/ 217 | 218 | /** 219 | * 一般用户不设置的话 220 | * 这里的ThreadEnforcer为ThreadEnforcer.MAIN 221 | */ 222 | enforcer.enforce(this); 223 | 224 | /** 225 | * 通过HandlerFinder找到所有 226 | * object里所有 @Produce 方法 227 | * 找回来的封装成EventProducer 228 | * 并且最后返回Map, EventProducer> 229 | */ 230 | Map, EventProducer> foundProducers = handlerFinder.findAllProducers(object); 231 | for (Class type : foundProducers.keySet()) { 232 | 233 | /** 234 | * 逐个拿到EventProducer 235 | */ 236 | final EventProducer producer = foundProducers.get(type); 237 | /** 238 | * ConcurrentMap putIfAbsent 安全的put 239 | * 防止并发 240 | * 查看缓存 ConcurrentMap, EventProducer> 有木有 241 | */ 242 | EventProducer previousProducer = producersByType.putIfAbsent(type, producer); 243 | //checking if the previous producer existed 244 | 245 | /** 246 | * 有缓存 先 “炸” 一下 247 | */ 248 | if (previousProducer != null) { 249 | throw new IllegalArgumentException("Producer method for type " + type 250 | + " found on type " + producer.target.getClass() 251 | + ", but already registered by type " + previousProducer.target.getClass() + "."); 252 | } 253 | 254 | /** 255 | * 逐个拿到全部缓存EventHandler ( @Subscribe方法封装类 ) 256 | */ 257 | Set handlers = handlersByType.get(type); 258 | if (handlers != null && !handlers.isEmpty()) { 259 | for (EventHandler handler : handlers) { 260 | /** 261 | * 逐个去调用@Subscribe 和 @Produce 方法 262 | */ 263 | dispatchProducerResultToHandler(handler, producer); 264 | } 265 | } 266 | } 267 | 268 | /********************* 269 | * 处理 @Produce 逻辑 * 270 | *********************/ 271 | 272 | 273 | /*********************** 274 | * 处理 @SubScribe 逻辑 * 275 | ***********************/ 276 | 277 | /** 278 | * 通过HandlerFinder找到所有 279 | * object里所有 @SubScribe 方法 280 | * 找回来的封装成EventHandler 281 | * Map, Set> 282 | */ 283 | Map, Set> foundHandlersMap = handlerFinder.findAllSubscribers(object); 284 | 285 | /** 286 | * 逐个拿到EventHandler 287 | */ 288 | for (Class type : foundHandlersMap.keySet()) { 289 | 290 | /** 291 | * 拿到缓存 EventHandler 集合 292 | */ 293 | Set handlers = handlersByType.get(type); 294 | if (handlers == null) { 295 | //concurrent put if absent 296 | Set handlersCreation = new CopyOnWriteArraySet(); 297 | 298 | /** 299 | * 往缓存 EventHandler Map 里放入一份 type 对应的 EventHandle 集合 300 | */ 301 | handlers = handlersByType.putIfAbsent(type, handlersCreation); 302 | if (handlers == null) { 303 | handlers = handlersCreation; 304 | } 305 | } 306 | final Set foundHandlers = foundHandlersMap.get(type); 307 | if (!handlers.addAll(foundHandlers)) { 308 | throw new IllegalArgumentException("Object already registered."); 309 | } 310 | } 311 | 312 | /** 313 | * 遍历所有找到 EventHandler ( @Subscribe方法 ) 314 | */ 315 | for (Map.Entry, Set> entry : foundHandlersMap.entrySet()) { 316 | Class type = entry.getKey(); 317 | /** 318 | * 再拿一次 EventProducer 缓存 319 | * 如果object 里 存在 @Producer 方法 320 | * 才循环 EventHandler 的逻辑里去 调用 321 | * dispatchProducerResultToHandler方法 322 | */ 323 | EventProducer producer = producersByType.get(type); 324 | if (producer != null && producer.isValid()) { 325 | Set foundHandlers = entry.getValue(); 326 | for (EventHandler foundHandler : foundHandlers) { 327 | if (!producer.isValid()) { 328 | break; 329 | } 330 | if (foundHandler.isValid()) { 331 | dispatchProducerResultToHandler(foundHandler, producer); 332 | } 333 | } 334 | } 335 | } 336 | 337 | /*********************** 338 | * 处理 @SubScribe 逻辑 * 339 | ***********************/ 340 | } 341 | 342 | /** 343 | * 完成了所谓的 提供 @Produce 被自身 @Subscribe 消费的流程 344 | * 345 | * @param handler handler 346 | * @param producer producer 347 | */ 348 | private void dispatchProducerResultToHandler(EventHandler handler, EventProducer producer) { 349 | /** 350 | * 在这里进行一些数据的判断 351 | */ 352 | Object event = null; 353 | try { 354 | /** 355 | * 然后调用EventProducer里的 produceEvent 方法 356 | * produceEvent里又反射 357 | * 调用注册object里的 @Produce 方法 358 | */ 359 | event = producer.produceEvent(); 360 | } catch (InvocationTargetException e) { 361 | throwRuntimeException("Producer " + producer + " threw an exception.", e); 362 | } 363 | if (event == null) { 364 | return; 365 | } 366 | /** 367 | * 通过 @Produce 的方法提供的事件不为null 368 | * 如果 event 不为空 369 | * 最后进到dispatch里调用 370 | * EventHandler里的handleEvent方法 371 | * 调用注册object里的 的 @Subscribe 方法 372 | * 完成了所谓的 提供 @Produce 被自身 @Subscribe 消费的流程 373 | */ 374 | dispatch(event, handler); 375 | } 376 | 377 | /** 378 | * Unregisters all producer and handler methods on a registered {@code object}. 379 | * 380 | * @param object object whose producer and handler methods should be unregistered. 381 | * @throws IllegalArgumentException if the object was not previously registered. 382 | * @throws NullPointerException if the object is null. 383 | */ 384 | public void unregister(Object object) { 385 | if (object == null) { 386 | throw new NullPointerException("Object to unregister must not be null."); 387 | } 388 | 389 | /** 390 | * 一般用户不设置的话 391 | * 这里的ThreadEnforcer为ThreadEnforcer.MAIN 392 | */ 393 | enforcer.enforce(this); 394 | 395 | /** 396 | * 通过HandlerFinder找到所有 397 | * object里所有 @Produce 方法 398 | * 找回来的封装成EventProducer 399 | * 并且最后返回Map, EventProducer> 400 | */ 401 | Map, EventProducer> producersInListener = handlerFinder.findAllProducers(object); 402 | for (Map.Entry, EventProducer> entry : producersInListener.entrySet()) { 403 | final Class key = entry.getKey(); 404 | 405 | /** 406 | * 拿到对应的 EventProducer 407 | * 这里只拿一个 又表明了: 408 | * 一个 object 只存在 一个 EventProducer 409 | * producer 表示 已缓存的 该 object 的 所有 EventProducer 410 | * value 表示 通过Finder 找到的 所有 EventHandler 411 | */ 412 | EventProducer producer = getProducerForEventType(key); 413 | EventProducer value = entry.getValue(); 414 | 415 | if (value == null || !value.equals(producer)) { 416 | throw new IllegalArgumentException( 417 | "Missing event producer for an annotated method. Is " + object.getClass() 418 | + " registered?"); 419 | } 420 | /** 421 | * 从 EventProducer 缓存Map里移除 422 | * 并调用 EventProducer.invalidate()方法 423 | * 设置该 EventProducer 不合法 424 | */ 425 | producersByType.remove(key).invalidate(); 426 | } 427 | 428 | Map, Set> handlersInListener = handlerFinder.findAllSubscribers(object); 429 | for (Map.Entry, Set> entry : handlersInListener.entrySet()) { 430 | 431 | /** 432 | * 拿到对应的 Set 433 | * 又表明了: 434 | * 一个 object 只存在 N个 EventHandler 435 | * currentHandlers 表示 已缓存的 该 object 的 所有 EventHandler 436 | * eventMethodsInListener 表示 通过Finder 找到的 所有 EventHandler 437 | */ 438 | Set currentHandlers = getHandlersForEventType(entry.getKey()); 439 | Collection eventMethodsInListener = entry.getValue(); 440 | 441 | if (currentHandlers == null || !currentHandlers.containsAll(eventMethodsInListener)) { 442 | throw new IllegalArgumentException( 443 | "Missing event handler for an annotated method. Is " + object.getClass() 444 | + " registered?"); 445 | } 446 | 447 | /** 448 | * 循环所有 缓存的 EventHandler 449 | * 如果 缓存的 EventHandler 又存在 Finder 先查的 EventHandler 里 450 | * 标记为 不合法 451 | */ 452 | for (EventHandler handler : currentHandlers) { 453 | if (eventMethodsInListener.contains(handler)) { 454 | handler.invalidate(); 455 | } 456 | } 457 | 458 | /** 459 | * 该 object 所有缓存的 EventHandler 集合中 剔除 460 | * Finder现查的所有 EventHandler 461 | */ 462 | currentHandlers.removeAll(eventMethodsInListener); 463 | } 464 | } 465 | 466 | /** 467 | * Posts an event to all registered handlers. This method will return successfully after the 468 | * event has been posted to all handlers, and regardless of any exceptions thrown by handlers. 469 | *

If no handlers have been subscribed for {@code event}'s class, and {@code event} is not 470 | * already a {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted. 471 | * 472 | * @param event event to post. 473 | * @throws NullPointerException if the event is null. 474 | */ 475 | public void post(Object event) { 476 | if (event == null) { 477 | throw new NullPointerException("Event to post must not be null."); 478 | } 479 | 480 | /** 481 | * 一般用户不设置的话 482 | * 这里的ThreadEnforcer为ThreadEnforcer.MAIN 483 | */ 484 | enforcer.enforce(this); 485 | 486 | /** 487 | * 拿到 该事件 + 该事件所有父类 的Set集合 488 | */ 489 | Set> dispatchTypes = flattenHierarchy(event.getClass()); 490 | 491 | boolean dispatched = false; 492 | /** 493 | * 遍历该事件 + 该事件所有父类 的Set集合 494 | */ 495 | for (Class eventType : dispatchTypes) { 496 | 497 | /** 498 | * 拿到 该 object 所有缓存 EventHandler 499 | */ 500 | Set wrappers = getHandlersForEventType(eventType); 501 | 502 | /** 503 | * 开始进入 遍历缓存的 EventHandler 504 | * 并处理事件 505 | * 进入 enqueueEvent 逻辑 506 | */ 507 | if (wrappers != null && !wrappers.isEmpty()) { 508 | dispatched = true; 509 | for (EventHandler wrapper : wrappers) { 510 | /** 511 | * 事件 + EventHandler 包装成 EventWithHandler 512 | * 入队 513 | */ 514 | enqueueEvent(event, wrapper); 515 | } 516 | } 517 | } 518 | 519 | /** 520 | * 根据上面的循环可知道 521 | * 如果 object 存在 一个 EventHandler 522 | * 并且post的 事件不是 DeadEvent 523 | * 就会执行一次 post(new DeadEvent(this, event)) 524 | */ 525 | if (!dispatched && !(event instanceof DeadEvent)) { 526 | post(new DeadEvent(this, event)); 527 | } 528 | 529 | dispatchQueuedEvents(); 530 | } 531 | 532 | /** 533 | * Queue the {@code event} for dispatch during {@link #dispatchQueuedEvents()}. Events are queued 534 | * in-order of occurrence so they can be dispatched in the same order. 535 | * 封装成 EventWithHandler 536 | * 入队 537 | */ 538 | protected void enqueueEvent(Object event, EventHandler handler) { 539 | eventsToDispatch.get().offer(new EventWithHandler(event, handler)); 540 | } 541 | 542 | /** 543 | * Drain the queue of events to be dispatched. As the queue is being drained, new events may be 544 | * posted to the end of the queue. 545 | * 开始处理事件队列 546 | */ 547 | protected void dispatchQueuedEvents() { 548 | // don't dispatch if we're already dispatching, that would allow reentrancy and out-of-order events. Instead, leave 549 | // the events to be dispatched after the in-progress dispatch is complete. 550 | if (isDispatching.get()) { 551 | return; 552 | } 553 | 554 | isDispatching.set(true); 555 | try { 556 | while (true) { 557 | /** 558 | * 拿到队列,取出一个 EventWithHandler 559 | */ 560 | EventWithHandler eventWithHandler = eventsToDispatch.get().poll(); 561 | if (eventWithHandler == null) { 562 | break; 563 | } 564 | 565 | /** 566 | * 处理 @Subscribe 逻辑 567 | */ 568 | if (eventWithHandler.handler.isValid()) { 569 | dispatch(eventWithHandler.event, eventWithHandler.handler); 570 | } 571 | } 572 | } finally { 573 | isDispatching.set(false); 574 | } 575 | } 576 | 577 | /** 578 | * Dispatches {@code event} to the handler in {@code wrapper}. This method is an appropriate 579 | * override point for subclasses that wish to make event delivery asynchronous. 580 | *

581 | * 调用 EventHandler.handleEvent方法 582 | * 将事件传入,进而调用 @Subscribe 方法 583 | * 584 | * @param event event to dispatch. 585 | * @param wrapper wrapper that will call the handler. 586 | */ 587 | protected void dispatch(Object event, EventHandler wrapper) { 588 | /** 589 | * 调用 EventHandler.handleEvent方法 590 | * 将事件传入,进而调用 @Subscribe 方法 591 | */ 592 | try { 593 | wrapper.handleEvent(event); 594 | } catch (InvocationTargetException e) { 595 | throwRuntimeException( 596 | "Could not dispatch event: " + event.getClass() + " to handler " + wrapper, e); 597 | } 598 | } 599 | 600 | /** 601 | * Retrieves the currently registered producer for {@code type}. If no producer is currently 602 | * registered for {@code type}, this method will return {@code null}. 603 | * 根据object的class类型 拿到object缓存的 一个EventProducer 604 | * 605 | * @param type type of producer to retrieve. 606 | * @return currently registered producer, or {@code null}. 607 | */ 608 | EventProducer getProducerForEventType(Class type) { 609 | return producersByType.get(type); 610 | } 611 | 612 | /** 613 | * Retrieves a mutable set of the currently registered handlers for {@code type}. If no handlers 614 | * are currently registered for {@code type}, this method may either return {@code null} or an 615 | * empty set. 616 | * 根据object的class类型 拿到object缓存的 一个 EventHandler 集合 617 | * 618 | * @param type type of handlers to retrieve. 619 | * @return currently registered handlers, or {@code null}. 620 | */ 621 | Set getHandlersForEventType(Class type) { 622 | return handlersByType.get(type); 623 | } 624 | 625 | /** 626 | * Flattens a class's type hierarchy into a set of Class objects. The set will include all 627 | * superclasses (transitively), and all interfaces implemented by these superclasses. 628 | * 629 | * @param concreteClass class whose type hierarchy will be retrieved. 630 | * @return {@code concreteClass}'s complete type hierarchy, flattened and uniqued. 631 | */ 632 | Set> flattenHierarchy(Class concreteClass) { 633 | Set> classes = flattenHierarchyCache.get(concreteClass); 634 | if (classes == null) { 635 | /** 636 | * 寻找改 事件 的所有父类 包括 自己 637 | * 返回一个Set 集合 638 | */ 639 | Set> classesCreation = getClassesFor(concreteClass); 640 | /** 641 | * flattenHierarchyCache 里放入 该事件自身为key 642 | * 上面的Set为 value 643 | */ 644 | classes = flattenHierarchyCache.putIfAbsent(concreteClass, classesCreation); 645 | if (classes == null) { 646 | classes = classesCreation; 647 | } 648 | } 649 | 650 | return classes; 651 | } 652 | 653 | /** 654 | * 寻找一个类所有父类 包括自己 存为一个 Set 集合 655 | * 656 | * @param concreteClass concreteClass 657 | * @return Set> 658 | */ 659 | private Set> getClassesFor(Class concreteClass) { 660 | List> parents = new LinkedList>(); 661 | Set> classes = new HashSet>(); 662 | 663 | parents.add(concreteClass); 664 | 665 | while (!parents.isEmpty()) { 666 | Class clazz = parents.remove(0); 667 | classes.add(clazz); 668 | 669 | Class parent = clazz.getSuperclass(); 670 | if (parent != null) { 671 | parents.add(parent); 672 | } 673 | } 674 | return classes; 675 | } 676 | 677 | /** 678 | * Throw a {@link RuntimeException} with given message and cause lifted from an {@link 679 | * InvocationTargetException}. If the specified {@link InvocationTargetException} does not have a 680 | * cause, neither will the {@link RuntimeException}. 681 | */ 682 | private static void throwRuntimeException(String msg, InvocationTargetException e) { 683 | Throwable cause = e.getCause(); 684 | if (cause != null) { 685 | throw new RuntimeException(msg + ": " + cause.getMessage(), cause); 686 | } else { 687 | throw new RuntimeException(msg + ": " + e.getMessage(), e); 688 | } 689 | } 690 | 691 | private final ConcurrentMap, Set>> flattenHierarchyCache = 692 | new ConcurrentHashMap, Set>>(); 693 | 694 | /** 695 | * Simple struct representing an event and its handler. 696 | */ 697 | static class EventWithHandler { 698 | final Object event; 699 | final EventHandler handler; 700 | 701 | public EventWithHandler(Object event, EventHandler handler) { 702 | this.event = event; 703 | this.handler = handler; 704 | } 705 | } 706 | } 707 | --------------------------------------------------------------------------------