(n.nodes.size());
27 | for (Node subNode : n.nodes) {
28 | n.nodes.add(deepClone(subNode));
29 | }
30 | return n;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/linkedlist/ListNode.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.linkedlist;
2 |
3 | /**
4 | * Created by wcy on 2021/1/20.
5 | */
6 | public class ListNode {
7 | int val;
8 | ListNode next;
9 |
10 | ListNode() {
11 | }
12 |
13 | ListNode(int val) {
14 | this.val = val;
15 | }
16 |
17 | ListNode(int val, ListNode next) {
18 | this.val = val;
19 | this.next = next;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/linkedlist/Odd_Even_Linked_List.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.linkedlist;
2 |
3 | /**
4 | * https://leetcode.com/explore/interview/card/top-interview-questions-easy/93/linked-list/560/
5 | * Created by wcy on 2021/1/20.
6 | */
7 | class Odd_Even_Linked_List {
8 | public ListNode oddEvenList(ListNode head) {
9 | if (head == null || head.next == null) {
10 | return head;
11 | }
12 | ListNode odd = head;
13 | ListNode even = head.next;
14 | ListNode evenHead = head.next;
15 | while (even != null && even.next != null) {
16 | odd.next = even.next;
17 | odd = odd.next;
18 | even.next = odd.next;
19 | even = even.next;
20 | }
21 | odd.next = evenHead;
22 | return head;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/linkedlist/Reverse_Linked_List.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.linkedlist;
2 |
3 | /**
4 | * https://leetcode.com/explore/interview/card/top-interview-questions-medium/107/linked-list/784/
5 | * Created by wcy on 2021/1/20.
6 | */
7 | class Reverse_Linked_List {
8 | public ListNode reverseList(ListNode head) {
9 | if (head == null || head.next == null) {
10 | return head;
11 | }
12 | ListNode curr = head;
13 | ListNode prev = null;
14 | while (curr != null) {
15 | ListNode next = curr.next;
16 | curr.next = prev;
17 | prev = curr;
18 | curr = next;
19 | }
20 | return prev;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/sort/QuickSort.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.sort;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * Created by wcy on 2018/1/5.
7 | */
8 | public class QuickSort {
9 |
10 | public static void main(String[] args) {
11 | int[] array = {9, 3, 7, 12, 4, 0, 11, 6};
12 | quickSort(array, 0, array.length - 1);
13 | System.out.println(Arrays.toString(array));
14 | }
15 |
16 | private static void quickSort(int[] array, int start, int end) {
17 | if (start >= end) {
18 | return;
19 | }
20 |
21 | int left = start;
22 | int right = end;
23 | int key = array[left];
24 |
25 | while (left < right) {
26 | while (left < right && array[right] >= key) {
27 | right--;
28 | }
29 | if (left < right) {
30 | array[left] = array[right];
31 | left++;
32 | }
33 |
34 | while (left < right && array[left] <= key) {
35 | left++;
36 | }
37 | if (left < right) {
38 | array[right] = array[left];
39 | right--;
40 | }
41 | }
42 |
43 | array[left] = key;
44 |
45 | quickSort(array, start, left - 1);
46 | quickSort(array, right + 1, end);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/string/Increasing_Triplet_Subsequence.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.string;
2 |
3 | /**
4 | * https://leetcode.com/explore/interview/card/top-interview-questions-medium/103/array-and-strings/781/
5 | * Created by wcy on 2020/10/28.
6 | */
7 | public class Increasing_Triplet_Subsequence {
8 |
9 | public boolean increasingTriplet(int[] nums) {
10 | int m1 = Integer.MAX_VALUE;
11 | int m2 = Integer.MAX_VALUE;
12 | for (int n : nums) {
13 | if (n <= m1) {
14 | m1 = n;
15 | } else if (n <= m2) {
16 | m2 = n;
17 | } else {
18 | return true;
19 | }
20 | }
21 | return false;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/string/Longest_Palindromic_Substring.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.string;
2 |
3 | /**
4 | * https://leetcode.com/explore/interview/card/top-interview-questions-medium/103/array-and-strings/780/
5 | * Created by wcy on 2020/10/28.
6 | */
7 | public class Longest_Palindromic_Substring {
8 |
9 | public String longestPalindrome(String s) {
10 | if (s == null || s.length() == 0) return "";
11 | int left = 0;
12 | int right = 0;
13 | for (int i = 0; i < s.length(); i++) {
14 | int len1 = maxRange(s, i, i);
15 | int len2 = maxRange(s, i, i + 1);
16 | if (len1 > right - left + 1) {
17 | left = i - len1 / 2;
18 | right = i + len1 / 2;
19 | }
20 | if (len2 > right - left + 1) {
21 | left = i - (len2 / 2 - 1);
22 | right = i + len2 / 2;
23 | }
24 | }
25 | return s.substring(left, right + 1);
26 | }
27 |
28 | public int maxRange(String s, int left, int right) {
29 | while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
30 | left--;
31 | right++;
32 | }
33 | // 最后一次无效
34 | left++;
35 | right--;
36 | return right - left + 1;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/string/Longest_Substring_Without_Repeating_Characters.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.string;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | /**
7 | * https://leetcode.com/explore/interview/card/top-interview-questions-medium/103/array-and-strings/779/
8 | *
9 | * 基本思路是维护一个窗口,每次关注窗口中的字符串,在每次判断中,左窗口和右窗口选择其一向前移动。
10 | * 同样是维护一个HashSet,正常情况下移动右窗口,如果没有出现重复则继续移动右窗口,如果发现重复字符,
11 | * 则说明当前窗口中的串已经不满足要求,继续移动有窗口不可能得到更好的结果,此时移动左窗口,直到不再有重复字符为止,
12 | * 中间跳过的这些串中不会有更好的结果,因为他们不是重复就是更短。
13 | * 因为左窗口和右窗口都只向前,所以两个窗口都对每个元素访问不超过一遍,因此时间复杂度为O(2*n)=O(n),是线性算法。
14 | * 空间复杂度为HashSet的size,也是O(n)。
15 | *
16 | * Created by wcy on 2020/10/27.
17 | */
18 | public class Longest_Substring_Without_Repeating_Characters {
19 |
20 | public int lengthOfLongestSubstring(String s) {
21 | int max = Integer.MIN_VALUE;
22 | int subStart = 0;
23 | Set set = new HashSet<>();
24 | for (int i = 0; i < s.length(); i++) {
25 | // 当前字符
26 | char c = s.charAt(i);
27 | if (set.contains(c)) {
28 | max = Math.max(max, set.size());
29 | // 移除相同字符前面的字符
30 | while (s.charAt(subStart) != c) {
31 | set.remove(s.charAt(subStart));
32 | subStart++;
33 | }
34 | // 移除相同字符
35 | set.remove(s.charAt(subStart));
36 | subStart++;
37 | }
38 | set.add(c);
39 | }
40 |
41 | max = Math.max(max, set.size());
42 | return max;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/trees/Binary_Tree_Level_Order_Traversal.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.trees;
2 |
3 | import java.util.ArrayDeque;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import java.util.Queue;
7 |
8 | /**
9 | * https://leetcode.com/explore/interview/card/top-interview-questions-easy/94/trees/628/
10 | * Created by wcy on 2021/1/22.
11 | */
12 | class Binary_Tree_Level_Order_Traversal {
13 |
14 | List> levelOrder(TreeNode root) {
15 | List> res = new ArrayList<>();
16 | if (root == null) {
17 | return res;
18 | }
19 | Queue queue = new ArrayDeque<>();
20 | queue.add(root);
21 | while (queue.size() > 0) {
22 | int len = queue.size();
23 | List level = new ArrayList<>(len);
24 | while (len > 0) {
25 | TreeNode node = queue.poll();
26 | level.add(node.val);
27 | if (node.left != null) queue.add(node.left);
28 | if (node.right != null) queue.add(node.right);
29 | len--;
30 | }
31 | res.add(level);
32 | }
33 | return res;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/trees/Binary_Tree_Zigzag_Level_Order_Traversal.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.trees;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Stack;
6 |
7 | /**
8 | * Created by wcy on 2020/11/25.
9 | */
10 | class Binary_Tree_Zigzag_Level_Order_Traversal {
11 |
12 | public List> zigzagLevelOrder(TreeNode root) {
13 | List> result = new ArrayList<>();
14 | if (root == null) return result;
15 | Stack stack = new Stack<>();
16 | stack.push(root);
17 | boolean reverse = false;
18 | while (!stack.isEmpty()) {
19 | int size = stack.size();
20 | List list = new ArrayList<>(size);
21 | List cacheList = new ArrayList<>(size);
22 | while (size > 0) {
23 | TreeNode node = stack.pop();
24 | list.add(node.val);
25 | if (reverse) {
26 | if (node.right != null) cacheList.add(node.right);
27 | if (node.left != null) cacheList.add(node.left);
28 | } else {
29 | if (node.left != null) cacheList.add(node.left);
30 | if (node.right != null) cacheList.add(node.right);
31 | }
32 | size--;
33 | }
34 | stack.addAll(cacheList);
35 | reverse = !reverse;
36 | result.add(list);
37 | }
38 | return result;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/trees/Construct_Binary_Tree_from_Preorder_and_Inorder_Traversal.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.trees;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by wcy on 2020/11/25.
8 | */
9 | class Construct_Binary_Tree_from_Preorder_and_Inorder_Traversal {
10 | public TreeNode buildTree(int[] preorder, int[] inorder) {
11 | if (preorder.length == 0 || inorder.length == 0) {
12 | return null;
13 | }
14 | List preorderList = new ArrayList<>(preorder.length);
15 | List inorderList = new ArrayList<>(inorder.length);
16 | for (int i : preorder) preorderList.add(i);
17 | for (int i : inorder) inorderList.add(i);
18 | return getRoot(preorderList, inorderList);
19 | }
20 |
21 | public TreeNode getRoot(List preorder, List inorder) {
22 | TreeNode root = new TreeNode(preorder.get(0));
23 | int leftSize = inorder.indexOf(preorder.get(0));
24 | int rightSize = inorder.size() - leftSize - 1;
25 | if (leftSize > 0) {
26 | root.left = getRoot(preorder.subList(1, leftSize + 1), inorder.subList(0, leftSize));
27 | }
28 | if (rightSize > 0) {
29 | root.right = getRoot(preorder.subList(leftSize + 1, preorder.size()), inorder.subList(leftSize + 1, inorder.size()));
30 | }
31 | return root;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/trees/Kth_Smallest_Element_in_a_BST.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.trees;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * https://leetcode.com/explore/interview/card/top-interview-questions-medium/108/trees-and-graphs/790/
8 | * Created by wcy on 2021/3/3.
9 | */
10 | class Kth_Smallest_Element_in_a_BST {
11 |
12 | public int kthSmallest(TreeNode root, int k) {
13 | List list = new ArrayList<>();
14 | middleTravel(root, list);
15 | int index = k - 1;
16 | if (index < 0 || index >= list.size()) {
17 | return -1;
18 | }
19 | return list.get(index);
20 | }
21 |
22 | public void middleTravel(TreeNode node, List list) {
23 | if (node == null) return;
24 | middleTravel(node.left, list);
25 | list.add(node.val);
26 | middleTravel(node.right, list);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/trees/Populating_Next_Right_Pointers_in_Each_Node.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.trees;
2 |
3 | /**
4 | * https://leetcode.com/explore/interview/card/top-interview-questions-medium/108/trees-and-graphs/789/
5 | * Created by wcy on 2021/3/3.
6 | */
7 | class Populating_Next_Right_Pointers_in_Each_Node {
8 | class Node {
9 | public int val;
10 | public Node left;
11 | public Node right;
12 | public Node next;
13 |
14 | public Node() {
15 | }
16 |
17 | public Node(int _val) {
18 | val = _val;
19 | }
20 |
21 | public Node(int _val, Node _left, Node _right, Node _next) {
22 | val = _val;
23 | left = _left;
24 | right = _right;
25 | next = _next;
26 | }
27 | }
28 |
29 | public Node connect(Node root) {
30 | if (root == null) return null;
31 | connect(root.left, root.right);
32 | return root;
33 | }
34 |
35 | public void connect(Node left, Node right) {
36 | if (left == null || right == null) {
37 | return;
38 | }
39 | left.next = right;
40 | connect(left.left, left.right);
41 | connect(left.right, right.left);
42 | connect(right.left, right.right);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/trees/TreeNode.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.trees;
2 |
3 | /**
4 | * Created by wcy on 2020/11/25.
5 | */
6 | class TreeNode {
7 | int val;
8 | TreeNode left;
9 | TreeNode right;
10 |
11 | TreeNode() {
12 | }
13 |
14 | TreeNode(int val) {
15 | this.val = val;
16 | }
17 |
18 | TreeNode(int val, TreeNode left, TreeNode right) {
19 | this.val = val;
20 | this.left = left;
21 | this.right = right;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/doc/algorithm/algorithm.md:
--------------------------------------------------------------------------------
1 | # 数据结构与算法
2 |
3 | ## 1. 常用排序算法及时空复杂度
4 |
5 | | 排序算法 | 平均时间 | 最好时间 | 最坏时间 | 辅助存储 | 稳定性 | 备注 |
6 |
7 | | -- | -- | -- | -- | -- | -- | -- |
8 |
9 | | 选择排序 | O(n²) | O(n²) | O(n²) | O(1) | 不稳定 | n小时较好 |
10 |
11 | | 插入排序 | O(n²) | O(n) | O(n²) | O(1) | 稳定 | 大部分已有序时较好 |
12 |
13 | | 冒泡排序 | O(n²) | O(n) | O(n²) | O(1) | 稳定 | n小时较好 |
14 |
15 | | 快速排序 | O(nlogn) | O(nlogn) | O(n²) | O(logn) | 不稳定 | n大时较好 |
16 |
17 | | 归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 | n大时较好 |
18 |
19 | | 希尔排序 | O(nlogn) | O(n) | O(ns)1pixelRef());
186 |
187 | jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
188 | static_cast(reinterpret_cast(bitmap)),
189 | buffer, isMutable, ninepatch,density);
190 | hasException(env); // For the side effectof logging.
191 | return obj;
192 | }
193 | ```
194 |
195 | 从代码中可以看到bitmap对象是通过env->NewObject()创建的,到这里疑惑就解开了,bitmap对象是虚拟机创建的,
196 | JNIEnv的NewObject方法返回的是java对象,并不是native对象,所以它会分配到dalvik heap中。
197 |
198 | ## 性能优化
199 |
200 | ### 启动优化
201 |
202 | 1. SDK 异步初始化/延迟初始化/懒加载
203 | 2. 分析启动耗时,优化启动调度,优化锁 loadLibrary0 ContextImpl
204 |
205 | ### 内存优化
206 |
207 | 1. LeakCanary 内存泄漏检测
208 | 2. 关注对象和页面的生命周期,推荐使用 JetPack Lifecycle 套件
209 | 3. 使用合理的图片尺寸和编码,推荐 RGB565
210 |
211 | ### 卡顿优化
212 |
213 | 1. BlockCanary 卡顿检测
214 | 2. Systrace 工具检测,分析应用层函数、IO、锁、Binder、CPU 调度等耗时信息
215 | 3. Android Studio Profiler,实时检测 CPU、内存、网络等信息
216 |
217 | ### 网络优化
218 |
219 | 1. DNS 缓存优化
220 | 2. 开启缓存 CacheControl
221 | 3. 升级 HTTP2.0,多路复用,Header 压缩
222 | 4. 针对频繁请求可以考虑使用自定义协议 ProtoBuf
223 |
224 | ## Looper和Handler
225 |
226 | ```java
227 | public class Looper {
228 | // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
229 | private static final ThreadLocal sThreadLocal = new ThreadLocal();
230 | // Looper内的消息队列
231 | final MessageQueue mQueue;
232 | // 当前线程
233 | Thread mThread;
234 |
235 | // 每个Looper对象中有它的消息队列,和它所属的线程
236 | private Looper() {
237 | mQueue = new MessageQueue();
238 | mRun = true;
239 | mThread = Thread.currentThread();
240 | }
241 |
242 | // 我们调用该方法会在调用线程的TLS中创建Looper对象
243 | public static final void prepare() {
244 | if (sThreadLocal.get() != null) {
245 | // 试图在有Looper的线程中再次创建Looper将抛出异常
246 | throw new RuntimeException("Only one Looper may be created per thread");
247 | }
248 | sThreadLocal.set(new Looper());
249 | }
250 |
251 | public static final void loop() {
252 | Looper me = myLooper(); //得到当前线程Looper
253 | MessageQueue queue = me.mQueue; //得到当前looper的MQ
254 |
255 | // 这两行没看懂,不过不影响理解
256 | Binder.clearCallingIdentity();
257 | final long ident = Binder.clearCallingIdentity();
258 | // 开始循环
259 | while (true) {
260 | Message msg = queue.next(); // 取出message
261 | if (msg != null) {
262 | if (msg.target == null) {
263 | // message没有target为结束信号,退出循环
264 | return;
265 | }
266 | // 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
267 | msg.target.dispatchMessage(msg);
268 | // 回收message资源
269 | msg.recycle();
270 | }
271 | }
272 | }
273 |
274 | public static Looper myLooper() {
275 | return sThreadLocal.get();
276 | }
277 | }
278 |
279 | public class Handler {
280 | public void dispatchMessage(Message msg) {
281 | if (msg.callback != null) {
282 | handleCallback(msg);
283 | } else {
284 | if (mCallback != null) {
285 | if (mCallback.handleMessage(msg)) {
286 | return;
287 | }
288 | }
289 | handleMessage(msg);
290 | }
291 | }
292 | }
293 | ```
294 |
295 | [Handler的初级、中级、高级问法,你都掌握了吗?](https://juejin.im/post/6893791473121280013)
296 |
297 | ### Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
298 |
299 | 在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,
300 | 此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。
301 | 这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
302 |
303 | [Android中为什么主线程不会因为Looper.loop()里的死循环卡死?](https://www.zhihu.com/question/34652589/answer/90344494)
304 |
305 | ### Handler异步消息
306 |
307 | 1. `public Handler(boolean async)`
308 | 2. `Message.setAsynchronous(boolean async)`
309 |
310 | #### 同步屏障
311 |
312 | 1. 屏障消息和普通消息的区别在于屏障没有target,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的
313 | 2. 屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发
314 | 3. postSyncBarrier()返回一个int类型的数值,通过这个数值可以撤销屏障即removeSyncBarrier(),该方法仅限系统使用
315 | 4. 插入普通消息会唤醒消息队列,但是插入屏障不会
316 |
317 | [Android Handler之同步屏障机制](https://www.jianshu.com/p/ed318296f95f)
318 |
319 | ### AsyncTask和Handler对比
320 |
321 | 1. AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作, 并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新)
322 | ,最后反馈执行的结果给UI主线程.
323 |
324 | - 优点:1.简单,快捷 2.过程可控
325 | - 缺点:在使用多个异步操作和并进行UI变更时,就变得复杂起来.
326 |
327 | 2. Handler在异步实现时,涉及到Handler,Looper,Message,Thread四个对象,实现异步的流程是主线程启动Thread(子线程), Thread(子线程)
328 | 运行并生成Message,Looper获取 Message并传递给Handler,Handler逐个获取Looper中的Message,并进行UI变更。
329 |
330 | - 优点:1.结构清晰,功能定义明确 2.对于多个后台任务时,简单,清晰
331 | - 缺点:在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)
332 |
333 | ## Android消息推送机制
334 |
335 | 几种常见的解决方案:
336 |
337 | 1. 轮询(Pull)方式:应用程序应当阶段性的与服务器进行连接并查询是否有新的消息到达,你必须自己实现与服务器之间的通信,例如消息排队等。
338 | 而且你还要考虑轮询的频率,如果太慢可能导致某些消息的延迟,如果太快,则会大量消耗网络带宽和电池。
339 | 2. SMS(Push)方式:在Android平台上,你可以通过拦截SMS消息并且解析消息内容来了解服务器的意图,并获取其显示内容进行处理。
340 | 这个方案的好处是,可以实现完全的实时操作。但是问题是这个方案的成本相对比较高。
341 | 3. 持久连接(Push)方式:这个方案可以解决由轮询带来的性能问题,但是还是会消耗手机的电池。
342 |
343 | 百度云推送:百度云推送的实现技术简单来说就是利用Socket维持Client和Server间的一个TCP长连接,通过这种方式能大大降低由轮询方式带来的Device的耗电量和数据访问流量。
344 |
345 | ### 移动端获取网络数据优化的几个点
346 |
347 | 1. 连接复用: 节省连接建立时间,如开启keep-alive
348 | 对于Android来说默认情况下HttpURLConnection和HttpClient都开启了keep-alive。只是2.2之前HttpURLConnection存在影响连接池的Bug
349 | 2. 请求合并: 即将多个请求合并为一个进行请求,比较常见的就是网页中的CSSImage Sprites。如果某个页面内请求过多,也可以考虑做一定的请求合并
350 | 3. 减少请求数据的大小: 对于post请求,body可以做gzip压缩的,header也可以作数据压缩(不过只支持http 2.0)
351 | 4. 返回的数据的body也可以作gzip压缩,body数据体积可以缩小到原来的30%左右。
352 | (也可以考虑压缩返回的json数据的key数据的体积,尤其是针对返回数据格式变化不大的情况,支付宝聊天返回的数据用到了)
353 | 5. 根据用户的当前的网络质量来判断下载什么质量的图片(电商用的比较多)
354 |
355 | ## Serializable 和 Parcelable 的区别
356 |
357 | 1. Parcelable的效率要快于Serializable(这是最主要的区别)。
358 |
359 | - Serializable底层实现需要用到反射,而且也会产生大量的对象(这可能会触发GC);再者就是Serializable是在IO操作。
360 | - Parcelable底层实现则不需要反射,而且它是内存操作。
361 |
362 | 2. Parcelable的使用要复杂于Serializable。
363 | 3. IPC的时候用Parcelable,是因为它效率高。网络传输和保存至磁盘的时候用Serializable,是因为Parcelable不能保证当外部条件发生变化时数据的连续性。
364 |
365 | ## View
366 |
367 | 
368 |
369 | View的绘制主要涉及三个方法: onMeasure()、onLayout()和onDraw()。
370 |
371 | 1. onMeasure主要用于计算view的大小,onLayout主要用于确定view在ContentView中的位置,onDraw主要是绘制view;
372 | 2. 在执行onMeasure(),onLayout()方法时都会先通过相应的标志位或者对应的坐标点来判断是否需要执行对应的函数,
373 | 如我们经常调用的invalidate方法就只会执行onDraw方法,因为此时的视图大小和位置均未发生改变,除非调用requestLayout方法完整强制进行view
374 | 的绘制,从而执行上面三个方法。
375 |
376 | ### Android View draw 流程
377 |
378 | View中:
379 |
380 | ```
381 | public void draw(Canvas canvas) {
382 | /*
383 | 1. Draw the background: 绘制背景
384 | 2. If necessary, save the canvas' layers to prepare for fading: 如有必要,颜色渐变淡之前保存画布层(即锁定原有的画布内容)
385 | 3. Draw view's content: 绘制view的内容
386 | 4. Draw children: 绘制子view
387 | 5. If necessary, draw the fading edges and restore layers: 如有必要,绘制颜色渐变淡的边框,并恢复画布(即画布改变的内容附加到原有内容上)
388 | 6. Draw decorations (scrollbars for instance): 绘制装饰,比如滚动条
389 | */
390 | ...
391 | if (!dirtyOpaque) {
392 | drawBackground(canvas); //背景绘制
393 | }
394 | // skip step 2 & 5 if possible (common case) 通常情况跳过第2和第5步
395 | ...
396 | if (!dirtyOpaque) onDraw(canvas); //调用onDraw
397 | dispatchDraw(canvas); //绘制子view
398 | onDrawScrollBars(canvas); //绘制滚动条
399 | ...
400 | }
401 | ```
402 |
403 | ViewGroup中:
404 |
405 | ```
406 | protected void dispatchDraw(Canvas canvas) {
407 | ...
408 | drawChild(...); //绘制子view
409 | ...
410 | }
411 | ```
412 |
413 | 自定义ViewGroup,onDraw可能不会被调用,原因是需要先设置一个背景(颜色或图),因此,一般重写dispatchDraw来绘制ViewGroup。
414 |
415 | ### SurfaceView和View的区别
416 |
417 | SurfaceView在新的线程中更新画面,而View必须在UI线程中更新画面。
418 |
419 | 在UI线程中更新画面可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
420 | SurfaceView由于是在新的线程中更新画面所以不会阻塞UI线程。但这也带来了另外一个问题,就是事件同步。
421 | 比如你触屏了一下,你需要SurfaceView中thread处理,一般就需要有一个event queue的设计来保存 touch event,这会稍稍复杂一点,因为涉及到线程同步。
422 |
423 | ### include merge ViewStub 标签
424 |
425 | 简言之: include merge都是用来解决重复布局的问题,但是merge标签能够在布局重用的时候减少UI层级结构。
426 |
427 | ViewStub标签是用来给其他的view事先占据好位置,当需要的时候调用inflater()或者是 setVisible()方法显示这些View。
428 |
429 | ## Touch 事件机制
430 |
431 | 1. 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的 View(ViewGroup)开始一直往下(子View)传递。 子 View 可以通过
432 | onTouchEvent()对事件进行处理。
433 | 2. 事件由父 View(ViewGroup)传递给子View,ViewGroup 可以通过 onInterceptTouchEvent()对事件做拦截,停止其往下传递。
434 | 3. 如果事件从上往下传递过程中一直没有被停止,且最底层子 View 没有消费事件,事件会反向往上传递,这时父 View(ViewGroup)可以进行消费, 如果还是没有被消费的话,最后会到
435 | Activity 的 onTouchEvent()函数。
436 | 4. 如果 View 没有对 ACTION_DOWN 进行消费,之后的其他事件不会传递过来。
437 | 5. OnTouchListener 优先于 onTouchEvent()对事件进行消费。
438 |
439 | 上面的消费即表示相应函数返回值为 true。
440 |
441 | 
442 |
443 | 
444 |
445 | ## RecyclerView
446 |
447 | ### RecyclerView缓存机制
448 |
449 | [RecyclerView缓存机制](https://www.wanandroid.com/wenda/show/14222)
450 | [Android ListView 与 RecyclerView 对比浅析--缓存机制](https://mp.weixin.qq.com/s/-CzDkEur-iIX0lPMsIS0aA)
451 |
452 | 1. 一级缓存:mAttachedScrap 和 mChangedScrap
453 | mAttachedScrap:LayoutManager每次layout子View之前,那些已经添加到RecyclerView中的Item以及被删除的Item的临时存放地。
454 | 使用场景就是RecyclerView滚动时、还有在可见范围内删除Item后用notifyItemRemoved方法通知更新时;
455 | mChangedScrap:作用:存放可见范围内有更新的Item。使用场景:可见范围内的Item有更新,并且使用notifyItemChanged方法通知更新时;
456 | 2. 二级缓存:mCachedViews
457 | mCachedViews:作用:存放滚动过程中没有被重新使用且状态无变化的那些旧Item,即离屏缓存,默认容量2。场景:滚动,prefetch;
458 | 3. 三级缓存:ViewCacheExtension
459 | 自定义缓存,常规方式无法使用
460 | 4. 四级缓存:RecycledViewPool
461 | RecycledViewPool:作用:缓存Item的最终站,用于保存那些Removed、Changed、以及mCachedViews满了之后更旧的Item。
462 | 场景:Item被移除、Item有更新、滚动过程;
463 |
464 | ### SnapHelper
465 |
466 | SnapHelper是一个RecyclerView的工具类,本身是抽象类,默认有两种实现LinearSnapHelper和PageSnapHelper。
467 | 他们的主要作用是帮助RecyclerView自定义滑动方式,可以实现像ViewPager或者Gallery的滑动方式(一次翻一页或者一次翻多页)
468 |
469 | ## Scroller
470 |
471 | Scroller执行流程里面的三个核心方法 mScroller.startScroll() mScroller.computeScrollOffset() view.computeScroll()
472 |
473 | 1. 在mScroller.startScroll()中为滑动做了一些初始化准备。
474 | 比如:起始坐标,滑动的距离和方向以及持续时间(有默认值),动画开始时间等
475 | 2. mScroller.computeScrollOffset()方法主要是根据当前已经消逝的时间来计算当前的坐标点。
476 | 因为在mScroller.startScroll()中设置了动画时间,那么在computeScrollOffset()方法中依据已经消逝的时间就很容易得到
477 | 当前时刻应该所处的位置并将其保存在变量mCurrX和 mCurrY中。除此之外该方法还可判断动画是否已经结束。
478 |
479 | ## Android 动画原理
480 |
481 | ### 补间动画
482 |
483 | 在每一次VSYNC到来时,在View的draw方法里面,根据当前时间计算动画进度,计算出一个需要变换的Transformation矩阵, 然后最终设置到canvas上去,调用canvas
484 | concat做矩阵变换。
485 |
486 | 
487 |
488 | [Android动画Animation运行原理解析](https://mp.weixin.qq.com/s/uqFErwA5gBGrzW5GoKbnBA)
489 |
490 | ### 属性动画
491 |
492 | 如果当前存在要运行的动画,那么 AnimationHandler 会去通过 Choreographer 向底层注册监听下一个屏幕刷新信号, 当接收到信号时,它的 mFrameCallback
493 | 会开始进行工作,工作的内容包括遍历列表来分别处理每个属性动画在当前帧的行为, 处理完列表中的所有动画后,如果列表还不为 0,那么它又会通过 Choreographer
494 | 再去向底层注册监听下一个屏幕刷新信号事件,如此反复,直至所有的动画都结束。
495 |
496 | [属性动画 ValueAnimator 运行原理全解析](https://mp.weixin.qq.com/s/SZXJQNXar0SjApbl4rXicA)
497 |
498 | ### Lottie
499 |
500 | 使用工具将 AE 动画导出为 Json 文件,该文件描述了该动画,而 lottie 的原理就是将描述的动画用 native code 翻译出来,核心原理是 canvas 绘制。 lottie
501 | 随属性动画修改 progress,每一个 Layer 根据当前的 progress 绘制所对应的帧内容,progress 值变为1,动画结束。(有点类似于帧动画)
502 | [Lottie 实现炫酷动画背后的原理](https://juejin.cn/post/6844903828815347726)
503 |
504 | ## WebView
505 |
506 | ### Native 与 Js通信
507 |
508 | 复写 WebView 的 WebChromeClient 类的 onJsPrompt 方法,按照一定的协议,传递方法和参数,通过 WebView.loadUrl 回调执行结果。 Js 中执行
509 | window.prompt 调用 Native 方法。
510 |
511 | ### loadUrl 与 evaluateJavascript 的区别
512 |
513 | 1. evaluateJavascript 的执行不会使页面刷新,而方法 loadUrl 的执行则会使页面刷新
514 | 2. evaluateJavascript Android 4.4 后才可使用
515 |
516 | ### WebView 秒开方案
517 |
518 | 1. WebView 内核提前初始化
519 | 2. 资源预置,通过拦截 WebView 资源请求,直接返回本地资源
520 | 3. 数据预取,启动 WebView 的同时开始下载资源,WebView 可以直接使用已下载的资源
521 | 4. 使用离线包,需要有版本控制能力
522 |
523 | [Android Webview H5 秒开方案实现](https://mp.weixin.qq.com/s/XfBt_gTw0gN7tXzuyP4PTw)
524 |
525 | ## 换肤方案
526 |
527 | 通过 AssetManager 加载 apk 文件中的资源,通过 LayoutInflater.Factory hook View 创建,两者配合可以做到动态换肤。
528 |
529 | [Android 常用换肤方式以及原理分析](https://juejin.im/post/6844903670270656525)
530 |
531 | ```java
532 | class Skin {
533 | void loadRes() {
534 | String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test.apk";
535 | // 通过反射获取未安装apk的AssetManager
536 | AssetManager assetManager = AssetManager.class.newInstance();
537 | // 通过反射增加资源路径
538 | Method method = assetManager.getClass().getMethod("addAssetPath", String.class);
539 | method.invoke(assetManager, apkPath);
540 | File dexDir = ctx.getDir("dex", Context.MODE_PRIVATE);
541 | if (!dexDir.exists()) {
542 | dexDir.mkdir();
543 | }
544 | // 获取未安装apk的Resources
545 | Resources resources = new Resources(assetManager, ctx.getResources().getDisplayMetrics(),
546 | ctx.getResources().getConfiguration());
547 | // 获取未安装apk的ClassLoader
548 | ClassLoader classLoader = new DexClassLoader(apkPath, dexDir.getAbsolutePath(), null, ctx.getClassLoader());
549 | // 反射获取class
550 | Class aClass = classLoader.loadClass("com.noob.resourcesapp.R$drawable");
551 | int id = (int) aClass.getField("icon_collect").get(null);
552 | imageView.setImageDrawable(resources.getDrawable(id));
553 | }
554 |
555 | void setFactory() {
556 | LayoutInflater.from(this).setFactory(new LayoutInflater.Factory() {
557 | @Override
558 | public View onCreateView(String name, Context context, AttributeSet attrs) {
559 | Log.e("MainActivity", "name :" + name);
560 | int count = attrs.getAttributeCount();
561 | for (int i = 0; i < count; i++) {
562 | Log.e("MainActivity", "AttributeName :" + attrs.getAttributeName(i) + "AttributeValue :" + attrs.getAttributeValue(i));
563 | }
564 | return null;
565 | }
566 | });
567 | }
568 | }
569 | ```
570 |
571 | ## 热修复原理
572 |
573 | ### QQ空间
574 |
575 | 把补丁类生成 patch.dex,在app启动时,使用反射获取当前应用的ClassLoader,也就是 BaseDexClassLoader,
576 | 反射获取其中的pathList,类型为DexPathList,反射获取其中的 Element[] dexElements, 记为elements1;
577 | 然后使用当前应用的ClassLoader作为父ClassLoader,构造出 patch.dex 的 DexClassLoader, 通过反射可以获取到对应的 Element[]
578 | dexElements,记为elements2。将elements2拼在elements1前面,然后再去调用加载类的方法loadClass。
579 |
580 | - 隐藏的技术难点 CLASS_ISPREVERIFIED 问题
581 | apk在安装时会进行dex文件进行验证和优化操作。这个操作能让app运行时直接加载odex文件,能够减少对内存占用,加快启动速度,如果没有odex操作,需要从apk包中提取dex再运行。
582 | 在验证过程,如果某个类的调用关系都在同一个dex文件中,那么这个类会被打上CLASS_ISPREVERIFIED标记,表示这个类已经预先验证过了。
583 | 但是再使用的过程中会反过来校验下,如果这个类被打上了CLASS_ISPREVERIFIED但是存在调用关系的类不在同一个dex文件中的话,会直接抛出异常。
584 | 为了解决这个问题,QQ空间给出的解决方案就是,准备一个 Hack 类,这个类会单独打包成一个 hack.dex,然后在所有的类的构造方法中增加这样的代码:
585 |
586 | ```java
587 | class A {
588 | public A() {
589 | if (ClassVerifier.PREVENT_VERIFY) {
590 | System.out.println(Hack.class);
591 | }
592 | }
593 | }
594 | ```
595 |
596 | 这样在 odex 过程中,每个类都会出现 Hack 在另一个dex文件中的问题,所以odex的验证过程也就不会继续下去,这样做牺牲了dvm对dex的优化效果了。
597 |
598 | ### Tinker
599 |
600 | 修复前和修复后的apk分别定义为apk1和apk2,tinker自研了一套dex文件差分合并算法,在生成补丁包时,生成一个差分包 patch.dex,
601 | 后端下发patch.dex到客户端时,tinker会开一个线程把旧apk的class.dex和patch.dex合并,生成新的class.dex并存放在本地目录上,
602 | 重新启动时,会使用本地新生成的class.dex对应的elements替换原有的elements数组。
603 |
604 | 资源修复:新建 AssertManager,通过 addAssetPath 函数,加入外部的资源路径,然后将 Resources 的 mAssets 的字段设为新的 AssertManager,
605 | 这样在通过 getResources 去获取资源的时候就可以获取到我们外部的资源了。
606 |
607 | ### Robust
608 |
609 | 1. 打基础包时插桩,在每个方法前插入一段类型为 ChangeQuickRedirect 静态变量的逻辑;
610 | 2. 加载补丁时,从补丁包中读取要替换的类及具体替换的方法实现,新建 ClassLoader 加载补丁dex。找到补丁对应的 class,通过反射将 ChangeQuickRedirect
611 | 静态变量赋值为补丁中的实现,从而代理方法的实现。
612 |
613 | ```java
614 | // 插桩后的源码 State
615 | class A {
616 | public static ChangeQuickRedirect changeQuickRedirect;
617 |
618 | public long getIndex() {
619 | if (changeQuickRedirect != null) {
620 | // PatchProxy中封装了获取当前className和methodName的逻辑,并在其内部最终调用了changeQuickRedirect的对应函数
621 | if (PatchProxy.isSupport(new Object[0], this, changeQuickRedirect, false)) {
622 | return ((Long) PatchProxy.accessDispatch(new Object[0], this, changeQuickRedirect, false)).longValue();
623 | }
624 | }
625 | return 100L;
626 | }
627 | }
628 | ```
629 |
630 | ```java
631 | // 补丁类 StatePatch
632 | public class StatePatch implements ChangeQuickRedirect {
633 | @Override
634 | public Object accessDispatch(String methodSignature, Object[] paramArrayOfObject) {
635 | String[] signature = methodSignature.split(":");
636 | // 混淆后的 getIndex 方法 对应 a
637 | if (TextUtils.equals(signature[1], "a")) {//long getIndex() -> a
638 | return 106;
639 | }
640 | return null;
641 | }
642 |
643 | @Override
644 | public boolean isSupport(String methodSignature, Object[] paramArrayOfObject) {
645 | String[] signature = methodSignature.split(":");
646 | if (TextUtils.equals(signature[1], "a")) {//long getIndex() -> a
647 | return true;
648 | }
649 | return false;
650 | }
651 | }
652 | ```
653 |
654 | ## 插件化方案
655 |
656 | ### VirtualApk
657 |
658 | 1. Activity:在宿主apk中提前占几个坑,然后通过“欺上瞒下”的方式,启动插件apk的Activity; 因为要支持不同的launchMode以及一些特殊的属性,需要占多个坑。
659 | 2. Service:通过代理Service的方式去分发;主进程和其他进程,VirtualAPK使用了两个代理Service。
660 | 3. BroadcastReceiver:静态转动态。
661 | 4. ContentProvider:通过一个代理Provider进行分发。
662 |
663 | ### 资源id冲突如何解决
664 |
665 | 1. 修改aapt源码,定制aapt工具,编译期间修改PP段。(PP字段是资源id的第一个字节,表示包空间)
666 | DynamicAPK的做法就是如此,定制aapt,替换google的原始aapt,在编译的时候可以传入参数修改PP段:例如传入0x05编译得到的资源的PP段就是0x05。
667 | 2. 修改aapt的产物,即编译后期重新整理插件Apk的资源,编排ID。
668 | VirtualApk采用的就是这个方案。
669 |
670 | ## Kotlin
671 |
672 | ### 协程
673 |
674 | 协程是轻量级的线程,它基于线程池API。相比较 RxJava,协程可以使用阻塞的方式写出非阻塞式的代码,解决并发中常见的回调地狱,这是其最大的优点。
675 |
676 | [即学即用Kotlin - 协程](https://juejin.im/post/6854573211418361864)
677 |
678 | ## Android Jetpack
679 |
680 | ### Lifecycle
681 |
682 | 1. Activity中调用LifecycleRegistry的addObserver,传入一个LifecycleObserver
683 | 2. 传入的LifecycleObserver被封装成一个ObserverWithState存入集合中,当生命周期发生改变的时候,
684 | 就会遍历这个ObserverWithState集合,并且调用ObserverWithState的dispatchEvent进行分发
685 | 3. 在ObserverWithState构造方法中,调用了Lifecycling.getCallback(observer)生成了具体的 GenericLifecycleObserver对象返回。
686 | 在ObserverWithState的dispatchEvent()方法中调用了GenericLifecycleObserver对象的onStateChanged方法进行事件分发
687 | 4. 在 ComponentActivity 的 onCreate 时添加 ReportFragment,在 ReportFragment 中对 Activity 的声明周期进行分发
688 |
689 | [Android 官方架构组件(一)——Lifecycle](https://juejin.im/post/6844903748448288781)
690 |
691 | ### ViewModel
692 |
693 | 1. 在 Activity 中提供了 onRetainNonConfigurationInstance 方法,用于处理配置发生改变时数据的保存
694 | 2. ComponentActivity 重写 onRetainNonConfigurationInstance 方法,将 ViewModelStore 存储在
695 | NonConfigurationInstances
696 | 3. Activity 重新创建后,在 getViewModelStore 中通过 getLastNonConfigurationInstance 获取旧 Activity 保存的
697 | ViewModelStore
698 | 4. Fragment 的 ViewModel 存储在 Activity 的 ViewModelStore 中
699 |
700 | 
701 |
702 | [关于ViewModel你应该知道的知识点](https://juejin.cn/post/6890077903648718861#heading-13)
703 | [Jetpack 面试: ViewModel 必知的几个问题](https://mp.weixin.qq.com/s/-Zxf_560jgjWo_pRhxJBew)
704 |
705 | ### DataStore
706 |
707 | SharedPreferences 缺点
708 |
709 | 1. 效率低。直接I/O读写,使用 xml 格式,全量更新。
710 | 2. commit 可能会导致 ANR。commit 提交是同步的,直到磁盘操作成功后才会完成,所以当数据量比较大时,使用commit很可能引起ANR。
711 | 3. apply 可能导致 ANR。apply 异步写入时会向 QueuedWork 中添加一个等待写入完成任务,当生命周期处于 handleStopActivity 的时候会调用
712 | QueuedWork.waitToFinish 等待写入任务执行完毕。
713 | 4. getXXX() 导致ANR。get 方法必须等待 SP 异步加载完毕,有可能导致ANR。
714 |
715 | MMKV 优点
716 |
717 | 1. 通过 mmap 内存映射文件,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。
718 | 2. 使用 protobuf 协议序列化数据,性能高。
719 | 3. MMKV是增量更新,有性能优势。
720 |
721 | [再见SharedPreferences,你好MMKV!](https://mp.weixin.qq.com/s/VBMDIE0QHXQAMuIjon-Fjg)
722 |
723 | Preferences DataStore 优点
724 |
725 | - DataStore 是基于 Flow 实现的,所以保证了在主线程的安全性
726 | - 以事务方式处理更新数据,事务有四大特性(原子性、一致性、 隔离性、持久性)
727 | - 没有 apply() 和 commit() 等等数据持久的方法
728 | - 自动完成 SharedPreferences 迁移到 DataStore,保证数据一致性,不会造成数据损坏
729 | - 可以监听到操作成功或者失败结果
730 |
731 | ## 开源库
732 |
733 | ### LeakCanary的核心原理
734 |
735 | 1. 通过 registerActivityLifecycleCallbacks() 监听各个 Activity 的 onDestroy 方法
736 | Fragment: 通过向 Activity 的 FragmentManager 以及 childFragmentManager 注册一个 FragmentLifecycleCallback
737 | 来监听 Fragment Destroy
738 | ViewModel: 通过反射监听 ViewModel onCleared() 事件
739 | 2. Activity 退出后,拿到 Activity 的对象封装成 WeakReference 弱引用对象, 配合 ReferenceQueue,如果对象被回收,JVM
740 | 就会把弱引用存入与之关联的引用队列之中
741 | 3. 通过手动 Runtime.getRuntime().gc() 垃圾回收
742 | 4. 根据 ReferenceQueue 是否有值来判断对象是否被回收,如果有值,说明 Activity 已经被回收,没有泄露
743 | 5. 如果没有移除,通过 android 原生接口 Debug.dumpHprofData(),把 Hprof 文件搞下来,通过 haha 这个第三方库去解析是否有指定 Activity 的残留
744 |
745 | ### BlockCanary
746 |
747 | 1. 通过 Looper.setMessageLogging() 给主线程消息设置一个 Printer,记录每个消息的执行时间
748 | 2. 通过 Thread.getStackTrace() 获取执行堆栈
749 |
750 | ### EventBus
751 |
752 | [EventBus源码详解](https://juejin.im/post/6881265680465788936)
753 |
754 | ### Glide
755 |
756 | **Glide缓存原理**
757 | - Glide缓存分为弱引用+ LruCache+ DiskLruCache,其中读取数据的顺序是:弱引用 > LruCache > DiskLruCache>网络;写入缓存的顺序是:网络 --> DiskLruCache--> LruCache-->弱引用
758 | - 内存缓存分为弱引用的和 LruCache ,其中正在使用的图片使用弱引用缓存,暂时不使用的图片用 LruCache缓存,这一点是通过 图片引用计数器(acquired变量)来实现的,详情可以看内存缓存的小结。
759 | - 磁盘缓存就是通过DiskLruCache实现的,根据缓存策略的不同会获取到不同类型的缓存图片。它的逻辑是:先从转换后的缓存中取;没有的话再从原始的(没有转换过的)缓存中拿数据;再没有的话就从网络加载图片数据,获取到数据之后,再依次缓存到磁盘和弱引用。
760 | - [Android开源框架面试题:谈谈Glide框架的缓存机制设计](https://zhuanlan.zhihu.com/p/648543711)
761 |
762 | [Android glide使用过程中遇到的坑(进阶篇)](https://www.jianshu.com/p/deccde405e04)
763 |
764 | ### AndResGuard
765 |
766 | 1. 生成新的资源文件目录,里面对资源文件路径进行混淆(其中涉及如何复用旧的mapping文件)
767 | ,例如将res/drawable/hello.png混淆为r/s/a.png,并将映射关系输出到mapping文件中。
768 | 2. 对资源id进行混淆(其中涉及如何复用旧的mapping文件),并将映射关系输出到mapping文件中。
769 | 3. 生成新的resources.arsc文件,里面对资源项值字符串池、资源项key字符串池进行混淆替换,对资源项entry中引用的资源项字符串池位置进行修正、并更改相应大小,并打包生成新的apk。
770 |
--------------------------------------------------------------------------------
/doc/android/image/activity_lifecycle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/android/image/activity_lifecycle.jpg
--------------------------------------------------------------------------------
/doc/android/image/touch_event_consume.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/android/image/touch_event_consume.jpg
--------------------------------------------------------------------------------
/doc/android/image/touch_event_not_consume.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/android/image/touch_event_not_consume.jpg
--------------------------------------------------------------------------------
/doc/android/image/tween_animation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/android/image/tween_animation.png
--------------------------------------------------------------------------------
/doc/android/image/view_draw_process.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/android/image/view_draw_process.jpg
--------------------------------------------------------------------------------
/doc/android/image/view_model.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/android/image/view_model.webp
--------------------------------------------------------------------------------
/doc/java/image/hashmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/java/image/hashmap.jpg
--------------------------------------------------------------------------------
/doc/java/java.md:
--------------------------------------------------------------------------------
1 | # Java要点
2 |
3 | ## Object的hashCode方法,equals方法和clone方法在实现的时候要注意什么?
4 |
5 | 在一个运行的进程中,相等的对象必须要有相同的哈希码;不同的对象可以有相同的哈希码;
6 |
7 | 1. 无论你何时实现equals方法,你必须同时实现hashCode方法;
8 | 2. 永远不要把哈希码误用作为key,哈希冲突是很常见的事情;hashmap中的contains方法的实现!
9 | 3. 哈希码可变,hashcode并不保证在不同的应用执行中得到相同的结果;
10 | 4. 在分布式应用中不要使用哈希码。
11 |
12 | 替代哈希码:SHA1,加密的哈希码,160位密钥,冲突几乎是不可能的。
13 |
14 | 集合增加时的原理:
15 |
16 | 当使用HashSet时,hashCode()方法就会被调用,判断已经存储在集合中的对象的hashCode值是否与增加的对象的hashCode值一致;
17 | 如果不一致,直接加进去;如果一致,再进行equals方法的比较,equals方法如果返回true,表示对象已经加进去了,就不会再增加新的对象,否则加进去。
18 |
19 | ### 使用clone()方法的步骤:
20 |
21 | 1. 实现clone的类首先需要继承Cloneable接口。
22 | 2. 在类中重写Object类中的clone()方法。
23 | 3. 在clone方法中调用super.clone()。
24 | 4. 把浅复制的引用指向原型对象新的克隆体。
25 |
26 | ## HashMap是怎么实现的?
27 |
28 | 1.HashMap的数据结构
29 |
30 | 数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。 那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?
31 | 答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为“链表的数组”,如图:
32 |
33 | 
34 |
35 | 从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。 那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)
36 | %len获得,也就是元素的key的哈希值对数组长度取模得到。
37 | 比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。
38 |
39 | HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。 这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。
40 |
41 | 首先HashMap里面实现一个静态内部类Entry,其重要的属性有key,value,next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,
42 | 我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。
43 |
44 | [图解HashMap](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/1203/8832.html)
45 |
46 | 2.HashMap的存取实现
47 |
48 | 既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现:
49 |
50 | ```java
51 | class HashMapTest {
52 | void setValue() {
53 | // 存储时:
54 | int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值
55 | int index = hash % Entry.length;
56 | Entry[index] = value;
57 | }
58 |
59 | void getValue() {
60 | // 取值时:
61 | int hash = key.hashCode();
62 | int index = hash % Entry.length;
63 | return Entry[index];
64 | }
65 | }
66 | ```
67 |
68 | ## Java内部类为什么可以访问外部类的成员?
69 |
70 | 内部类都持有一个外部类的引用,这个是引用是外部类名.this。内部类可以定义在外部类中的成员位置上,也可以定义在外部类中的局部位置上。
71 | 当内部类被定义在局部位置上,只能访问局部中被final修饰的局部变量。
72 |
73 | 如果内部类被静态修饰,相当于外部类,会出现访问局限性,只能访问外部类中的静态成员。
74 |
75 | 注意:如果内部类中定义了静态成员,那么该内部类必须是静态的。内部类编译后的文件名为:"外部类名$内部类名.java"
76 |
77 | ## ArrayList如何边遍历边删除?多线程访问ArrayList怎么做到互斥访问?
78 |
79 | ### ArrayList边遍历边删除——迭代器:
80 |
81 | ```java
82 | class ArrayListTest {
83 | void main() {
84 | ArrayList list = new ArrayList();
85 | list.add("one");
86 | list.add("two");
87 | list.add("two");
88 | list.add("two");
89 | list.add("two");
90 | Iterator iter = list.iterator();
91 | while (iter.hasNext()) {
92 | String s = iter.next();
93 | if (s.equals("two")) {
94 | iter.remove();
95 | }
96 | }
97 | System.out.println(list);
98 | }
99 | }
100 | ```
101 |
102 | ### ListIterator和Iterator的区别:
103 |
104 | 1. ListIterator实现了Iterator接口,拥有Iterator的所有方法。
105 | 2. ListIterator有add()和set()方法,可以向List中添加/修改对象,而Iterator不能。
106 | 3. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历。 但是ListIterator有hasPrevious()和previous()
107 | 方法,可以实现逆向遍历,而Iterator不能。
108 | 4. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
109 |
110 | ### 使ArrayList线程安全:
111 |
112 | 1. 将访问ArrayList的方法设置为synchronized;
113 | 2. `List list = Collections.synchronizedList(new ArrayList());`
114 |
115 | ## Java的四中引用类型
116 |
117 | 强引用:JVM宁愿抛出OOM也不会将它回收,可能导致内存泄露
118 |
119 | 软引用:当内存空间不足的时候才会去回收软引用的对象
120 |
121 | 弱引用:在系统GC时,弱引用的对象一定会被回收,软弱引用适合保存那些可有可无的缓存数据
122 |
123 | 虚引用:虚引用跟没有引用差不多,即使虚引用对象还存在,get方法总是返回null,它最大的作用是跟踪对象回收,清理被销毁对象的相关资源
124 |
125 | WeakHashMap适用场景:如果系统需要一张很大的map表,map中的表项作为缓存之用,即使没能从map中拿到数据也没关系的情况下。
126 | 一旦内存不足的时候,WeakHashMap会将没有被引用的表项清除掉,从而避免内存溢出。它是实现缓存的一种特别好的方式。
127 |
128 | 如果希望WeakHashMap能够自动清理数据就不要在系统的其他地方强引用WeakHashMap的key,否则,这些key不会被回收。
129 |
130 | ## 抽象类和接口的区别
131 |
132 | 1. 抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。
133 | 2. 抽象类中可以定义非抽象方法,子类可以直接继承使用。接口中都是抽象方法,需要实现类去实现。
134 | 3. 抽象类使用的是is-a关系。接口使用的has-a系。
135 | 4. 抽象类的成员修饰符可以自定义,接口中的成员修饰符是固定的,全都是public的。
136 |
137 | ## JVM
138 |
139 | JVM内存模型: 程序计数器,虚拟机栈,本地方法栈,Java堆,方法区
140 |
141 | 程序计数器: 程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
142 | 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
143 |
144 | 虚拟机栈: 与程序计数器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。
145 | 虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。
146 |
147 | 本地方法栈: 本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,
148 | 其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
149 |
150 | Java堆: 几乎所有的对象和数组都是在堆中分配空间的,分为新生代和老年代。新生代可分为eden, survivor space 0, survivor space 1
151 |
152 | 方法区: 方法区(Method Area)与Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 在Hot
153 | spot虚拟机中,方法区也叫永久区,但是也会被GC,GC主要有两类:对常量池的回收,对类元数据的回收
154 |
155 | 如果VM确认所有该类的实例都被回收并且装载该类的类加载器也被回收了,那么就回收该类的元数据
156 |
157 | 运行时常量池(Runtime Constant Pool)是方法区的一部分。(不一定)
158 |
159 | Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,
160 | 运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
161 |
162 | JVM参数: 设置最大堆内存Xmx,最小堆内存Xms,新生代大小Xmn,老年代大小PermSize,线程栈大小Xss,新生代eden和s0空间大小比例以及老年代和新生代的空间大小比例
163 |
164 | ### 垃圾回收算法
165 |
166 | 1. 引用计数法: 给对象添加一个引用计数器,每当一个地方引用它时就+1,当引用失效时就-1,任何时刻计数器为0的对象就是不可能再被引用的。缺点是无法处理循环引用问题
167 | 2. 标记-清除法: 标记所有从根节点开始的可达对象,清除所有未被标记的对象。缺点是会造成内存空间不连续,不连续的内存空间的工作效率低于连续的内存空间,不容易分配内存
168 | 3. 复制算法: 将内存空间分成两块,每次将正在使用的内存中的存活对象复制到未使用的内存块中,算法效率高,但是代价是将系统内存折半。适用于新生代(存活对象少,垃圾对象多)
169 | 4. 标记-压缩算法: 标记-清除的改进,清除未标记的对象时还将所有的存活对象压缩到内存的一端既避免碎片产生,又不需要两块同样大小的内存块,性价比高。适用于老年代
170 | 5. 分代: 把堆分成两个或者多个子堆,每一个子堆被视为一代。算法在运行的过程中优先收集那些“年幼”的对象,如果一个对象经过多次收集仍然“存活”,
171 | 那么就可以把这个对象转移到高一级的堆里,减少对其的扫描次数。
172 |
173 | ### 垃圾回收器的类型
174 |
175 | - 线程数: 串行,并行
176 | - 工作模式: 并发,独占
177 | - 碎片处理: 压缩,非压缩
178 | - 分代: 新生代,老年代
179 |
180 | - 并行: 开启多个线程同时进行垃圾回收,缩短GC停顿时间
181 | - 并发: 垃圾回收线程和应用程序线程交替工作
182 | - CMS: Concurrent Mark Sweep 并发标记清除,减少GC造成的停顿时间。过程:初始标记,并发标记,重新标记,并发清理,并发重置
183 | - G1: 基于标记-整理算法
184 |
185 | ### GC Roots有哪些?
186 |
187 | 1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
188 | 2. 方法区中的类静态属性引用的对象
189 | 3. 方法区中的常量引用的对象
190 | 4. 原生方法栈(Native Method Stack)中 JNI 中引用的对象
191 |
192 | 对方法区的定义是:各个线程共享的内存区域,存储已被虚拟机加载的类信息,变量,静态变量等数据。
193 |
194 | ## 类加载器
195 |
196 | DexClassLoader和PathClassLoader的区别
197 |
198 | 1. DexClassLoader:能够加载未安装的jar/apk/dex
199 | 2. PathClassLoader:只能加载系统中已经安装过的apk
200 |
201 | ### 为什么要使用双亲委派机制?
202 |
203 | 1. 避免类的重复加载
204 | 2. 避免核心类被不同的类加载器加载到内存中造成冲突和混乱,保证Java核心库的安全
205 |
206 | ## ThreadLocal
207 |
208 | 一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用 ThreadLocal。
209 |
210 | 比如对应 Handler 来说,它需要获取当前线程的 Looper,很显然 Looper 的作用域就是线程并且不同线程具有不同的 Looper, 这个时候通过 ThreadLocal 就可以轻松实现
211 | Looper 在线程中的存取。 如果不采用 ThreadLocal,那么系统就必须提供一个全局的哈希表供 Handler 查找指定线程的 Looper, 这样一来就必须提供一个类似于
212 | LooperManager 的类了,但是系统并没有这么做而是选择了 ThreadLocal,这就是 ThreadLocal 的好处
213 |
214 | ```java
215 | // Looper#myLooper()
216 | class Looper {
217 | static final ThreadLocal sThreadLocal = new ThreadLocal<>();
218 |
219 | private static void prepare(boolean quitAllowed) {
220 | if (sThreadLocal.get() != null) {
221 | throw new RuntimeException("Only one Looper may be created per thread");
222 | }
223 | sThreadLocal.set(new Looper(quitAllowed));
224 | }
225 |
226 | public static @Nullable
227 | Looper myLooper() {
228 | return sThreadLocal.get();
229 | }
230 | }
231 | ```
232 |
233 | 源码
234 |
235 | ```java
236 | public class ThreadLocal {
237 | private final int threadLocalHashCode = nextHashCode();
238 | private static int nextHashCode = 0;
239 | private static final int HASH_INCREMENT = 0x61c88647;
240 |
241 | private static synchronized int nextHashCode() {
242 | int h = nextHashCode;
243 | nextHashCode = h + HASH_INCREMENT;
244 | return h;
245 | }
246 |
247 | public T get() {
248 | Thread t = Thread.currentThread();
249 | ThreadLocalMap map = getMap(t);
250 | if (map != null)
251 | return (T) map.get(this);
252 | T value = initialValue();
253 | createMap(t, value);
254 | return value;
255 | }
256 |
257 | public void set(T value) {
258 | Thread t = Thread.currentThread();
259 | ThreadLocalMap map = getMap(t);
260 | if (map != null)
261 | map.set(this, value);
262 | else
263 | createMap(t, value);
264 | }
265 |
266 | ThreadLocalMap getMap(Thread t) {
267 | return t.threadLocals;
268 | }
269 |
270 | void createMap(Thread t, T firstValue) {
271 | t.threadLocals = new ThreadLocalMap(this, firstValue);
272 | }
273 | // ...
274 | }
275 | ```
276 |
277 | ThreadLocalMap 类是ThreadLocal中定义的内部类,但是它的实例却用在Thread类中:
278 |
279 | ```java
280 | public class Thread implements Runnable {
281 | // ...
282 | ThreadLocal.ThreadLocalMap threadLocals = null;
283 | // ...
284 | }
285 | ```
286 |
287 | [带你了解源码中的 ThreadLocal](https://mp.weixin.qq.com/s/VNBwPPJeENyU9iyxJcN1qw)
288 |
289 | ## 设计模式
290 |
291 | 1.单例模式
292 |
293 | 2.工厂模式
294 |
295 | 简单工厂模式 工厂方法模式 抽象工厂模式
296 |
297 | 3.代理模式和装饰模式的区别
298 |
299 | 代理模式关注于控制对对象的访问,而装饰器模式关注于在一个对象上动态的添加方法。换句话说,用代理模式,代理类可以对它的客户隐藏一个对象的具体信息。
300 | 因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
301 | 使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
302 |
303 | ## 位运算
304 |
305 | & 按位与 相同位的两个数字都为1,则为1;若有一个不为1,则为0。(5&3=1)
306 |
307 | | 按位或 相同位只要一个为1即为1。(5|3=7)
308 |
309 | ^ 按位异或 相同位不同则为1,相同则为0。(5^3=6)
310 |
311 | ~ 按位取反 把内存中的0和1全部取反。(~5=-6)
312 |
313 | << 左移 (5<<1=10)
314 |
315 | `>>` 有符号右移 (5`>>`1=2)
316 |
317 | `>>>` 无符号右移 (5`>>>`1=2)
318 |
319 | ## 线程安全
320 |
321 | 线程 A 和线程 B 分别对主内存的变量进行读写操作。其中主内存中的变量为共享变量,也就是说此变量只此一份,多个线程间共享。
322 | 但是线程不能直接读写主内存的共享变量,每个线程都有自己的工作内存,线程需要读写主内存的共享变量时需要先将该变量拷贝一份副本到自己的工作内存,
323 | 然后在自己的工作内存中对该变量进行所有操作,线程工作内存对变量副本完成操作之后需要将结果同步至主内存。
324 |
325 | 线程的工作内存是线程私有内存,线程间无法互相访问对方的工作内存。
326 |
327 | ### volatile是如何做到及时可见性的
328 |
329 | 当共享变量被 volatile 修饰后,在多线程环境下,当一个线程对它进行修改值后,会立即写入到内存中,
330 | 然后让其他所有持有该共享变量的线程的工作内存中的值过期,这样其他线程就必须去内存中重新获取最新的值,从而做到共享变量及时可见性。
331 |
332 | ### synchronized 实现原理
333 |
334 | JVM 基于进入和退出Monitor对象来实现方法同步和代码块同步。
335 |
336 | 方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。 JVM可以从方法常量池中的方法表结构(method_info Structure) 中的
337 | ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。 当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED
338 | 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词), 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。
339 |
340 | 代码块的同步是利用monitorenter和monitorexit这两个字节码指令。它们分别位于同步代码块的开始和结束位置。
341 | 当jvm执行到monitorenter指令时,当前线程试图获取monitor对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;
342 | 当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。 如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。
343 |
344 | ### 公平锁和非公平锁
345 |
346 | 公平锁指在分配锁前检查是否有线程在排队等待获取该锁,优先将锁分配给排队时间最长的线程。
347 |
348 | 非公平锁指在分配锁时不考虑线程排队等待的情况,直接尝试获取锁,在获取不到锁时再排到队尾等待。 因为公平锁需要在多核的情况下维护一个锁线程等待队列,
349 | 基于该队列进行锁的分配,因此效率比非公平锁低很多,java中的synchronized是非公平锁,ReentrantLock默认的lock方法采用的是非公平锁,ReentrantLock也支持公平锁。
350 |
351 | ## 13. 泛型
352 |
353 | ### 泛型好处
354 |
355 | 1. 泛型简单易用
356 | 2. 类型安全: 泛型的主要目标是实现java的类型安全。泛型可以使编译器知道一个对象的限定类型是什么,这样编译器就可以在一个高的程度上验证这个类型
357 | 3. 消除了强制类型转换: 使得代码可读性好,减少了很多出错的机会
358 |
359 | ### 泛型的实现原理
360 |
361 | 泛型的实现是靠类型擦除技术,类型擦除是在编译期完成的,也就是在编译期,编译器会将泛型的类型参数都擦除成它的限定类型, 如果没有则擦除为 object
362 | 类型,之后在获取的时候再强制类型转换为对应的类型。在运行期间并没有泛型的任何信息,因此也没有优化。
363 |
--------------------------------------------------------------------------------
/doc/network/image/sliding_window.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/network/image/sliding_window.jpg
--------------------------------------------------------------------------------
/doc/network/image/tcp_ip.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/doc/network/image/tcp_ip.jpg
--------------------------------------------------------------------------------
/doc/network/network.md:
--------------------------------------------------------------------------------
1 | # 计算机网络要点
2 |
3 | ### 1. TCP/IP
4 |
5 | 
6 |
7 | 1. 网络层:负责相邻计算机之间的通信。
8 |
9 | - IP(Internet Protocol)协议
10 | - ICMP(Internet Control Message Protocol)控制报文协议
11 | - ARP(Address Resolution Protocol)地址转换协议
12 | - RARP(Reverse ARP)反向地址转换协议
13 |
14 | 2. 传输层:提供应用程序间的通信。
15 |
16 | - 传输控制协议TCP(Transmission Control Protocol)
17 | TCP协议是一种可靠的、面向连接的协议,保证通信主机之间有可靠的字节流传输,完成流量控制功能,协调收发双方的发送与接收速度,达到正确传输的目的。
18 | - 用户数据报协议UDP(User Datagram protocol)
19 | UDP是一种不可靠、无连接的协议,其特点是协议简单、额外开销小、效率较高,但是不能保证传输是否正确。
20 |
21 | 3. 应用层:向用户提供一组常用的应用程序,比如电子邮件、文件传输访问、远程登录等。
22 |
23 | - FTP(File Transfer Protocol)是文件传输协议,一般上传下载用FTP服务。
24 | - Telnet服务是用户远程登录服务,使用明码传送,保密性差、简单方便。
25 | - DNS(Domain Name Service)是域名解析服务,提供域名到IP地址之间的转换。
26 | - SMTP(Simple Mail Transfer Protocol)是简单邮件传输协议,用来控制信件的发送、中转。
27 | - NFS(Network File System)是网络文件系统,用于网络中不同主机间的文件共享。
28 | - HTTP(Hypertext Transfer Protocol)是超文本传输协议,用于实现互联网中的WWW服务。
29 |
30 | ### TCP如何保证可靠性
31 |
32 | 1. 将数据截断为合理的长度。
33 | 应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。
34 | 2. 超时重发。
35 | 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
36 | 3. 对于收到的请求,给出确认响应。
37 | 当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
38 | 4. TCP将保持它首部和数据的检验和。
39 | 这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。
40 | 5. 对失序数据进行重新排序,然后才交给应用层。
41 | 既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。 如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
42 | 6. 对于重复数据,能够丢弃重复数据。
43 | 既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
44 | 7. TCP可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出
45 | TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。 这将防止较快主机致使较慢主机的缓冲区溢出。TCP使用的流量控制协议是可变大小的滑动窗口协议。
46 |
47 | ## 2. HTTP
48 |
49 | HTTP请求包含的内容:1.请求行2.请求头3.请求体
50 |
51 | 第一部分请求行写法是固定的,由三部分组成,第一部分是请求方法,第二部分是请求网址,第三部分是HTTP版本。
52 |
53 | 第二部分HTTP头在HTTP请求可以是3种HTTP头:1.请求头(request header) 2.普通头(general header) 3.实体头(entity header)。
54 | 通常来说,由于Get请求往往不包含内容实体,因此也不会有实体头。
55 |
56 | 第三部分内容只在POST请求中存在,因为GET请求并不包含任何实体。
57 |
58 | HTTP响应包含的内容:1.响应行2.响应头3.响应体
59 |
60 | 第一部分包括HTTP版本、响应状态码、状态码的描述。
61 |
62 | 1xx信息类 2xx响应成功 3xx重定向类 4xx客户端错误类 5xx服务端错误类
63 |
64 | 第二部分包含的头包括:1.响应头(response header) 2.普通头(general header) 3.实体头(entity header)。
65 |
66 | 第三部分HTTP响应内容就是HTTP请求所请求的信息。这个信息可以是一个HTML,也可以是一个图片。
67 |
68 | ### HTTP 2.0 与 HTTP 1.1 的区别
69 |
70 | 1. 新的二进制格式(Binary Format)
71 | HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
72 | 2. 多路复用(MultiPlexing)
73 | 即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的
74 | id将request再归属到各自不同的服务端请求里面。
75 | 3. header压缩
76 | HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header
77 | fields表,既避免了重复header的传输,又减小了需要传输的大小。
78 | 4. 服务端推送(server push)
79 | 同SPDY一样,HTTP2.0也具有server push功能。
80 |
81 | ### HttpClient两个超时时间
82 |
83 | 1. 连接超时 connectionTimeout:指的是连接一个url的连接等待时间
84 | 2. 读取数据超时 soTimeout:指的是连接上一个url,获取response的返回等待时间
85 |
86 | ### 浏览器输入一个url到服务器处理整个过程
87 |
88 | 1. 浏览器向DNS服务器请求解析该URL中的域名所对应的IP地址;
89 | 2. 解析出IP地址后,根据该IP地址和默认端口80,和服务器建立TCP连接;
90 | 3. 浏览器发出HTTP请求,该请求报文作为TCP三次握手的第三个报文的数据发送给服务器;
91 | 4. 服务器把对应的html文本发送给浏览器;
92 | 5. 释放TCP连接;
93 | 6. 浏览器将该文本显示出来。
94 |
95 | ### Http与Https的区别:
96 |
97 | 1. Https = Http + SSL + 加密算法 + 证书验证
98 | 2. Http使用80端口,Https使用443端口
99 |
100 | ### HTTPS加密过程
101 |
102 | 1. 访问HTTPS网站,服务端下发公钥。
103 | 2. 客户端向证书服务器验证公钥,然后生成一个串随机AES_128密码(假如是用AES加密),并把这个密码用刚才那个公钥加密,发给服务端。
104 | 3. 服务端用私钥解密浏览器发送的数据,得到浏览器随机生成的AES_128密码,并把网页内容全部用AES_128加密器起来,返回给浏览器。
105 | 4. 浏览器用刚刚的AES_128密码解密服务器返回的数据,得到可读的内容。
106 | 5. 之后发出的请求数据,也是用AES_128密码来加密。
107 |
108 | https://www.zhihu.com/question/28617156/answer/169262169
109 |
110 | ## 3. Session与Cookie的区别
111 |
112 | 1. Session保存在服务器,客户端不知道其中的信息;Cookie保存在客户端,服务器能够知道其中的信息。
113 | 2. Session中保存的是对象,Cookie中保存的是字符串。
114 | 3. Session不能区分路径,同一个用户在访问一个网站期间,所有的Session在任何一个地方都可以访问到。
115 | 而Cookie中如果设置了路径参数,那么同一个网站中不同路径下的Cookie互相是访问不到的。
116 | 4. Session需要借助Cookie才能正常工作。如果客户端完全禁止Cookie,Session将失效。
117 |
118 | ## 4. 三次握手四次挥手
119 |
120 | ### 三次握手
121 |
122 | 1. 建立连接时,客户端发送syn包到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
123 | 2. 服务器收到syn包,必须确认客户的SYN,同时自己也发送一个SYN包,即SYN+ACK 包,此时服务器进入SYN_RECV状态;
124 | 3. 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
125 |
126 | ### 四次挥手
127 |
128 | 1. 客户端发送一个FIN,用来关闭客户端到服务器的数据传送。
129 | 2. 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。客户端进入FIN_WAIT状态。
130 | 3. 服务器准备关闭客户端的连接,发送一个FIN给客户端。
131 | 4. 客户端发回ACK报文确认,并进入TIME_WAIT状态,若2MSL(报文最大生存时间)后依然没有收到回复则关闭连接。
132 |
133 | ## 5. TCP滑动窗口协议
134 |
135 | 
136 |
137 | 1. 首先是AB之间三次握手建立TCP连接。在报文的交互过程中,A将自己的缓冲区大小(窗口大小)3发送给B,B同理,这样双方就知道了对端的窗口大小。
138 | 2. A开始发送数据,A连续发送3个单位的数据,因为他知道B的缓冲区大小。在这一波数据发送完后,A就不能再发了,需等待B的确认。
139 | 3. A发送过来的数据逐渐将缓冲区填满。
140 | 4. 这时候缓冲区中的一个报文被进程读取,缓冲区有了一个空位,于是B向A发送一个ACK,这个报文中指示窗口大小为1。
141 | A收到B发过来的ACK消息,并且知道B将窗口大小调整为1,因此他只发送了一个单位的数据并且等待B的下一个确认报文。
142 | 5. 如此反复。
143 |
--------------------------------------------------------------------------------
/doc/os/os.md:
--------------------------------------------------------------------------------
1 | # 操作系统要点
2 |
3 | ## 1. 进程与线程
4 |
5 | - 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
6 | - 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
7 |
8 | ### 进程和线程的关系:
9 |
10 | 1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
11 | 2. 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
12 | 3. CPU分给线程,即真正在CPU上运行的是线程。
13 | 4. 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
14 |
15 | ### 进程与线程的区别:
16 |
17 | 1. 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
18 | 2. 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
19 | 3. 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
20 | 4. 系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
21 | 但是进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
22 | 线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些
23 |
24 | 结论:
25 |
26 | 1. 线程是进程的一部分
27 | 2. CPU调度的是线程
28 | 3. 系统为进程分配资源,不对线程分配资源
29 |
30 | 进程的三种状态:就绪、运行、阻塞
31 |
32 | 线程的五种状态:新建、就绪、运行、阻塞、死亡
33 |
34 | ### 进程调度算法
35 |
36 | 1. 先来先服务(FCFS)
37 | 按照请求次序进行调度。实现简单不用对队列进行调整,但是可能因某个元素处理时间过长,出现等待超时。
38 | 2. 短作业优先(SJF)
39 | 按照作业执行的消耗时间,消耗最短时间的先执行。需要预先估计作业执行时间,耗时较长的作业可能一直得不到执行。
40 | 3. 基于优先权的调度算法(FPPS)
41 | 给作业赋予一定优先权,每次从最高优先权的元素中取出一个并执行。
42 | 4. 时间片轮转(RR)
43 | 给每个作业赋予一个运行时间片,当时间片到则取下一个元素。是对FCFS和SJF算法的折衷,不会出现超时。
44 |
45 | ## 2. 常用SQL语句
46 |
47 | 1. 数据定义
48 |
49 | ```
50 | Create table sc(sno char(9),cno char(4),grade smallint,primary key(sno,cno),
51 | froeign key(sno) references student(sno),foreign key(cno) references course(cno));
52 | Create view is_student as select sno,sname,sage from student where sdept='IS';
53 | Drop table sc;
54 | Drop view is_student;
55 | ```
56 |
57 | 2. 数据查询
58 |
59 | 2.1 字符匹配
60 |
61 | %代表任意长度的字符串,_代表任意单个字符串。
62 |
63 | ```
64 | Select * from student where sname like '王%';
65 | ```
66 |
67 | 2.2 ORDER BY
68 |
69 | 升序(ASC) 降序(DESC)
70 |
71 | 2.3 聚集函数
72 |
73 | COUNT SUM AVG MAX MIN
74 |
75 | 2.4 WHERE与HAVING的区别
76 |
77 | WHERE字句与HAVING短语的区别在于作用对象不同,WHERE字句作用于基本表或视图,从中选择满足条件的元组。HAVING短语作用于组,从中选择满足条件的组。
78 |
79 | ```
80 | Select sno from sc group by sno having count(*)>3;
81 | ```
82 |
83 | 2.5 嵌套查询
84 |
85 | (NOT)IN, >, >ANY, >ALL, !=ANY, (NOT)EXISTS
86 |
87 | 查询选修了全部课程的学生姓名(没有一门课是他不选的)
88 |
89 | ```
90 | Select sname from student where not exists(select * from course where not exists
91 | (select * from sc where sno=student.sno and cno=course.cno));
92 | ```
93 |
94 | 2.6 limit的使用
95 |
96 | 查询课程成绩排名第三的学生
97 |
98 | ```
99 | Select sno,grade from sc order by grade desc limit 2,1;
100 | ```
101 |
102 | 3. 数据更新
103 |
104 | ```
105 | Insert into student(sno,sname) values('200215128','陈冬');
106 | Update student set sage=22 where sno='200215121';
107 | Delete from student where sno='200215128';
108 | ```
109 |
110 | 4. 触发器
111 |
112 | ```
113 | Create trigger t_student after insert on student for each row insert into course
114 | values(new.sno,new.sname);
115 | Drop trigger t_student;
116 | ```
--------------------------------------------------------------------------------
/doc/resume/introduce.md:
--------------------------------------------------------------------------------
1 | 你好,我是XXX,非常荣幸能够来参加贵公司的面试,下面我简单介绍一下我的个人情况,我从毕业到现在供职了网易和阿里两家公司,一直从事Android开发岗位,在工作之外,我喜欢编写技术博客,以及在GitHub上贡献开源代码,目前在GitHub上总共有3k的Star,技术博客有超过十万的阅读量,我非常热爱移动开发,早已久仰团队对技术的看重,所以希望自己今天在面试中有个更好的表现,未来也希望和你成为同事。
2 |
3 | - 亮点
4 | - 不要背简历
--------------------------------------------------------------------------------
/doc/resume/resume.md:
--------------------------------------------------------------------------------
1 | # 个人信息
2 |
3 | - 王晨彦/男/1993
4 | - 本科/西南科技大学/软件工程
5 | - 工作年限:6年
6 | - 手机:13136109681
7 | - Email:wangchenyan2015@gmail.com
8 | - 技术博客:https://juejin.im/user/2313028193754168/posts
9 | - Github:https://github.com/wangchenyan
10 | - 期望职位:Android 开发专家
11 |
12 | # 个人经历
13 |
14 | - 2021.05-至今 字节跳动资深 Android 开发工程师
15 | - 2018.10-2021.05 阿里巴巴资深 Android 开发工程师
16 | - 2016.07-2018.10 网易高级 Android 开发工程师
17 | - 2012.09-2016.06 西南科技大学 软件工程
18 |
19 | # 项目经验
20 |
21 | ## 字节教育在线教室(2021.05-至今)
22 |
23 | ### 工作职责
24 |
25 | 1. 负责在线教室解决方案层整体架构设计,提供开箱即用同时具备良好扩展能力的教室服务,降低业务接入成本,同时提供灵活定制的能力
26 | 2. 负责在线教室信令&状态机模块的重构和性能优化工作,通过良好的设计实现平台层和组件层的解耦;进行了深入的性能优化,减少信令包体积60%,解码速度提升2倍以上,GC次数下降28%
27 | 3. 负责台灯自习室性能优化,3个Top 10卡顿点优化了到1个,启动布局主线程耗时减少85%,GC 次数减少 48.9%
28 |
29 | ### 技术要点
30 |
31 | 架构设计、性能优化
32 |
33 | ## 口碑&饿了么(2018.10-2021.05)
34 |
35 | ### 工作职责
36 |
37 | 1. 负责口碑店铺动态化能力搭建,实现端内动态发布能力
38 | 2. 负责口碑店铺行业化框架封装,提供行业化店铺小程序独立发布能力
39 | 3. 负责饿了么合并下单和收银台日常迭代与性能优化
40 |
41 | ### 技术要点
42 |
43 | Native 动态化,小程序
44 |
45 | ## 网易考拉(2018.03-2018.10)
46 |
47 | ### 工作职责
48 |
49 | 1. 负责网易考拉 Android 种草社区模块设计与开发
50 | 2. 负责网易考拉 Android 多媒体库的开发与维护
51 | 3. 负责网易考拉 Android 权限优化、安装包瘦身、新版本适配等
52 |
53 | ### 技术要点
54 |
55 | 路由总线、性能优化
56 |
57 | ## 网易七鱼智能客服(2016.07-2018.03)
58 |
59 | ### 工作职责
60 |
61 | 1. 负责网易七鱼 Android SDK 的设计与开发
62 | 2. 负责网易七鱼客服 APP 的设计与开发,实现热修复能力
63 | 3. 通过内存映射优化七鱼 SDK 日志性能,写入速度提升 20 倍
64 |
65 | ### 技术要点
66 |
67 | 多进程、长连接、热修复
68 |
69 | # 专业技能
70 |
71 | * 熟练掌握 Java(Kotlin) 语言,熟悉 JVM
72 | * 熟练掌握多线程机制,线程间通信及线程安全
73 | * 熟悉 Gradle 语法,了解热修复和插件化
74 | * 熟悉掌握常用的数据结构知识和算法
75 | * 熟悉数据库,掌握 MySQL 的基本操作
76 | * 具备面向对象分析与设计能力,了解常用设计模式
77 | * 了解小程序、React Native、Weex等混合开发框架
78 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | android.useAndroidX=true
15 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangchenyan/android-interview/6131932d03929b7c7428c46b865189ab1ae2353d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Nov 09 17:23:46 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':code'
3 |
--------------------------------------------------------------------------------