├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── android ├── App 优化.md ├── App 启动过程(含 Activity 启动过程).md ├── App 打包流程.md ├── EditText 搜索优化.md └── RecyclerView 性能优化.md ├── base ├── net │ ├── TCP 与 UDP 的区别.md │ └── 网络模型.md └── os │ ├── 进程和线程的区别.md │ ├── 进程有哪几种状态.md │ ├── 进程调度策略.md │ └── 进程间通信的几种方式.md ├── design-patterns ├── 单例模式.md ├── 模板方法模式.md └── 责任链模式.md ├── hr └── 你的缺点是什么.md ├── java └── 线程池.md └── jvm └── 运行时数据区域.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/donate.png 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * 面试模块: 2 | * 面试题目: 3 | * 作者: 4 | * 参考答案: 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 安卓 offer 收割基 2 | 3 | 为了方便广大 Android 开发者们做面试准备,故建立此库,本着我的一些微不足道的开发经验来整理和收集 Android 题库,内容会逐渐完善,有兴趣的可以一起加入进来,把你认为总结得不错的面试题通过 [提 issue](https://github.com/Blankj/AndroidOfferKiller/issues/new) 来加入此库。 4 | 5 | 6 | ## 摘要 7 | 8 | * [1 计算机基础](#1-计算机基础) 9 | * [1.1 数据结构](#11-数据结构) 10 | * [1.2 操作系统](#12-操作系统) 11 | * [1.3 计算机网络](#13-计算机网络) 12 | * [2 Java](#2-java) 13 | * [3 Java 虚拟机](#3-java-虚拟机) 14 | * [4 Android](#4-android) 15 | * [5 设计模式](#5-设计模式) 16 | * [6 HR 面试](#6-hr-面试) 17 | * [7 简历模板](#7-简历模板) 18 | * [8 面经集合](#8-面经集合) 19 | * [9 内推通道](#9-内推通道) 20 | 21 | 22 | ### 1 计算机基础 23 | 24 | 相信考过计算机研究生的都知道 408,也就是计算机基础,它主要包括「数据结构」、「计算机组成原理」、「操作系统」和「计算机网络」,和 Android 面试有关的主要就是「数据结构」和「计算机网络」,其他两部分不做要求,感兴趣的也可以去了解。 25 | 26 | #### 1.1 数据结构 27 | 28 | | Title | Author | 29 | | :--------------------------------------- | :--------------------------------------- | 30 | | [LeetCode 题解][LeetCode 题解] | [Blankj][Blankj] | 31 | | [fucking-algorithm][fucking-algorithm] | [labuladong][labuladong] | 32 | 33 | 34 | #### 1.2 操作系统 35 | 36 | | Title | Author | 37 | | :--------------------------------------- | :--------------------------------------- | 38 | | [进程和线程的区别][进程和线程的区别] | [Omooo][Omooo] | 39 | | [进程有哪几种状态][进程有哪几种状态] | [Omooo][Omooo] | 40 | | [进程调度策略][进程调度策略] | [Omooo][Omooo] | 41 | | [进程间通信的几种方式][进程间通信的几种方式] | [Omooo][Omooo] | 42 | 43 | 44 | #### 1.3 计算机网络 45 | 46 | | Title | Author | 47 | | :--------------------------------------- | :--------------------------------------- | 48 | | [TCP 与 UDP 的区别][TCP 与 UDP 的区别] | [Blankj][Blankj] | 49 | 50 | 51 | ### 2 Java 52 | 53 | | Title | Author | 54 | | :--------------------------------------- | :--------------------------------------- | 55 | | [线程池][线程池] | [Blankj][Blankj] | 56 | 57 | 58 | ### 3 Java 虚拟机 59 | 60 | | Title | Author | 61 | | :--------------------------------------- | :--------------------------------------- | 62 | | [运行时数据区域][运行时数据区域] | [Blankj][Blankj] | 63 | 64 | 65 | ### 4 Android 66 | 67 | | Title | Author | 68 | | :--------------------------------------- | :--------------------------------------- | 69 | | [App 启动过程(含 Activity 启动过程)][App 启动过程(含 Activity 启动过程)]| [Blankj][Blankj]| 70 | | [RecyclerView 性能优化][RecyclerView 性能优化]| [Blankj][Blankj]| 71 | | [EditText 搜索优化][EditText 搜索优化] | [Blankj][Blankj]| 72 | 73 | 74 | ### 5 设计模式 75 | 76 | | Title | Author | 77 | | :--------------------------------------- | :--------------------------------------- | 78 | | [单例模式][单例模式] | [Blankj][Blankj] | 79 | | [责任链模式][责任链模式] | [AndroidTraveler][AndroidTraveler] | 80 | | [模板方法模式][模板方法模式] | [安卓小煜][安卓小煜] | 81 | 82 | ### 6 HR 面试 83 | 84 | | Title | Author | 85 | | :--------------------------------------- | :--------------------------------------- | 86 | | [你的缺点是什么][你的缺点是什么] | [Blankj][Blankj] | 87 | 88 | 89 | ### 7 简历模板 90 | 91 | | Title | Author | 92 | | :--------------------------------------- | :--------------------------------------- | 93 | | [技术人员的 Markdown 简历][技术人员的 Markdown 简历] | [Blankj][Blankj] | 94 | 95 | 96 | ### 8 面经集合 97 | 98 | | Title | Author | 99 | | :--------------------------------------- | :--------------------------------------- | 100 | | [Facebook 面经记][Facebook 面经记] | [Blankj][Blankj] | 101 | | [阿里巴巴面经记][阿里巴巴面经记] | [Blankj][Blankj] | 102 | 103 | 104 | ### 9 内推通道 105 | 106 | | Company |Position | 107 | | :--------------------------- |:------------------------------ | 108 | | [字节跳动][字节跳动] | [字节跳动靠谱内推][字节跳动靠谱内推]| 109 | 110 | 111 | ## 打个小广告 112 | 113 | 欢迎加入我的小专栏「**[基你太美](https://xiaozhuanlan.com/Blankj)**」一起学习。 114 | 115 | 116 | [Blankj]: https://github.com/Blankj 117 | [AndroidTraveler]: https://github.com/nesger 118 | [安卓小煜]: https://github.com/nesger 119 | [Omooo]: https://github.com/Omooo 120 | [labuladong]: https://github.com/labuladong 121 | 122 | 123 | [LeetCode 题解]: https://github.com/Blankj/awesome-java-leetcode 124 | [fucking-algorithm]: https://github.com/labuladong/fucking-algorithm 125 | 126 | 127 | [进程和线程的区别]: ./base/os/进程和线程的区别.md 128 | [进程有哪几种状态]: ./base/os/进程有哪几种状态.md 129 | [进程调度策略]: ./base/os/进程调度策略.md 130 | [进程间通信的几种方式]: ./base/os/进程间通信的几种方式.md 131 | 132 | 133 | [TCP 与 UDP 的区别]: ./base/net/TCP%20与%20UDP%20的区别.md 134 | 135 | 136 | [线程池]: ./java/线程池.md 137 | 138 | 139 | [运行时数据区域]: ./jvm/运行时数据区域.md 140 | 141 | 142 | [App 启动过程(含 Activity 启动过程)]: ./android/App%20启动过程(含%20Activity%20启动过程).md 143 | [RecyclerView 性能优化]: ./android/RecyclerView%20性能优化.md 144 | [EditText 搜索优化]: ./android/EditText%20搜索优化.md 145 | 146 | 147 | [单例模式]: ./design-patterns/单例模式.md 148 | [责任链模式]: ./design-patterns/责任链模式.md 149 | [模板方法模式]: ./design-patterns/模板方法模式.md 150 | 151 | 152 | [你的缺点是什么]: ./hr/你的缺点是什么.md 153 | 154 | 155 | [技术人员的 Markdown 简历]: https://github.com/Blankj/resume 156 | 157 | 158 | [Facebook 面经记]: https://blankj.com/2017/10/31/facebook-interview 159 | [阿里巴巴面经记]: https://blankj.com/2018/09/26/alibaba-interview 160 | 161 | 162 | [字节跳动]: https://job.bytedance.com 163 | [字节跳动靠谱内推]: https://bytedance.feishu.cn/docs/ 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /android/App 优化.md: -------------------------------------------------------------------------------- 1 | # App 优化 -------------------------------------------------------------------------------- /android/App 启动过程(含 Activity 启动过程).md: -------------------------------------------------------------------------------- 1 | # App 启动过程(含 Activity 启动过程) 2 | 3 | 这道题在曾经面试「菜鸟网络」中遇到过,不过当时只问了「Activity 启动过程」,这里对整个「App 启动过程」进行完整的源码分析,希望可以帮助到大家。 4 | 5 | ## 源码分析 6 | 7 | ### 1. Launcher 捕获点击事件,其过程为 `Launcher#onClick` -> `Launcher#onClickAppShortcut` -> `Launcher#startAppShortcutOrInfoActivity` -> `Launcher#startActivitySafely` -> `Activity#startActivity`,其 Launcher3 相关源码如下所示: 8 | 9 | ```java 10 | // https://github.com/amirzaidi/Launcher3/blob/f7951c32984036eef2f2130f21abded3ddf6160a/src/com/android/launcher3/Launcher.java#L2249 11 | public void onClick(View v) { 12 | ... 13 | Object tag = v.getTag(); 14 | if (tag instanceof ShortcutInfo) { 15 | onClickAppShortcut(v); 16 | } 17 | ... 18 | } 19 | 20 | // https://github.com/amirzaidi/Launcher3/blob/f7951c32984036eef2f2130f21abded3ddf6160a/src/com/android/launcher3/Launcher.java#L2412 21 | protected void onClickAppShortcut(final View v) { 22 | ... 23 | // Start activities 24 | startAppShortcutOrInfoActivity(v); 25 | } 26 | 27 | // https://github.com/amirzaidi/Launcher3/blob/f7951c32984036eef2f2130f21abded3ddf6160a/src/com/android/launcher3/Launcher.java#L2462 28 | private void startAppShortcutOrInfoActivity(View v) { 29 | ItemInfo item = (ItemInfo) v.getTag(); 30 | Intent intent;// 应用程序安装的时候根据 AndroidManifest.xml 由 PackageManagerService 解析并保存的 31 | if (item instanceof PromiseAppInfo) { 32 | PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item; 33 | intent = promiseAppInfo.getMarketIntent(); 34 | } else { 35 | intent = item.getIntent(); 36 | } 37 | ... 38 | boolean success = startActivitySafely(v, intent, item); 39 | ... 40 | } 41 | 42 | // https://github.com/amirzaidi/Launcher3/blob/f7951c32984036eef2f2130f21abded3ddf6160a/src/com/android/launcher3/Launcher.java#L2689 43 | public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { 44 | ... 45 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 46 | ... 47 | startActivity(intent, optsBundle); 48 | ... 49 | } 50 | ``` 51 | 52 | ### 2. 以 API 27 源码为例,说到了 `Acitvity#startActivity`,我们点击源码可以发现调用的是 `Activity#startActivityForResult`,其中调用到了 `Instrumentation#execStartActivity` 这个方法,源码如下所示: 53 | 54 | ``` java 55 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/Activity.java#4800 56 | public void startActivity(Intent intent, @Nullable Bundle options) { 57 | if (options != null) { 58 | startActivityForResult(intent, -1, options); 59 | } else { 60 | // Note we want to go through this call for compatibility with 61 | // applications that may have overridden the method. 62 | startActivityForResult(intent, -1); 63 | } 64 | } 65 | 66 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/Activity.java#4482 67 | public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, 68 | @Nullable Bundle options) { 69 | ... 70 | Instrumentation.ActivityResult ar = 71 | mInstrumentation.execStartActivity( 72 | this, mMainThread.getApplicationThread(), mToken, this, 73 | intent, requestCode, options); 74 | ... 75 | } 76 | ``` 77 | 78 | ### 3. 在 `Instrumentation#execStartActivity` 中我们可以发现它调用了 `ActivityManager#getService()#startActivity`,其 `ActivityManager#getService()` 是采用单例,返回的是实现 `IActivityManager` 类型的 `Binder` 对象,它的具体实现是在 `ActivityManagerService` 中。 79 | 80 | ```java 81 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/Instrumentation.java#1578 82 | public ActivityResult execStartActivity( 83 | Context who, IBinder contextThread, IBinder token, Activity target, 84 | Intent intent, int requestCode, Bundle options) { 85 | ... 86 | try { 87 | ... 88 | int result = ActivityManager.getService() 89 | .startActivity(whoThread, who.getBasePackageName(), intent, 90 | intent.resolveTypeIfNeeded(who.getContentResolver()), 91 | token, target != null ? target.mEmbeddedID : null, 92 | requestCode, 0, null, options); 93 | ... 94 | } catch (RemoteException e) { 95 | ... 96 | } 97 | return null; 98 | } 99 | 100 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityManager.java#4216 101 | public static IActivityManager getService() { 102 | return IActivityManagerSingleton.get(); 103 | } 104 | private static final Singleton IActivityManagerSingleton = 105 | new Singleton() { 106 | @Override 107 | protected IActivityManager create() { 108 | final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); 109 | final IActivityManager am = IActivityManager.Stub.asInterface(b); 110 | return am; 111 | } 112 | }; 113 | ``` 114 | 115 | ### 4. 我们再到 `ActivityManagerService#startActivity` 查看其源码,发现其调用了 `ActivityManagerService#startActivityAsUser`,该方法又调用了 `ActivityStarter#startActivityMayWait`,源码如下所示: 116 | 117 | ```java 118 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java#4516 119 | @Override 120 | public final int startActivity(IApplicationThread caller, String callingPackage, 121 | Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, 122 | int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { 123 | return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, 124 | resultWho, requestCode, startFlags, profilerInfo, bOptions, 125 | UserHandle.getCallingUserId()); 126 | } 127 | 128 | @Override 129 | public final int startActivityAsUser(IApplicationThread caller, String callingPackage, 130 | Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, 131 | int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { 132 | enforceNotIsolatedCaller("startActivity"); 133 | userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), 134 | userId, false, ALLOW_FULL_ONLY, "startActivity", null); 135 | // TODO: Switch to user app stacks here. 136 | return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, 137 | resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, 138 | profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser"); 139 | } 140 | ``` 141 | 142 | ### 5. 我们查找到 `ActivityStarter#startActivityMayWait`,其间调用了 `ActivityStarter#startActivityLocked`,接着是 `ActivityStarter#startActivity`,然后是 `ActivityStarter#startActivityUnchecked`,其调用了 `ActivityStackSupervisor#resumeFocusedStackTopActivityLocked`,源码如下所示: 143 | 144 | ```java 145 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java#673 146 | final int startActivityMayWait(IApplicationThread caller, int callingUid, 147 | String callingPackage, Intent intent, String resolvedType, 148 | IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, 149 | IBinder resultTo, String resultWho, int requestCode, int startFlags, 150 | ProfilerInfo profilerInfo, WaitResult outResult, 151 | Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId, 152 | TaskRecord inTask, String reason) { 153 | ... 154 | int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType, 155 | aInfo, rInfo, voiceSession, voiceInteractor, 156 | resultTo, resultWho, requestCode, callingPid, 157 | callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, 158 | options, ignoreTargetSecurity, componentSpecified, outRecord, inTask, 159 | reason); 160 | ... 161 | } 162 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java#263 163 | int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent, 164 | String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, 165 | IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, 166 | IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, 167 | String callingPackage, int realCallingPid, int realCallingUid, int startFlags, 168 | ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, 169 | ActivityRecord[] outActivity, TaskRecord inTask, String reason) { 170 | ... 171 | mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType, 172 | aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, 173 | callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, 174 | options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord, 175 | inTask); 176 | } 177 | 178 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java#294 179 | private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, 180 | String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, 181 | IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, 182 | IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, 183 | String callingPackage, int realCallingPid, int realCallingUid, int startFlags, 184 | ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, 185 | ActivityRecord[] outActivity, TaskRecord inTask) { 186 | ... 187 | return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, 188 | options, inTask, outActivity); 189 | } 190 | 191 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java#988 192 | private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, 193 | IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, 194 | int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, 195 | ActivityRecord[] outActivity) { 196 | ... 197 | result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, 198 | startFlags, doResume, options, inTask, outActivity); 199 | ... 200 | } 201 | 202 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java#1015 203 | private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, 204 | IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, 205 | int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, 206 | ActivityRecord[] outActivity) { 207 | ... 208 | mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, 209 | mOptions); 210 | ... 211 | } 212 | ``` 213 | 214 | ### 6. 到 `ActivityStackSupervisor#resumeFocusedStackTopActivityLocked` 中查看发现其调用了 `ActivityStack#resumeTopActivityUncheckedLocked`,然后是 `ActivityStack#resumeTopActivityInnerLocked`,接着变又回到 `ActivityStackSupervisor.java`,调用了 `ActivityStackSupervisor#startSpecificActivityLocked`,这个方法中会判断要启动 App 的进程是否存在,存在则通知进程启动 Activity,否则就先将进程创建出来,其源码如下所示: 215 | 216 | ```java 217 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java#2085 218 | boolean resumeFocusedStackTopActivityLocked( 219 | ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { 220 | ... 221 | return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); 222 | ... 223 | } 224 | 225 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java#2245 226 | boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { 227 | ... 228 | result = resumeTopActivityInnerLocked(prev, options); 229 | ... 230 | } 231 | 232 | http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStack.java#2286 233 | private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { 234 | ... 235 | mStackSupervisor.startSpecificActivityLocked(next, true, true); 236 | ... 237 | } 238 | 239 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java#1560 240 | void startSpecificActivityLocked(ActivityRecord r, 241 | boolean andResume, boolean checkConfig) { 242 | ... 243 | if (app != null && app.thread != null) { 244 | ... 245 | // 如果进程已存在,则通知进程启动组件 246 | realStartActivityLocked(r, app, andResume, checkConfig); 247 | return; 248 | ... 249 | } 250 | // 否则先将进程创建出来 251 | mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, 252 | "activity", r.intent.getComponent(), false, false, true); 253 | ... 254 | } 255 | ``` 256 | 257 | ### 7. 我们分析进程尚未存在的情况,因为我们后续还会再次遇到 `ActivityStackSupervisor#realStartActivityLocked`,`ActivityStackSupervisor#startSpecificActivityLocked` 中创建进程使用到的 `mService` 为 `ActivityManagerService`,我们查看 `ActivityManagerService#startProcessLocked` 的源码如下所示: 258 | 259 | ```java 260 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java#3777 261 | private final void startProcessLocked(ProcessRecord app, String hostingType, 262 | String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) { 263 | ... 264 | if (entryPoint == null) entryPoint = "android.app.ActivityThread"; 265 | startResult = Process.start(entryPoint, 266 | app.processName, uid, uid, gids, debugFlags, mountExternal, 267 | app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, 268 | app.info.dataDir, invokeWith, entryPointArgs); 269 | ... 270 | } 271 | ``` 272 | 273 | ### 8. 发现最终调用的事 `Process#start` 来启动进程,进程的入口就是在 `android.app.ActivityThread.java` 类中的 `main()` 函数,因此接下来我们从 `ActivityThread#main` 来分析,其调用了 `ActivityThread#attach`,其中 `ActivityManager.getService()` 之前提到过,返回的是一个是实现 `IActivityManager` 类型的 `Binder` 对象,它的具体实现是在 `ActivityManagerService` 中,相关源码如下所示: 274 | 275 | ```java 276 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#6459 277 | public static void main(String[] args) { 278 | ... 279 | Looper.prepareMainLooper(); 280 | 281 | ActivityThread thread = new ActivityThread(); 282 | thread.attach(false); 283 | ... 284 | Looper.loop(); 285 | ... 286 | } 287 | 288 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#6315 289 | private void attach(boolean system) { 290 | sCurrentActivityThread = this; 291 | mSystemThread = system; 292 | if (!system) { 293 | ... 294 | final IActivityManager mgr = ActivityManager.getService(); 295 | try { 296 | mgr.attachApplication(mAppThread); 297 | } catch (RemoteException ex) { 298 | ... 299 | } 300 | ... 301 | } 302 | ... 303 | } 304 | ``` 305 | 306 | ### 9. 我们又回到了 `ActivityManagerService` 中,查看其 `attachApplication` 函数,发现调用了 `thread#bindApplication` 和 `mStackSupervisor#attachApplicationLocked` 我们依次讲解这两个方法要做的事情,源码如下所示: 307 | 308 | ```java 309 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java#7215 310 | public final void attachApplication(IApplicationThread thread) { 311 | ... 312 | attachApplicationLocked(thread, callingPid); 313 | ... 314 | } 315 | 316 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java#6911 317 | private final boolean attachApplicationLocked(IApplicationThread thread, 318 | int pid) { 319 | .... 320 | thread.bindApplication(processName, appInfo, providers, 321 | app.instr.mClass, 322 | profilerInfo, app.instr.mArguments, 323 | app.instr.mWatcher, 324 | app.instr.mUiAutomationConnection, testMode, 325 | mBinderTransactionTrackingEnabled, enableTrackAllocation, 326 | isRestrictedBackupMode || !normalMode, app.persistent, 327 | new Configuration(getGlobalConfiguration()), app.compat, 328 | getCommonServicesLocked(app.isolated), 329 | mCoreSettingsObserver.getCoreSettingsLocked(), 330 | buildSerial); 331 | ... 332 | if (normalMode) { 333 | try { 334 | if (mStackSupervisor.attachApplicationLocked(app)) { 335 | didSomething = true; 336 | } 337 | } catch (Exception e) { 338 | ... 339 | } 340 | } 341 | ... 342 | } 343 | ``` 344 | 345 | ### 10. 上面说到的`thread#bindApplication` 中的这个 `thread` 是来自于 `ActivityThread#mAppThread`,其类型是 `ApplicationThread`,是 `ActivityThread` 的一个内部类,继承自 `IApplicationThread.Stub`,我们来查看 `ApplicationThread#bindApplication`,发现最后调用了 `ActivityThread#sendMessage` 方法,它内部调用了 `mH.sendMessage` 来发送消息,`mH` 是 `ActivityThread` 的内部类 `H` 的一个实例,查看 `H#handleMessage` 来查看它是怎么处理发送过来的消息,其最终走到了 `ActivityThread#handleBindApplication`。 346 | 347 | 在源码中我们可以发现它先创建 `mInstrumentation` 对象,调用 `data#info#makeApplication` 来创建 `Application` 对象,其对象 `data#info` 为 `LoadedApk` 的一个实例,查看 `LoadedApk#makeApplication` 中代码可以发现,其调用了 `Instrumentation#newApplication` 方法,内部靠 `Class#newInstance()` 完成对 `Application` 实例化,然后调用 `Application#attach(context)` 来绑定 `Context`。 348 | 349 | 以上创建完 `Application` 对象后便是调用 `Instrumentation#callApplicationOnCreate` 走 `Application` 的 `onCreate` 生命周期,以上涉及到的全部源码如下所示: 350 | 351 | ```java 352 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#899 353 | public final void bindApplication(String processName, ApplicationInfo appInfo, 354 | List providers, ComponentName instrumentationName, 355 | ProfilerInfo profilerInfo, Bundle instrumentationArgs, 356 | IInstrumentationWatcher instrumentationWatcher, 357 | IUiAutomationConnection instrumentationUiConnection, int debugMode, 358 | boolean enableBinderTracking, boolean trackAllocation, 359 | boolean isRestrictedBackupMode, boolean persistent, Configuration config, 360 | CompatibilityInfo compatInfo, Map services, Bundle coreSettings, 361 | String buildSerial) { 362 | ... 363 | sendMessage(H.BIND_APPLICATION, data); 364 | } 365 | 366 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#2593 367 | private void sendMessage(int what, Object obj) { 368 | sendMessage(what, obj, 0, 0, false); 369 | } 370 | 371 | private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { 372 | if (DEBUG_MESSAGES) Slog.v( 373 | TAG, "SCHEDULE " + what + " " + mH.codeToString(what) 374 | + ": " + arg1 + " / " + obj); 375 | Message msg = Message.obtain(); 376 | msg.what = what; 377 | msg.obj = obj; 378 | msg.arg1 = arg1; 379 | msg.arg2 = arg2; 380 | if (async) { 381 | msg.setAsynchronous(true); 382 | } 383 | mH.sendMessage(msg); 384 | } 385 | 386 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#1580 387 | public void handleMessage(Message msg) { 388 | ... 389 | switch (msg.what) { 390 | ... 391 | case BIND_APPLICATION: 392 | Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); 393 | AppBindData data = (AppBindData)msg.obj; 394 | handleBindApplication(data); 395 | Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 396 | break; 397 | ... 398 | } 399 | } 400 | 401 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#5429 402 | private void handleBindApplication(AppBindData data) { 403 | ... 404 | final InstrumentationInfo ii; 405 | ... 406 | // 创建 mInstrumentation 实例 407 | if (ii != null) { 408 | final ApplicationInfo instrApp = new ApplicationInfo(); 409 | ii.copyTo(instrApp); 410 | instrApp.initForUser(UserHandle.myUserId()); 411 | final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, 412 | appContext.getClassLoader(), false, true, false); 413 | final ContextImpl instrContext = ContextImpl.createAppContext(this, pi); 414 | 415 | try { 416 | final ClassLoader cl = instrContext.getClassLoader(); 417 | mInstrumentation = (Instrumentation) 418 | cl.loadClass(data.instrumentationName.getClassName()).newInstance(); 419 | } catch (Exception e) { 420 | ... 421 | } 422 | ... 423 | } else { 424 | mInstrumentation = new Instrumentation(); 425 | } 426 | ... 427 | Application app; 428 | ... 429 | // 创建 Application 实例 430 | try { 431 | ... 432 | app = data.info.makeApplication(data.restrictedBackupMode, null); 433 | mInitialApplication = app; 434 | ... 435 | try { 436 | mInstrumentation.callApplicationOnCreate(app); 437 | } catch (Exception e) { 438 | ... 439 | } 440 | } finally { 441 | ... 442 | } 443 | ... 444 | } 445 | 446 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/LoadedApk.java#959 447 | public Application makeApplication(boolean forceDefaultAppClass, 448 | Instrumentation instrumentation) { 449 | Application app = null; 450 | 451 | String appClass = mApplicationInfo.className; 452 | if (forceDefaultAppClass || (appClass == null)) { 453 | appClass = "android.app.Application"; 454 | } 455 | 456 | try { 457 | java.lang.ClassLoader cl = getClassLoader(); 458 | ... 459 | ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); 460 | app = mActivityThread.mInstrumentation.newApplication( 461 | cl, appClass, appContext); 462 | appContext.setOuterContext(app); 463 | } catch (Exception e) { 464 | ... 465 | } 466 | mActivityThread.mAllApplications.add(app); 467 | mApplication = app; 468 | 469 | if (instrumentation != null) {// 传入为 null 所以不走 470 | try { 471 | instrumentation.callApplicationOnCreate(app); 472 | } catch (Exception e) { 473 | ... 474 | } 475 | } 476 | ... 477 | return app; 478 | } 479 | 480 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/Instrumentation.java#1084 481 | public Application newApplication(ClassLoader cl, String className, Context context) 482 | throws InstantiationException, IllegalAccessException, 483 | ClassNotFoundException { 484 | return newApplication(cl.loadClass(className), context); 485 | } 486 | 487 | static public Application newApplication(Class clazz, Context context) 488 | throws InstantiationException, IllegalAccessException, 489 | ClassNotFoundException { 490 | Application app = (Application)clazz.newInstance(); 491 | app.attach(context); 492 | return app; 493 | } 494 | ``` 495 | 496 | ### 11. 说完了 9 中的 `thread#bindApplication`,下面我们继续说 `mStackSupervisor#attachApplicationLocked`,其 `mStackSupervisor` 是 `ActivityStackSupervisor` 的一个实例,我们查看 `ActivityStackSupervisor#attachApplicationLocked` 方法中发现会调用 `ActivityStackSupervisor#realStartActivityLocked`,其方法会调用 `app#thread#scheduleLaunchActivity`,源码如下所示: 497 | 498 | ```java 499 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java#956 500 | boolean attachApplicationLocked(ProcessRecord app) throws RemoteException { 501 | final String processName = app.processName; 502 | ... 503 | if (realStartActivityLocked(activity, app, 504 | top == activity /* andResume */, true /* checkConfig */)) { 505 | ... 506 | } 507 | ... 508 | } 509 | 510 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java#1313 511 | final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, 512 | boolean andResume, boolean checkConfig) throws RemoteException { 513 | ... 514 | app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, 515 | System.identityHashCode(r), r.info, 516 | // TODO: Have this take the merged configuration instead of separate global 517 | // and override configs. 518 | mergedConfiguration.getGlobalConfiguration(), 519 | mergedConfiguration.getOverrideConfiguration(), r.compat, 520 | r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, 521 | r.persistentState, results, newIntents, !andResume, 522 | mService.isNextTransitionForward(), profilerInfo); 523 | ... 524 | } 525 | ``` 526 | 527 | ### 12. 上面说到的 `app#thread#scheduleLaunchActivity` 中的 `thread` 是前面提到过的 `IApplicationThread`,它的实现类是 `ActivityThread#ApplicationThread` ,我们查看 `ActivityThread#ApplicationThread#scheduleLaunchActivity` 中的代码发现最终是发送 `LAUNCH_ACTIVITY` 消息,这步我们在第 10 步中有过分析,我们直接查看其处理消息相关代码即可,在 `H#handleMessage` 中,我们可以看到其会接收并处理很多和四大组件相关的操作,我们查看对 `LAUNCH_ACTIVITY` 的处理,发现对其处理的方法是 `ActivityThread#handleLaunchActivity`,它调用到了 `ActivityThread#performLaunchActivity` 方法,其中的实现再次涉及到了 `Instrumentation` 类,之前是在创建 `Application` 对象用到了它,如今是创建 `Activity` 对象又用到了它,其 `Instrumentation#newActivity` 也是通过 `Class.newInstance()` 来实例化 `Activity`,实例化结束后回到 `ActivityThread#performLaunchActivity` 中来让 `activity` 依附到 window 中,然后`callActivityOnCreate` 走 `Activity` 的 `onCreate` 生命周期,涉及到的源码如下所示: 528 | 529 | ```java 530 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#756 531 | public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, 532 | ActivityInfo info, Configuration curConfig, Configuration overrideConfig, 533 | CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, 534 | int procState, Bundle state, PersistableBundle persistentState, 535 | List pendingResults, List pendingNewIntents, 536 | boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { 537 | ... 538 | sendMessage(H.LAUNCH_ACTIVITY, r); 539 | } 540 | 541 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#1580 542 | public void handleMessage(Message msg) { 543 | ... 544 | switch (msg.what) { 545 | ... 546 | case LAUNCH_ACTIVITY: { 547 | Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); 548 | final ActivityClientRecord r = (ActivityClientRecord) msg.obj; 549 | 550 | r.packageInfo = getPackageInfoNoCheck( 551 | r.activityInfo.applicationInfo, r.compatInfo); 552 | handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); 553 | Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 554 | } break; 555 | ... 556 | } 557 | } 558 | 559 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#2833 560 | private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { 561 | ... 562 | Activity a = performLaunchActivity(r, customIntent); 563 | ... 564 | } 565 | 566 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/ActivityThread.java#2644 567 | private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 568 | ... 569 | Activity activity = null; 570 | try { 571 | java.lang.ClassLoader cl = appContext.getClassLoader(); 572 | activity = mInstrumentation.newActivity( 573 | cl, component.getClassName(), r.intent); 574 | ... 575 | } catch (Exception e) { 576 | ... 577 | } 578 | 579 | try { 580 | // 返回之前创建过的 application 对象 581 | Application app = r.packageInfo.makeApplication(false, mInstrumentation); 582 | ... 583 | if (activity != null) { 584 | ... 585 | // attach 到 window 上 586 | activity.attach(appContext, this, getInstrumentation(), r.token, 587 | r.ident, app, r.intent, r.activityInfo, title, r.parent, 588 | r.embeddedID, r.lastNonConfigurationInstances, config, 589 | r.referrer, r.voiceInteractor, window, r.configCallback); 590 | ... 591 | if (r.isPersistable()) { 592 | mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); 593 | } else { 594 | mInstrumentation.callActivityOnCreate(activity, r.state); 595 | } 596 | ... 597 | } 598 | } catch (Exception e) { 599 | ... 600 | } 601 | return activity; 602 | } 603 | 604 | // http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/app/Instrumentation.java#1143 605 | public Activity newActivity(ClassLoader cl, String className, 606 | Intent intent) 607 | throws InstantiationException, IllegalAccessException, 608 | ClassNotFoundException { 609 | return (Activity)cl.loadClass(className).newInstance(); 610 | } 611 | 612 | public Activity newActivity(Class clazz, Context context, 613 | IBinder token, Application application, Intent intent, ActivityInfo info, 614 | CharSequence title, Activity parent, String id, 615 | Object lastNonConfigurationInstance) throws InstantiationException, 616 | IllegalAccessException { 617 | Activity activity = (Activity)clazz.newInstance(); 618 | ActivityThread aThread = null; 619 | activity.attach(context, aThread, this, token, 0 /* ident */, application, intent, 620 | info, title, parent, id, 621 | (Activity.NonConfigurationInstances)lastNonConfigurationInstance, 622 | new Configuration(), null /* referrer */, null /* voiceInteractor */, 623 | null /* window */, null /* activityConfigCallback */); 624 | return activity; 625 | } 626 | ``` 627 | 628 | 到此为止,一个 App 的启动过程已分析结束,最后献上启动涉及到的类的流程图: 629 | 630 | ![App 的启动流程图](http://ww1.sinaimg.cn/large/b75b8776gy1fulx2ikj15j20ia0o6mxs.jpg) 631 | 632 | 633 | ## 结语 634 | 635 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /android/App 打包流程.md: -------------------------------------------------------------------------------- 1 | # App 打包流程 2 | 3 | ![](http://ww1.sinaimg.cn/large/b75b8776gy1fuamn2azgej20qe0togmu.jpg) -------------------------------------------------------------------------------- /android/EditText 搜索优化.md: -------------------------------------------------------------------------------- 1 | # EditText 搜索优化 2 | 3 | ## 描述 4 | 5 | 设计一个 EditText 的文本监听器,停止输入 1s 后,如果文本发生变化则触发监听器。 6 | 7 | 例子: 8 | 文本内容是 111,111 -> 1111 -> 11111,连续输入都小于 1s,在输完后的 1s 触发监听器为 11111; 9 | 文本内容是 111,111 -> 1111 -> 111,连续输入都小于 1s,在输完后的 1s 不触发监听器; 10 | 11 | 类似微信的客户端搜索,不同的是微信在 111 -> 1111 -> 111 是会触发改变的。 12 | 13 | 14 | 15 | 效果如下所示,注意观察 title 文本的改变: 16 | 17 | ![demo.gif](http://ww1.sinaimg.cn/large/b75b8776ly1g6prj25wgsg206k0dwaod.gif) 18 | 19 | ## 参考答案 20 | 代码很简单,结合注释参考即可,小功能从构思到编码到完成到优化,其实也是要花不少时间的,希望可以帮到你们哈。 21 | ```java 22 | public class SearchEditText extends EditText { 23 | 24 | private static final long LIMIT = 1000; 25 | 26 | private OnTextChangedListener mListener; 27 | private String mStartText = "";// 记录开始输入前的文本内容 28 | private Runnable mAction = new Runnable() { 29 | @Override 30 | public void run() { 31 | if (mListener != null) { 32 | // 判断最终和开始前是否一致 33 | if (!StringUtils.equals(mStartText, getText().toString())) { 34 | mStartText = getText().toString();// 更新 mStartText 35 | mListener.onTextChanged(mStartText); 36 | } 37 | } 38 | } 39 | }; 40 | 41 | public SearchEditText(Context context) { 42 | super(context); 43 | } 44 | 45 | public SearchEditText(Context context, AttributeSet attrs) { 46 | super(context, attrs); 47 | } 48 | 49 | public SearchEditText(Context context, AttributeSet attrs, int defStyleAttr) { 50 | super(context, attrs, defStyleAttr); 51 | } 52 | 53 | /** 54 | * 在 LIMIT 时间内连续输入不触发文本变化 55 | */ 56 | public void setOnTextChangedListener(OnTextChangedListener listener) { 57 | mListener = listener; 58 | } 59 | 60 | @Override 61 | protected void onTextChanged(final CharSequence text, int start, int lengthBefore, int lengthAfter) { 62 | super.onTextChanged(text, start, lengthBefore, lengthAfter); 63 | // 移除上一次的回调 64 | removeCallbacks(mAction); 65 | postDelayed(mAction, LIMIT); 66 | } 67 | 68 | @Override 69 | protected void onDetachedFromWindow() { 70 | super.onDetachedFromWindow(); 71 | removeCallbacks(mAction); 72 | } 73 | 74 | public interface OnTextChangedListener { 75 | void onTextChanged(String text); 76 | } 77 | } 78 | ``` 79 | 80 | 81 | 82 | ## 结语 83 | 84 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /android/RecyclerView 性能优化.md: -------------------------------------------------------------------------------- 1 | # RecyclerView 性能优化 2 | 3 | 阿里四面有三面都问了这个问题,在此做了整理,希望可以帮助到大家,欢迎查漏补缺。 4 | 5 | ## 数据处理和视图加载分离 6 | 7 | 我们知道,从远端拉取数据肯定是要放在异步的,在我们拉取下来数据之后可能就匆匆把数据丢给了 VH 处理,其实,数据的处理逻辑我们也应该放在异步处理,这样 Adapter 在 notify change 后,ViewHolder 就可以简单无压力地做数据与视图的绑定逻辑,比如: 8 | 9 | ```java 10 | mTextView.setText(Html.fromHtml(data).toString()); 11 | ``` 12 | 13 | 这里的 `Html.fromHtml(data)` 方法可能就是比较耗时的,存在多个 `TextView` 的话耗时会更为严重,这样便会引发掉帧、卡顿,而如果把这一步与网络异步线程放在一起,站在用户角度,最多就是网络刷新时间稍长一点。 14 | 15 | 16 | ## 数据优化 17 | 18 | 分页拉取远端数据,对拉取下来的远端数据进行缓存,提升二次加载速度;对于新增或者删除数据通过 `DiffUtil` 来进行局部刷新数据,而不是一味全局地刷新数据。 19 | 20 | 21 | ## 布局优化 22 | 23 | ### 减少过渡绘制 24 | 25 | 减少布局层级,可以考虑使用自定义 View 来减少层级,或者更合理地设置布局来减少层级,不推荐在 RecyclerView 中使用 `ConstraintLayout`,有很多开发者已经反映了使用它效果更差,相关链接有:[Is ConstraintLayout that slow?](https://www.reddit.com/r/androiddev/comments/7ylbz3/is_constraintlayout_that_slow/)、[constraintlayout 1.1.1 not work well in listview](https://issuetracker.google.com/issues/112000722)。 26 | 27 | 28 | ### 减少 xml 文件 inflate 时间 29 | 30 | 这里的 xml 文件不仅包括 layout 的 xml,还包括 drawable 的 xml,xml 文件 inflate 出 ItemView 是通过耗时的 IO 操作,尤其当 Item 的复用几率很低的情况下,随着 Type 的增多,这种 inflate 带来的损耗是相当大的,此时我们可以用代码去生成布局,即 `new View()` 的方式,只要搞清楚 xml 中每个节点的属性对应的 API 即可。 31 | 32 | 33 | ### 减少 View 对象的创建 34 | 35 | 一个稍微复杂的 Item 会包含大量的 View,而大量的 View 的创建也会消耗大量时间,所以要尽可能简化 ItemView;设计 ItemType 时,对多 ViewType 能够共用的部分尽量设计成自定义 View,减少 View 的构造和嵌套。 36 | 37 | 38 | ## 其他 39 | 40 | 其他并不代表不重要,而是我不能把他们进行分类哈,其中可能某些操作会对你的 RecyclerView 有很大的优化。 41 | 42 | * 升级 `RecycleView` 版本到 25.1.0 及以上使用 Prefetch 功能,可参考 [RecyclerView 数据预取](https://juejin.im/entry/58a3f4f62f301e0069908d8f)。 43 | 44 | * 如果 Item 高度是固定的话,可以使用 `RecyclerView.setHasFixedSize(true);` 来避免 `requestLayout` 浪费资源; 45 | 46 | * 设置 `RecyclerView.addOnScrollListener(listener);` 来对滑动过程中停止加载的操作。 47 | 48 | * 如果不要求动画,可以通过 `((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false);` 把默认动画关闭来提升效率。 49 | 50 | * 对 `TextView` 使用 `String.toUpperCase` 来替代 `android:textAllCaps="true"`。 51 | 52 | * 对 `TextView` 使用 `StaticLayout` 或者 `DynamicLayout` 的自定义 `View` 来代替它。 53 | 54 | * 通过重写 `RecyclerView.onViewRecycled(holder)` 来回收资源。 55 | 56 | * 通过 `RecycleView.setItemViewCacheSize(size);` 来加大 `RecyclerView` 的缓存,用空间换时间来提高滚动的流畅性。 57 | 58 | * 如果多个 `RecycledView` 的 `Adapter` 是一样的,比如嵌套的 `RecyclerView` 中存在一样的 `Adapter`,可以通过设置 `RecyclerView.setRecycledViewPool(pool);` 来共用一个 `RecycledViewPool`。 59 | 60 | * 对 `ItemView` 设置监听器,不要对每个 Item 都调用 `addXxListener`,应该大家公用一个 `XxListener`,根据 `ID` 来进行不同的操作,优化了对象的频繁创建带来的资源消耗。 61 | 62 | * 通过 getExtraLayoutSpace 来增加 RecyclerView 预留的额外空间(显示范围之外,应该额外缓存的空间),如下所示: 63 | ``` 64 | new LinearLayoutManager(this) { 65 | @Override 66 | protected int getExtraLayoutSpace(RecyclerView.State state) { 67 | return size; 68 | } 69 | }; 70 | ``` 71 | 72 | 73 | ## 结语 74 | 75 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /base/net/TCP 与 UDP 的区别.md: -------------------------------------------------------------------------------- 1 | # TCP 与 UDP 的区别 2 | 3 | | 区别点 | TCP | UDP | 4 | | -------- | -------- | ------ | 5 | | 连接性 | 面向连接 | 无连接 | 6 | | 可靠性 | 可靠 | 不可靠| 7 | | 有序性 | 有序 | 无序 | 8 | | 面向 | 字节流 | 报文(保留报文的边界) | 9 | | 有界性 | 有界 | 无界 | 10 | | 流量控制 | 有(滑动窗口) | 无 | 11 | | 拥塞控制 | 有(慢开始、拥塞避免、快重传、快恢复) | 无 | 12 | | 传输速度 | 慢 | 快 | 13 | | 量级 | 重量级 | 轻量级 | 14 | | 双工性 | 全双工 | 一对一、一对多、多对一、多对多 | 15 | | 头部 | 大(20-60 字节) | 小(8 字节) | 16 | | 应用 | 文件传输、邮件传输、浏览器等 | 即时通讯、视频通话等 | 17 | 18 | 19 | ## 结语 20 | 21 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /base/net/网络模型.md: -------------------------------------------------------------------------------- 1 | # 网络模型 2 | 3 | 4 | 5 | 6 | ## 结语 7 | 8 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /base/os/进程和线程的区别.md: -------------------------------------------------------------------------------- 1 | # 进程和线程的区别 2 | 3 | ## 典型回答 4 | 5 | 1. 进程是操作系统资源分配的基本单位,而线程是 CPU 调度和分配的基本单位 6 | 2. 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是一个进程中的不同的执行路径 7 | 3. 进程是执行的程序,线程是进程内的控制流。 8 | 9 | 10 | ## 知识延伸 11 | 12 | 进程是现代分时操作系统的工作单元,进程是执行的程序,这是一种非正式的说法。进程不只是程序代码,进程还包括当前活动,如程序计数器、进程堆栈和数据段。 13 | 14 | 操作系统内的每个进程表示,采用进程控制块(Process Control Block,PCB),它包含了许多与某个特定进程相关的信息: 15 | 16 | * 进程状态:状态可以包括新的、就绪、运行、等待、停止等。 17 | * 程序计数器:计数器表示进程将要执行的下个指令的地址。 18 | * CPU 寄存器:根据计算机体系结构的不同,寄存器的类型和数量也会不同。它们包括累加器、索引寄存器、堆栈指针、通用寄存器和其他条件码信息寄存器。在发生中断时,这些状态信息与程序计数器一起需要保存,以便进程以后能正确的继续执行。 19 | * CPU 调度信息:这类信息包括进程优先级、调度队列的指针和其他调度参数。 20 | * 内存管理信息:根据操作系统使用的内存系统,这类信息可以包括基地址和界限寄存器的值、页表或段表。 21 | * 记账信息:这类信息包括 CPU 时间、实际使用时间、时间期限、记账数据、作业或进程数量等。 22 | * I/O 状态信息:这类信息包括分配给进程的 I/O 设备列表、打开文件列表等。 23 | 24 | PCB 结构如下图: 25 | 26 | ![](http://ww1.sinaimg.cn/mw690/b75b8776ly1gagdt1bvxnj205508t749.jpg) 27 | 28 | 每个线程是 CPU 使用的一个基本单元;它包括线程 ID、程序计数器、寄存器组和堆栈。。它与同一进程的其他线程共享代码段、数据段和其他操作系统资源,如打开文件和信号。每个传统或重量级进程只有单个控制线程,如果一个进程具有多个控制线程,那么它能同时执行多个任务,下图说明了传统单线程进程和多线程进程的差异: 29 | 30 | ![](http://ww1.sinaimg.cn/mw690/b75b8776ly1gagdtm540eg20fv09bt8s.gif) 31 | 32 | 对于多处理器体系结构,多线程的优点更大,因为线程可以在多处理核上并行运行。不管有多少可用 CPU,单线程进程只能运行在一个 CPU 上。 33 | 34 | 35 | ## 扩展阅读 36 | 37 | 知乎上有一个对于 “线程和进程的区别” 的回答非常好,总结一句话是: 38 | 39 | **进程和线程都是一个时间段的描述,是 CPU 工作时间段的描述,只是颗粒不同罢了。** 40 | 41 | 原文地址如下:[线程和进程的区别是什么?](https://www.zhihu.com/question/25532384/answer/81152571) 42 | 43 | 44 | ## 结语 45 | 46 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /base/os/进程有哪几种状态.md: -------------------------------------------------------------------------------- 1 | # 进程有哪几种状态 2 | 3 | ## 典型回答 4 | 5 | 进程在执行时会改变状态。进程状态,部分取决于进程的当前活动。每个进程可能处于以下状态: 6 | 7 | * 新的(New):进程正在创建 8 | * 运行(Running):指令正在执行 9 | * 等待(Waiting):进程等待发生某个事件(如 I/O 完成或收到信号) 10 | * 就绪(Ready):进程等待分配处理器 11 | * 终止(Terminated):进程完成执行 12 | 13 | 这些状态名称比较随意,而且随着操作系统的不同而有所不同。不过,它们表示的状态在所有操作系统上都会出现。有的操作系统对进程状态定义的更细。重要的是认识到:一次只有一个进程可以在一个处理器上运行,但是许多进程可处于就绪或等待状态。下面是一个进程状态图: 14 | 15 | ![进程状态](http://ww1.sinaimg.cn/mw690/b75b8776ly1gage04w085j20hq09174w.jpg) 16 | 17 | 18 | ## 结语 19 | 20 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /base/os/进程调度策略.md: -------------------------------------------------------------------------------- 1 | # 进程调度策略 2 | 3 | ## 典型回答 4 | 5 | CPU 调度是多道程序操作系统的基础。通过在进程间切换 CPU,操作系统可以使得计算机更加高效。每当 CPU 空闲时,操作系统就应从就绪队列中选择一个进程来执行。CPU 调度算法有以下几种: 6 | 7 | 1. 先到先服务调度 8 | 2. 最短作业优先调度 9 | 3. 优先级调度 10 | 4. 轮转调度 11 | 5. 多级队列调度 12 | 6. 多级反馈队列调度 13 | 14 | 先来先服务(FCFS)调度是最简单的调度算法,但是它会让短进程等待很长的进程。最短作业优先调度(SJF)调度可证明是最佳的,提供最短的平均等待时间,然而,SJF 的实现是很难的,因为预测下一个 CPU 执行的长度是难的。SJF 算法是通用优先级调度算法(简单分配 CPU 到具有最高优先级的进程)的一个特例。优先级和 SJF 的调度可能产生饥饿。老化技术阻止饥饿。 15 | 16 | 轮转(RR)调度更适合于分时(交互)系统。RR 调度为就绪队列的首个进程,分配 q 个时间单位,这里 q 是时间片。在 q 个时间单位之后,如果该进程还没有释放 CPU,那么它被抢占并添加到就绪队列的尾部。该算法的主要问题是选择时间片,如果时间片太大,那么 RR 调度就成了 FCFS 调度;如果时间片太小,那么由于上下文切换引起的调度开销就过大。 17 | 18 | FCFS 调度算法是非抢占的,而 RR 调度算法是抢占的。SJF 和优先级算法可以是抢占的,也可以是非抢占的。 19 | 20 | 多级队列算法允许多个不同算法用于不同类型的进程。最常用的模型包括:使用 RR 调度的前台交互队列与使用 FCFS 调度的后台批处理队列。多级反馈队列允许进程在队列之间迁移。 21 | 22 | ## 知识延伸 23 | 24 | 对于支持线程的操作系统,操作系统实际调度的是内核级线程而非进程。 25 | 26 | 用户级线程是由线程库来管理的,而内核并不知道它们。用户级线程为了运行在 CPU 上,最终应映射到相关的内核级线程,但是这种映射可能不是直接的,可能采用轻量级进程(LWP)。 27 | 28 | ### 多处理器调度 29 | 30 | 对于多处理器系统,CPU 调度的一种方法是让一个处理器处理所有调度决定、I/O 处理以及其他系统活动,其他处理器只执行用户代码。这种非对称多处理很简单,因为只有一个处理器访问系统数据结构,减少了数据共享的需要。 31 | 32 | 第二种方法是使用对称多处理(SMP),即每个处理器自我调度。所有进程可能处于一个共同的就绪队列中,或每个处理器都有它自己的私有就绪进程队列。不管如何,调度这样进行:每个处理器的调度程序都检查共同就绪队列,以便选择执行一个进程。 33 | 34 | 大多数 SMP 系统试图避免将进程从一个处理器移到另一个处理器,而是试图让一个进程运行在同一个处理器上。这称为处理器的亲和性,即一个进程对它运行的处理器具有亲和性。 35 | 36 | 对于 SMP 系统,最重要的是保持所有处理器的负载平衡,以便充分利用多处理器的优点。负载平衡设法将负载平均分配到 SMP 系统的所有处理器。负载平衡通常有两种方法:推迁移和拉迁移。但是,负载平衡往往会抵消掉处理器亲和性的好处,也就是说,保持进程运行在同一个处理器上的好处是进程可以利用它在该处理器缓存内的数据。 37 | 38 | 接下来,我们讨论 Linux 和 Windows 的调度策略。重要的是要注意,我们使用的进程调度这一术语是泛指的。事实上,在讨论 Windows 系统时,采用内核线程调度,而在讨论 Linux 时,采用任务调度。 39 | 40 | ### Linux 调度 41 | 42 | 完全公平调度程序(CFS)是默认的 Linux 调度算法。 43 | 44 | Linux 系统的调度基于调度类,每个类都有一个特定的优先级。内核针对不同的调度类,采用不同的调度算法,以便满足系统与进程的需要。Linux 标准内核实现了两个调度类:采用 CFS 调度算法的默认调度类和实时调度类。 45 | 46 | CFS 调度程序并不采用严格规则来为一个优先级分配某个长度的时间片,而是为每个任务分配一定比例的 CPU 处理时间。 47 | 48 | Linux CFS 调度程序采用高效算法,以便选择运行下个任务。每个可运行的任务放置在红黑树上。 49 | 50 | 当一个任务变得可运行时,它被添加到树上。当一个任务变得不可运行时,它从树上删除。一般来说,得到较少处理时间的任务会偏向树的左侧;得到较多处理时间的任务会偏向树的右侧。由于红黑树是平衡的,找到最左侧结点需要 lg N 的·时间复杂度。从 CFS 调度程序角度而言,这也是具有最高优先级的任务。 51 | 52 | ### Windows 调度 53 | 54 | Windows 采用基于优先级的、抢占调度算法来调度线程。Windows 调度程序确保具有最高优先级的线程总是在运行的。用于处理调度的 Windows 内核部分称为调度程序。 55 | 56 | 调度程序采用 32 级的优先级方案,以便确定线程执行方案。 57 | 58 | 59 | ## 结语 60 | 61 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /base/os/进程间通信的几种方式.md: -------------------------------------------------------------------------------- 1 | # 进程间通信的几种方式 2 | 3 | ## 典型回答 4 | 5 | 1. 套接字 6 | 套接字为通信的端点。通过网络通信的每对进程需要使用一对套接字,即每个进程各有一个。每个套接字由一个 IP 地址和一个端口号组成。通常,套接字采用 CS 架构,服务器通过监听指定的端口,来等待特定服务。服务器在收到请求后,接受来自客户端套接字的连接,从而完成连接。 7 | 8 | 2. 管道 9 | 管道提供了一个相对简单的进程间的相互通信,普通管道允许父进程和子进程之间的通信,而命名管道允许不相关进程之间的通信。 10 | 11 | ## 知识延伸 12 | 13 | 进程间通信有两种基本模型:**共享内存** 和 **消息传递**。 14 | 15 | 共享内存模型会建立起一块供协作进程共享的内存区域,进程通过向此共享区域读出或写入数据来交换信息。消息传递模型通过在协作进程间交换信息来实现通信。 16 | 17 | 下图给出了两个模型的对比: 18 | 19 | ![](http://ww1.sinaimg.cn/mw690/b75b8776ly1gage4sa3btg20ex09hmx5.gif) 20 | 21 | 很多系统同时实现了这两种模型。 22 | 23 | 消息传递对于交换较少数量的数据很有用,因为无需避免冲突。对于分布式系统,消息传递也比共享内存更易实现。共享内存可以快于消息传递,这是因为消息传递的实现经常采用系统调用,因此需要更多的时间以便内核介入。与此相反,共享内存系统仅在建立共享内存区域时需要系统调用;一旦建立共享内存,所有访问都可作为常规内存访问,无需借助内核。 24 | 25 | 对具有多个处理核的系统上,消息传递的性能要优于共享内存。共享内存会有高速缓存一致性问题,这是由共享数据在多个高速缓存之间迁移而引起的。随着系统处理核的日益增加,可能导致消息传递作为 IPC 的首选机制。 26 | 27 | ### 共享内存系统 28 | 29 | 采用共享内存的进程间通信,需要通信进程建立共享内存区域。通常,这一片共享内存区域驻留在创建共享内存段的进程地址空间内。其它希望使用这个共享内存段进行通信的进程应将其附加到自己的地址空间。回忆一下,通常操作系统试图阻止一个进程访问另一个进程的内存。共享内存需要两个或更多的进程同意取消这一限制;这样它们通过在共享区域内读出或写入来交换信息。数据的类型或位置取决于这些进程,而不是受控于操作系统。另外,进程负责确保,它们不向同一位置同时写入数据。 30 | 31 | ### 消息传递模型 32 | 33 | 消息传递提供一种机制,以便允许进程不必通过共享地址空间来实现通信和同步。对分布式环境(通信进程可能位于通过网络连接的不同计算机),这特别有用。 34 | 35 | 需要通信的进程应有一个方法,以便互相引用。它们可以使用直接或间接的通信。 36 | 37 | 对于直接通信,需要通信的每个进程必须明确指定通信的接受者或发送者。采用这种方案,原语 send() 和 receive() 定义如下: 38 | 39 | ``` 40 | send(P, message): 向进程 P 发送 message 41 | receive(Q, message): 从进程 Q 接收 message 42 | ``` 43 | 44 | 这种方案展示了寻址的对称性,即发送和接收进程必须指定对方,以便通信。这种方案的一个变形采用寻址的非对称性,即只要发送者指定接受者,而接受者不需要指定发送者。采用这种方案,原语 send() 和 receive() 定义如下: 45 | 46 | ``` 47 | send(P, message): 向进程 P 发送 message 48 | receive(id, message): 从任何进程接收 message,这里变量 id 被设置成与其通信进程的名称 49 | ``` 50 | 51 | 在间接通信中,通过邮箱或端口来发送和接收消息。邮箱可以抽象成一个对象,进程可以向其中存放消息,也可从中删除消息,每个邮箱都有一个唯一的标识符。一个进程可以通过多个不同邮箱与另一个进程通信,但是两个进程只有拥有一个共享邮箱时才能通信。原语 send() 和 receive() 定义如下: 52 | 53 | ``` 54 | send(A, message): 向邮箱 A 发送 message 55 | receive(A, message): 从邮箱 A 接收 message 56 | ``` 57 | 58 | ## 客户机 / 服务器通信 59 | 60 | 客户机 / 服务器通信的两种策略:套接字和管道。 61 | 62 | 套接字: 63 | 64 | 套接字为通信的端点。通过网络通信的每对进程需要使用一对套接字,即每个进程各有一个。每个套接字由一个 IP 地址和一个端口号组成。通常,套接字采用 CS 架构,服务器通过监听指定的端口,来等待特定服务。服务器在收到请求后,接受来自客户端套接字的连接,从而完成连接。 65 | 66 | Java 提供三种不同类型的套接字。面向连接的 TCP 套接字用 Socket 类实现。无连接的 UDP 套接字使用 DatagramSocket 类。最后,MulticastSocket 类为 DatagramSocket 类的子类,多播套接字允许数据发送到多个接受者。 67 | 68 | 使用套接字的通信,虽然常用和高效,但是属于分布式进程之间的一种低级形式的通信。一个原因是,套接字只允许在通信线程之间交换无结构的字节流。客户机或服务器需要自己加上数据结构。 69 | 70 | 管道: 71 | 72 | 管道允许两个进程进行通信。管道是早期 UNIX 系统最早使用的一种 IPC 机制。管道为进程之间的相互通信提供了一种较为简单的方法,尽管也有一定的局限性。在实现管道时,应该考虑以下四个问题: 73 | 74 | 1. 管道允许单向通信还是双向通信? 75 | 2. 如果允许双向通信,它是半双工(数据在同一时间内只能按一个方向传输)的还是全双工(数据在同一时间内可在两个方向上传输)的? 76 | 3. 通信进程之间知否有一定的关系(如父子关系)? 77 | 4. 管道通信是否能够通过网络,还是只能在同一机器上进行? 78 | 79 | 下面就讨论两种常见类型的用于 UNIX 和 Windows 系统的管道:普通管道和命名管道。 80 | 81 | **普通管道** 82 | 83 | 普通管道允许两个进程按标准的生产者和消费者方式进行通信:生产者向管道的一端(写入端)写,消费者从管道的另一端(读出端)读。因此,普通管道是单向的,只允许单向通信。如果需要双向通信,那么就要采用两个管道,而每个管道向不同方向发送数据。 84 | 85 | 在 UNIX 系统上,普通管道的创建采用函数: 86 | 87 | ``` 88 | pipe(int fd[]) 89 | ``` 90 | 91 | 这个函数创建了一个管道,以便通过文件描述符 int fd[] 来访问:fd[0] 为管道的读出端,而 fd[1] 为管道的写入端。Unix 将管道作为一种特殊类型的文件。因此,访问管道可以采用普通的系统调用 read() 和 write()。 92 | 93 | 普通管道只能由创建进程所访问。通常情况下,父进程创建了一个管道,并使用它与其子进程通信(该进程由 fork() 来创建)。如前面所述,子进程继承了父进程打开的文件的打开文件。由于管道是一种特殊类型的文件,因此子进程也继承了父进程的管道。 94 | 95 | 对于 Windows 系统,普通管道被称为匿名管道,它们的行为类似于 Unix 的管道:它们是单向的,通信进程之间具有父子关系。 96 | 97 | 采用普通管道的进程需要有父子关系,这意味着,这些管道只可用于同一机器的进程间通信。 98 | 99 | **命名管道** 100 | 101 | 普通管道提供了一个简单的机制,允许一对进程通信。然鹅,只有当进程相互通信时,普通管道才存在。对于 Unix 和 Windows 系统,一旦进程已经完成通信并终止了,那么管道就不存在了。 102 | 103 | 命名管道提供了一个更强大的通信工具。通信可以是双向的,并且父子关系不是必需的。当建立一个命名管道后,多个进程多可用它通信。事实上,在一个典型的场景中,一个管道有多个写者。此外,当通信进程完成后,命名管道继续存在。虽然 Unix 和 Windows 系统都支持命名管道,但是实现细节具有很大不同。 104 | 105 | 对于 Unix,命名管道为 FIFO。一旦创建,它们表现为文件系统的典型文件。通过系统调用 mkfifo(),可以创建 FIFO;通过系统调用 open()、read()、write() 和 close(),可以操作 FIFO。FIFO 会一直存在,直到它被显式的从文件系统中删除。虽然 FIFO 允许双向通信,只允许半双工传输。如果数据要在两个方向上传输,那么通常使用两个 FIFO。此外,通信进程应位于同一台机器上,如果需要两个不同系统之间的通信,那么应该使用套接字。 106 | 107 | 与 UNIX 系统相比,Windows 系统的命名管道通信机制更加丰富。允许全双工通信,并且通信进程可以位于同一机器或不同机器。此外,UNIX 的 FIFO 只支持字节流的数据,而 Windows 系统允许字节流或消息流的数据。 108 | 109 | 110 | ## 结语 111 | 112 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /design-patterns/单例模式.md: -------------------------------------------------------------------------------- 1 | # 单例模式 2 | 3 | ## 单例模式的特点: 4 | 5 | 1. 单例类只能有一个实例; 6 | 2. 单例类必须自己创建自己的唯一实例; 7 | 3. 单例类必须给所有其他对象提供这一实例。 8 | 9 | 10 | ## 懒汉类 11 | 12 | ```java 13 | // 懒汉式单例类,在第一次调用的时候实例化自己 14 | public class Singleton { 15 | private Singleton() { 16 | } 17 | 18 | private volatile static Singleton singleton = null; // 声明成 volatile 19 | 20 | // 静态工厂方法 21 | public static Singleton getInstance() { 22 | if (singleton == null) { 23 | synchronized (Singleton.class) { 24 | if (singleton == null) { 25 | singleton = new Singleton(); 26 | } 27 | } 28 | } 29 | return singleton; 30 | } 31 | } 32 | ``` 33 | 34 | 35 | ## 饿汉类 36 | 37 | ``` java 38 | // 饿汉式单例类,在类初始化时,已经自行实例化 39 | public class Singleton { 40 | private Singleton() { 41 | } 42 | 43 | private static final Singleton single = new Singleton(); 44 | 45 | // 静态工厂方法 46 | public static Singleton getInstance() { 47 | return single; 48 | } 49 | } 50 | ``` 51 | 52 | 但当单例类当实现了 `Serializable` 接口后,反序列化时单例会被破坏,此时需要重写 `readResolve`,才能保证其反序列化依旧是单例,如下所示: 53 | 54 | ``` java 55 | private Object readResolve() throws ObjectStreamException { 56 | return single; 57 | } 58 | ``` 59 | 60 | 61 | ## 内部静态类 62 | 63 | ```java 64 | public class Singleton { 65 | private static class LazyHolder { 66 | private static final Singleton INSTANCE = new Singleton(); 67 | } 68 | 69 | private Singleton() { 70 | } 71 | 72 | public static Singleton getInstance() { 73 | return LazyHolder.INSTANCE; 74 | } 75 | } 76 | ``` 77 | 78 | 79 | ## 枚举类 80 | 81 | ``` java 82 | public enum Singleton { 83 | INSTANCE; 84 | 85 | public void whateverMethod() { 86 | } 87 | } 88 | ``` 89 | 90 | 枚举类是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒,不过在实际工作中,我很少看见有人这么写过。 91 | 92 | 93 | ## 结论 94 | 95 | 工作中我基本都是用内部静态类,具有懒加载和线程安全的效果,如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例,如果每次书写嫌麻烦的话,可以在 Android Studio 中设置 `Preferences -> Editor -> Live Templates` 来快速创建出自己需要的单例。 96 | 97 | 98 | ## 结语 99 | 100 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /design-patterns/模板方法模式.md: -------------------------------------------------------------------------------- 1 | ### 背景 2 | 最近在看《设计模式之禅》,为了能够更加深入的理解设计模式,达到学以致用。 3 | 这边记录一下自己的一些感受和看法,并结合具体代码实战来进行说明。 4 | 5 | ### 模板方法模式 6 | 7 | 但凡和设计模式挂上钩,我们总是会觉得「高不可攀」。 8 | 然而实际上,设计模式是基于大量实际代码的经验总结,它来自于实际的代码。 9 | 与其说「高不可攀」,其实它反而是比较「接地气」。 10 | 而模板方法模式相信你看完本篇文章之后,会发现,原来这就是模板方法模式,然后就去看你之前的代码了。 11 | 12 | ### 小例子初识模板方法模式 13 | 14 | 理解设计模式最好的方法就是通过项目开发中的实际场景来说明。 15 | 16 | 大家做 Android 开发的时候写 Activity 应该都会看到下面类似代码吧? 17 | 18 | ```java 19 | private void getIntents() { 20 | // 从 Intent 获取传递过来的一些参数,设置到属性中 21 | } 22 | 23 | private void findViewById() { 24 | // 通过 findViewById 来初始化各个组件 25 | } 26 | 27 | private void setViews() { 28 | // 给组件设置监听或者初始状态 29 | } 30 | 31 | ``` 32 | 33 | 假设我每个界面都这样写,那么重复代码太多了,很没必要。 34 | 虽然每个方法具体的逻辑不一样,但是都有这些操作。 35 | 36 | 这个时候我们第一个想法就是**继承**,抽取出一个 BaseActivity。 37 | 然后将这些通用代码都放到了 BaseActivity 里面,子类再来覆写就可以了。 38 | 39 | 但是还有一个问题,那就是,我每次都需要写下面代码: 40 | 41 | ```java 42 | getIntents(); 43 | findViewById(); 44 | setViews(); 45 | ``` 46 | 47 | 尤其是通用代码多的时候,有时候手误可能导致某些界面这三个方法调用顺序还不一样。 48 | 那怎么办呢?我们可以抽取出一个方法,这个方法代表了这三个方法统一的调用顺序,这样就不怕手误写错了。 49 | 而这个方法就是我们的**模板方法**。 50 | 51 | ```java 52 | public abstract class BaseActivity extends Activity { 53 | /** 54 | * 从 Intent 获取传递过来的一些参数,设置到属性中 55 | */ 56 | protected abstract void getIntents(); 57 | 58 | /** 59 | * 通过 findViewById 来初始化各个组件 60 | */ 61 | protected abstract void findViewById(); 62 | 63 | /** 64 | * 给组件设置监听或者初始状态 65 | */ 66 | protected abstract void setViews(); 67 | 68 | /** 69 | * 模板方法 70 | */ 71 | final public void init() { 72 | getIntents(); 73 | findViewById(); 74 | setViews(); 75 | } 76 | } 77 | ``` 78 | 79 | 这样我后面的 Activity 都可以继承这个 BaseActivity,然后只需要调用 init 方法即可。 80 | 至于不同的 Activity 的逻辑我再在三个方法里面各自实现即可。 81 | 82 | ### 钩子方法 83 | 84 | 一听到这个词,是不是觉得有点「高大上」,似乎很 NB? 85 | 然而,听完我后面的讲述,你内心估计 86 | ![](https://user-gold-cdn.xitu.io/2019/8/29/16cdd2f75dd6ac5d?w=255&h=255&f=png&s=41949) 87 | 88 | 我们上面的方法里面,并不是所有的 Activity 都有其他 Activity 传递数据过来的,因此 getIntents 这个方法不一定所有子类都要调用。 89 | 这个时候我们可以提供一个钩子方法,改动部分代码如下: 90 | 91 | ```java 92 | /** 93 | * 模板方法 94 | */ 95 | final public void init() { 96 | if (isGetIntents()) { 97 | getIntents(); 98 | } 99 | findViewById(); 100 | setViews(); 101 | } 102 | 103 | /** 104 | * 钩子方法,是否需要设置数据,默认为 true 105 | * @return 106 | */ 107 | protected boolean isGetIntents() { 108 | return true; 109 | } 110 | ``` 111 | 112 | 可以看到,通过钩子方法,当我们默认需要获取数据时,什么都不用改动,如果我们不需要获取数据,只需要覆写我们的钩子方法 isGetIntents 并返回 false 即可。 113 | 114 | 好了,有了初步的印象之后,接下来就正式的加深了解吧。 115 | 116 | ![](https://user-gold-cdn.xitu.io/2019/5/22/16add3eca3d46bbf?w=640&h=480&f=jpeg&s=26634) 117 | 118 | ### 定义 119 | 120 | >Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. 121 | >定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 122 | 123 | 简单的说就是父类定义了一个模板方法,在这个模板方法里面有一些特定的步骤。具体的步骤实现留给子类去处理。 124 | 125 | 父类的模板方法保持了各个子类的共性,模板方法里面的步骤使得每个子类都有自己的个性。 126 | 127 | ### 通用代码实现 128 | 129 | 父类: 130 | ```java 131 | public abstract class AbstractPatternClass { 132 | /** 133 | * 基本方法,模板方法里面调用 134 | */ 135 | protected abstract void firstModule(); 136 | /** 137 | * 基本方法,模板方法里面调用 138 | */ 139 | protected abstract void secondModule(); 140 | 141 | /** 142 | * 模板方法,多个基本方法组合 143 | */ 144 | final public void templateMethod() { 145 | firstModule(); 146 | secondModule(); 147 | } 148 | } 149 | ``` 150 | 151 | 具体子类: 152 | ```java 153 | public class ConcreteClass extends AbstractPatternClass { 154 | @Override 155 | protected void firstModule() { 156 | // TODO 子类实现自己的逻辑 157 | } 158 | 159 | @Override 160 | protected void secondModule() { 161 | // TODO 子类实现自己的逻辑 162 | } 163 | } 164 | ``` 165 | 166 | 场景使用类: 167 | ```java 168 | public class PatternTest { 169 | public static void main(String[] args) { 170 | AbstractPatternClass abstractPatternClass = new ConcreteClass(); 171 | // 调用模板方法 172 | abstractPatternClass.templateMethod(); 173 | } 174 | } 175 | ``` 176 | 177 | 钩子方法我们上面已经说过了,相信聪明的你知道如何使用,这里就不再赘述了。 178 | 179 | ### 注意点 180 | 181 | 父类中的基本方法尽量设计为 protected 类型,符合迪米特法则。 182 | 父类中的模板方法一般设置为 final,不允许子类覆写。这样的目的一个是为了避免子类恶意操作,一个是为了模板的共性。 183 | 184 | 185 | ### 应用 186 | 187 | 当你在写代码经常用到**复制和粘贴快捷键**时,你就要考虑是不是可以进行抽取。 188 | 当你修改一个地方的时候,发现其他地方也要连带修改,也需要考虑一下。 189 | 多个子类有公共方法,并且逻辑基本相同。 190 | 复杂的一些算法之类的,可以让子类通过基本方法传递一些参数,核心逻辑放在模板方法里面。 191 | 重构项目的时候,也可以考虑一下把相同代码抽取到父类,通过钩子方法定制化模板。 192 | 193 | 194 | ### 结语 195 | 196 | >最后一点就是注意不要滥用设计模式,不要为了设计而设计 197 | 198 | 199 | **参考资料**: 200 | 设计模式之禅(第2版) 201 | 202 | 203 | ### 最后 204 | 205 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 206 | 207 | -------------------------------------------------------------------------------- /design-patterns/责任链模式.md: -------------------------------------------------------------------------------- 1 | # 责任链模式 2 | 3 | ## 定义 4 | 5 | **百度百科的介绍**:责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。 6 | 7 | **维基百科的介绍**:责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。 8 | 9 | **我的介绍**:顾名思义,责任链模式是一条链,链上有多个节点,每个节点都有各自的责任。当有输入时,第一个责任节点看自己能否处理该输入,如果可以就处理。如果不能就交由下一个责任节点处理。依次类推,直到最后一个责任节点。 10 | 11 | 定义总是有点文绉绉,还是看下下面的例子加深下理解吧。 12 | 13 | ## 例子 14 | 15 | 1. 需求开发例子 16 | 17 | 假设现在有个需求来了,首先是实习生拿到这个需求。 18 | 如果实习生能够实现,直接实现。如果不行,他把这个需求交给初级工程师。 19 | 如果初级工程师能够实现,直接实现。如果不行,交给中级工程师。 20 | 如果中级工程师能够实现,直接实现。如果不行,交给高级工程师。 21 | 如果高级工程师能够实现,直接实现。如果不行,交给 CTO。 22 | 如果 CTO能够实现,直接实现。如果不行,直接跟产品说,需求不做。 23 | 24 | 2. 买球篮例子 25 | 26 | 假设你现在有个篮球,然后想要买个球篮。 27 | 你肯定是到店里,让老板把所有尺寸的球篮拿出来。 28 | 然后你一个一个试。 29 | 第一个不行,就第二个。 30 | 第二个不行,就第三个。 31 | ... 32 | 直到找到合适的。 33 | 34 | 35 | ## 传统代码 36 | 37 | 给定一个输入值,根据输入值执行不同逻辑。 38 | 39 | ```java 40 | String input = "1"; 41 | if ("1".equals(input)) { 42 | //TODO do something 43 | } else if ("2".equals(input)) { 44 | //TODO do something 45 | } else if ("3".equals(input)) { 46 | //TODO do something 47 | } 48 | ``` 49 | 50 | 或者如下代码: 51 | 52 | ```java 53 | String input = "1"; 54 | switch (input) { 55 | case "1": 56 | //TODO do something 57 | break; 58 | case "2": 59 | //TODO do something 60 | break; 61 | case "3": 62 | //TODO do something 63 | break; 64 | default: 65 | //TODO do something 66 | break; 67 | } 68 | ``` 69 | 70 | 如果每个分支里面的逻辑比较简单,那还好,如果逻辑复杂,**假设每个 case 大概要 100 行代码处理,有 10 个 case,一下子就出来一个「千行代码」文件,而且还不利于维护、测试和扩展。** 71 | 72 | 下面来介绍如何通过责任链模式的妙用来拆分代码。 73 | 74 | 75 | ## 责任链模式代码 76 | 77 | 这里以上面场景为例子进行拆分代码说明。 78 | 79 | 1. 定义一个抽象类。 80 | 81 | ```java 82 | public abstract class BaseCase { 83 | // 为 true 表明自己可以处理该 case 84 | private boolean isConsume; 85 | 86 | public BaseCase(boolean isConsume) { 87 | this.isConsume = isConsume; 88 | } 89 | 90 | // 下一个责任节点 91 | private BaseCase nextCase; 92 | 93 | public void setNextCase(BaseCase nextCase) { 94 | this.nextCase = nextCase; 95 | } 96 | 97 | public void handleRequest() { 98 | if (isConsume) { 99 | // 如果当前节点可以处理,直接处理 100 | doSomething(); 101 | } else { 102 | // 如果当前节点不能处理,并且有下个节点,交由下个节点处理 103 | if (null != nextCase) { 104 | nextCase.handleRequest(); 105 | } 106 | } 107 | } 108 | 109 | abstract protected void doSomething(); 110 | } 111 | ``` 112 | 113 | 2. 定义多个 case 来实现该抽象类,如下所示: 114 | 115 | ```java 116 | public class OneCase extends BaseCase { 117 | public OneCase(boolean isConsume) { 118 | super(isConsume); 119 | } 120 | 121 | @Override 122 | protected void doSomething() { 123 | // TODO do something 124 | System.out.println(getClass().getName()); 125 | } 126 | } 127 | ``` 128 | 129 | 3. 初始化各个 case,并指定每个 case 的下一个节点。 130 | 131 | ```java 132 | String input = "1"; 133 | OneCase oneCase = new OneCase("1".equals(input)); 134 | TwoCase twoCase = new TwoCase("2".equals(input)); 135 | DefaultCase defaultCase = new DefaultCase(true); 136 | oneCase.setNextCase(twoCase); 137 | twoCase.setNextCase(defaultCase); 138 | oneCase.handleRequest(); 139 | ``` 140 | 141 | 142 | ## 优化 143 | 144 | 参考 OkHttp 里面的 Interceptor 实现,将所有的 case 集中起来,通过遍历确定能够处理的 case。 145 | 146 | 1. 定义一个接口。 147 | 148 | ```java 149 | interface BaseCase { 150 | // 所有 case 处理逻辑的方法 151 | void doSomething(String input, BaseCase baseCase); 152 | } 153 | ``` 154 | 155 | 2. 建立一个责任链管理类,管理所有 case。 156 | 157 | ```java 158 | public class CaseChain implements BaseCase { 159 | // 所有 case 列表 160 | private List mCaseList = new ArrayList<>(); 161 | // 索引,用于遍历所有 case 列表 162 | private int index = 0; 163 | 164 | // 添加 case 165 | public CaseChain addBaseCase(BaseCase baseCase) { 166 | mCaseList.add(baseCase); 167 | return this; 168 | } 169 | 170 | @Override 171 | public void doSomething(String input, BaseCase baseCase) { 172 | // 所有遍历完了,直接返回 173 | if (index == mCaseList.size()) return; 174 | // 获取当前 case 175 | BaseCase currentCase = mCaseList.get(index); 176 | // 修改索引值,以便下次回调获取下个节点,达到遍历效果 177 | index++; 178 | // 调用当前 case 处理方法 179 | currentCase.doSomething(input, this); 180 | } 181 | } 182 | ``` 183 | 184 | 3. 各个 case 实现接口。这里以其中一个为例。 185 | 186 | ```java 187 | public class OneCase implements BaseCase { 188 | @Override 189 | public void doSomething(String input, BaseCase baseCase) { 190 | if ("1".equals(input)) { 191 | // TODO do something 192 | System.out.println(getClass().getName()); 193 | return; 194 | } 195 | // 当前没法处理,回调回去,让下一个去处理 196 | baseCase.doSomething(input, baseCase); 197 | } 198 | } 199 | ``` 200 | 201 | 4. 初始化各个 case 202 | 203 | ```java 204 | String input = "1"; 205 | CaseChain caseChain = new CaseChain();  206 | caseChain.addBaseCase(new OneCase()) 207 | .addBaseCase(new TwoCase()) 208 | .addBaseCase(new DefaultCase());  209 | caseChain.doSomething(input, caseChain); 210 | ``` 211 | 212 | ### 总结 213 | 214 | 其实见到该模式就应该能联想到数据结构的链表,所谓的处理就是遍历一遍该列表筛选出符合的就让它去处理即可。 215 | 216 | 217 | ## 结语 218 | 219 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /hr/你的缺点是什么.md: -------------------------------------------------------------------------------- 1 | # 你的缺点是什么 2 | 3 | ## 切勿变相夸自己 4 | 5 | “我妈妈说,我最大的缺点,就是太执着,不撞南墙不回头……” 6 | 7 | “我觉得我最大的缺点就是善良,比较容易心软……” 8 | 9 | “我最大的缺点是太拼了,不懂得平衡工作和生活,之前熬夜加班,身体吃不消就病倒了……” 10 | 11 | 这种说法就是在质疑 HR 的智商,你认为 HR 会让你觉得用这种答案心悦诚服地接受了? 12 | 13 | 14 | ## 切勿坦白 15 | 16 | “我最大的缺点是智商有余情商不足,不太会察言观色” 17 | 18 | “我比较缺乏耐心和意志力,长久地坚持对我来说不是易事,当然我在改了” 19 | 20 | “上一段工作很忙碌压力很大,我不太能适应,在这种高压环境的适应力上,我还要加强” 21 | 22 | 这种实诚得让人心疼的回答,是在给自己挖坑。 23 | 24 | 25 | ## 参考回答 26 | 27 | 尽量避开三观,性格方面的缺点,最好还是着眼于知识和技能,因为这两点改进空间大、速度快。我们可以 **「往高处说,往远处说」**。 28 | 29 | ### 往高处说 30 | 31 | 往高处说:能力层次有高有低,请你挑一个与你目前所在层次相隔较远的能力缺陷来说。 32 | 33 | 就拿我们 Android 开发来说,你可能应聘的是 「高级 Android 开发工程师」,那么我们想想这个岗位之上的职位有什么?那就是你的 leader 或者 CTO 吧,我们可以说说他们身上的一些特质,比如管理能力和思考战略布局能力等,以下例句可作为参考。 34 | 35 | “我的执行力不错,通常领导交代的任务我都能完成。但是正因为如此,我主动地深入思考就相对少了一些,更多是在行动上。如何站到更高的格局上去看待工作,如何思考战略布局,这类深度的专研我还需要加强。” 36 | 37 | 38 | ### 往远处说 39 | 40 | 往远处说:术业有专攻,找一个与你本职工作间隔较远的专业能力缺陷来说。 41 | 42 | 还是拿我们 Android 开发来说,我们可以说自己在服务端开发方面能力欠缺,以下例句可作为参考。 43 | 44 | “这么多年,我都是在 Android 开发领域内纵深发展,对服务端开发了解和学习还不够,我自己也意识到了这一点,最近也在看这方面的学习资料,其实也就是想弥补自己这个不足,对我之后从事 Android 开发也是有好处的。” 45 | 46 | 47 | 参考知乎:[面试官问你的缺点是什么,应该如何回答?](https://www.zhihu.com/question/22357547) 48 | 49 | ## 结语 50 | 51 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /java/线程池.md: -------------------------------------------------------------------------------- 1 | # 线程池 2 | 3 | ## 优点 4 | 5 | 1. 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗; 6 | 2. 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行; 7 | 3. 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 8 | 9 | 10 | ## 介绍 11 | 12 | ### ThreadPoolExecutor 13 | 14 | Java 为我们提供了 ThreadPoolExecutor 来创建一个线程池,其完整构造函数如下所示: 15 | 16 | ```java 17 | public ThreadPoolExecutor(int corePoolSize, 18 | int maximumPoolSize, 19 | long keepAliveTime, 20 | TimeUnit unit, 21 | BlockingQueue workQueue, 22 | ThreadFactory threadFactory, 23 | RejectedExecutionHandler handler) 24 | ``` 25 | 26 | * int corePoolSize(核心线程数):线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程;核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态);如果设置了 allowCoreThreadTimeOut 为 true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉。 27 | 28 | * int maximumPoolSize(线程池能容纳的最大线程数量):线程总数 = 核心线程数 + 非核心线程数。 29 | 30 | * long keepAliveTime(非核心线程空闲存活时长):非核心线程空闲时长超过该时长将会被回收,主要应用在缓存线程池中,当设置了 allowCoreThreadTimeOut 为 true 时,对核心线程同样起作用。 31 | 32 | * TimeUnit unit(keepAliveTime 的单位):它是一个枚举类型,常用的如:TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)。 33 | 34 | * BlockingQueue workQueue(任务队列):当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务,常用的 workQueue 类型: 35 | 1. SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现 *线程数达到了 maximumPoolSize 而不能新建线程* 的错误,使用这个类型队列的时候,maximumPoolSize 一般指定成 Integer.MAX_VALUE,即无限大。 36 | 37 | 2. LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了 maximumPoolSize 的设定失效,因为总线程数永远不会超过 corePoolSize。 38 | 39 | 3. ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到 corePoolSize 的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了 maximumPoolSize,并且队列也满了,则发生错误。 40 | 41 | 4. DelayQueue:队列内元素必须实现 Delayed 接口,这就意味着你传进去的任务必须先实现 Delayed 接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。 42 | 43 | * ThreadFactory threadFactory(线程工厂):用来创建线程池中的线程,通常用默认的即可。 44 | 45 | * RejectedExecutionHandler handler(拒绝策略):在线程池已经关闭的情况下和任务太多导致最大线程数和任务队列已经饱和,无法再接收新的任务,在上面两种情况下,只要满足其中一种时,在使用 execute() 来提交新的任务时将会拒绝,线程池提供了以下 4 种策略: 46 | 1. AbortPolicy:默认策略,在拒绝任务时,会抛出RejectedExecutionException。 47 | 48 | 2. CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前的被丢弃的任务。 49 | 50 | 3. DiscardOldestPolicy:该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。 51 | 52 | 4. DiscardPolicy:该策略默默的丢弃无法处理的任务,不予任何处理。 53 | 54 | 55 | ### 线程池执行策略 56 | 57 | 当一个任务要被添加进线程池时,有以下四种执行策略: 58 | 59 | 1. 线程数量未达到 corePoolSize,则新建一个线程(核心线程)执行任务。 60 | 2. 线程数量达到了 corePoolsSize,则将任务移入队列等待。 61 | 3. 队列已满,新建非核心线程执行任务。 62 | 4. 队列已满,总线程数又达到了 maximumPoolSize,就会由 RejectedExecutionHandler 抛出异常。 63 | 64 | 其流程图如下所示: 65 | 66 | ![](http://ww1.sinaimg.cn/large/b75b8776gy1fvshz2bh4qj20rn0ehwf4.jpg) 67 | 68 | 69 | ### 常见的四类线程池 70 | 71 | 常见的四类线程池分别有 FixedThreadPool、SingleThreadExecutor、ScheduledThreadPool 和 CachedThreadPool,它们其实都是通过 ThreadPoolExecutor 创建的,其参数如下表所示: 72 | 73 | |参数|FixedThreadPool|SingleThreadExecutor|ScheduledThreadPool|CachedThreadPool| 74 | |---|---|---|---|---| 75 | |corePoolSize|nThreads|1|corePoolSize|0| 76 | |maximumPoolSize|nThreads|1|Integer.MAX_VALUE|Integer.MAX_VALUE| 77 | |keepAliveTime|0|0|10|60| 78 | |unit|MILLISECONDS|MILLISECONDS|MILLISECONDS|SECONDS| 79 | |workQueue|LinkedBlockingQueue|LinkedBlockingQueue|DelayedWorkQueue|SynchronousQueue| 80 | |threadFactory|defaultThreadFactory|defaultThreadFactory|defaultThreadFactory|defaultThreadFactory| 81 | |handler|defaultHandler|defaultHandler|defaultHandler|defaultHandler| 82 | |适用场景|已知并发压力的情况下,对线程数做限制|需要保证顺序执行的场景,并且只有一个线程在执行|需要多个后台线程执行周期任务的场景|处理执行时间比较短的任务| 83 | 84 | 如果你不想自己写一个线程池,那么你可以从上面看看有没有符合你要求的(一般都够用了),如果有,那么很好你直接用就行了,如果没有,那你就老老实实自己去写一个吧。 85 | 86 | 87 | ### 合理地配置线程池 88 | 89 | 需要针对具体情况而具体处理,不同的任务类别应采用不同规模的线程池,任务类别可划分为 CPU 密集型任务、IO 密集型任务和混合型任务。 90 | 91 | * CPU 密集型任务:线程池中线程个数应尽量少,推荐配置为 (CPU 核心数 + 1); 92 | 93 | * IO 密集型任务:由于 IO 操作速度远低于 CPU 速度,那么在运行这类任务时,CPU 绝大多数时间处于空闲状态,那么线程池可以配置尽量多些的线程,以提高 CPU 利用率,推荐配置为 (2 * CPU 核心数 + 1); 94 | 95 | * 混合型任务:可以拆分为 CPU 密集型任务和 IO 密集型任务,当这两类任务执行时间相差无几时,通过拆分再执行的吞吐率高于串行执行的吞吐率,但若这两类任务执行时间有数据级的差距,那么没有拆分的意义。 96 | 97 | 98 | ## 结语 99 | 100 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 -------------------------------------------------------------------------------- /jvm/运行时数据区域.md: -------------------------------------------------------------------------------- 1 | # 运行时数据区域 2 | 3 | JVM 在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。根据《Java 虚拟机规范(Java SE 7 版)》的规定,JVM 所管理的内存将会包括以下几个运行时数据区域: 4 | 5 | ![JVM 运行时数据区](http://ww1.sinaimg.cn/large/b75b8776gy1fu5kmimj6gj20dy0af3z6.jpg) 6 | 7 | 8 | ## 程序计数器 9 | 10 | 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。由于 JVM 的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。 11 | 12 | 如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在 JVM 规范中没有规定任何 OutOfMemoryError 情况的区域。 13 | 14 | 15 | ## Java 虚拟机栈 16 | 17 | Java 虚拟机栈也是线程私有的,它描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。它的内存模型如下所示: 18 | 19 | ![Java 虚拟机栈](http://ww1.sinaimg.cn/large/b75b8776gy1fu5li7jy21j208r073q37.jpg) 20 | 21 | 局部变量表存放了编译器克制的各种基本数据类型(boolean、byte、char、shot、int、float、long、double)、对象引用(reference 类型,它不是对象本身)和 returnAddress 类型(指向一条字节码指令的地址) 22 | 23 | 在 JVM 规范中,对该区域规定了这两种异常情况: 24 | 25 | 1. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常; 26 | 2. 如果虚拟机栈可以动态拓展,如果拓展时无法申请到足够的内存,就会抛出 OutOfMemoryError 异常。 27 | 28 | 29 | ## 本地方法栈 30 | 31 | 本地方法栈的作用与虚拟机栈作用是非常类似的,只不过虚拟机栈为执行 Java 方法(也就是字节码)服务,而本地方法栈则为 Native 方法服务。 32 | 33 | 34 | ## Java 堆 35 | 36 | 对大多数应用来说,Java 堆是 JVM 所管理的内存中最大的一块。它在虚拟机启动时创建,被所有线程所共享,它的唯一目的就是存放对象实例。 37 | 38 | Java 堆是垃圾收集器管理的主要区域,也被称作“GC”堆。 39 | 40 | * 从内存回收的角度来看,现代收集器基本都采用分代收集算法,所以 Java 还可以细分为:新生代和老年代;再细致一点的有 Eden 空间、From Survivor 空间、To Survivor 空间等。 41 | * 从内存分配的角度来看,线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区,但存储的仍然是对象实例,进一步划分的目的是为了更好地回首内存,或者更快地分配内存。 42 | 43 | Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可拓展的。如果在堆中没有内存完成实例分配,并且堆也无法再拓展时,将会抛出 OutOfMemoryError 异常。 44 | 45 | 46 | ## 方法区 47 | 48 | 方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 49 | 50 | 方法区也被称作“永久代”,但两者并不等价,仅仅是因为方法区是靠使用永久代来实现。 51 | 52 | *在 JDK 1.7 中,已经把原本放在永久代的字符串常量池移出; 53 | *在 JDK 1.8 中,移除了整个永久代,取而代之的是一个叫元空间(Metaspace)的区域。 54 | 55 | 方法区除了和 Java 堆一样不需要连续的内存空间和可以选择固定大小或者可拓展外,还可以选择不实现垃圾收集,但不实现垃圾回收可能会引发内存泄漏。 56 | 57 | 当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。 58 | 59 | 60 | ## 运行时常量池 61 | 62 | 它是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。 63 | 64 | 运行时常量池相对于 Class 文件常量池的另外一个重要特征是具备动态性,Java 语言并不要求常量一定只有编译期才能产生,也就是并非预置入 Class 文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得较多的便是 String 类得 intern() 方法。 65 | 66 | 既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时便会抛出 OutOfMemoryError 异常。 67 | 68 | 69 | ## 直接内存 70 | 71 | 它并不是 JVM 运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。但是它也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现,所以放到这里一起讲解。 72 | 73 | 直接内存的分配不会收到 Java 堆大小的限制,它受本机总内存(包括 RAM 以及 SWAP 区或者分页文件)大小以及处理器寻址空间得限制。服务器管理员在配制虚拟机参数时,会根据实际内存设置 -Xmx 等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制从而导致动态拓展时出现 OutOfMemoryError 异常。 74 | 75 | 76 | ## 结语 77 | 78 | 我正在打造一个帮助 Android 开发者们拿到更好 offer 的面试库————**[安卓 offer 收割基](https://github.com/Blankj/AndroidOfferKiller)**,欢迎 star,觉得不错的可以持续关注,有兴趣的可以一起加入进来和我一同打造。 --------------------------------------------------------------------------------