├── 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 | *
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 | 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 | * 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 | * 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 | * 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 | * This class is thread-safe. Perform multiple cache operations atomically by
30 | * synchronizing on the cache: 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 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. 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. 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 | * 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 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
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
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
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
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
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 |
73 |
74 | ### 2.2 小部件配置信息
75 |
76 | **@xml/appwidget_provider_info.xml**
77 |
78 | ```xml
79 |
80 |
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 |
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 |
73 |
74 | ### 2.2 小部件配置信息
75 |
76 | **@xml/appwidget_provider_info.xml**
77 |
78 | ```xml
79 |
80 |
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 |
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 服务,尤其要注意的是,`
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 {@code
22 | * int cacheSize = 4 * 1024 * 1024; // 4MiB
23 | * LruCache
28 | *
29 | * {@code
31 | * synchronized (cache) {
32 | * if (cache.get(key) == null) {
33 | * cache.put(key, value);
34 | * }
35 | * }}
36 | *
37 | *
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
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
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
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 extends Handler> 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 | * Receiving Events
To receive events, an
39 | * object should:
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. Handler Methods
Event handler methods must accept only one argument: the event.
51 | * 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.