<?xml version="1.0" encoding="utf-8" ?><rss version="2.0"><channel><title><![CDATA[小小时光]]></title><description><![CDATA[行有不得，反求诸己。愿与君共勉。]]></description><link>https://blog.youkuaiyun.com/zhourui_1021</link><language>zh-cn</language><generator>https://blog.youkuaiyun.com/</generator><copyright><![CDATA[Copyright &copy; zhourui_1021]]></copyright><item><title><![CDATA[数据结构（一）数组]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/120190960</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/120190960</guid><author>zhourui_1021</author><pubDate>Thu, 09 Sep 2021 21:57:50 +0800</pubDate><description><![CDATA[概述

说起数组我们都不陌生，几乎在每一种编程语言中，基本上都会有数组这种数据类型。不仅如此它还是是最基础最简单的数据结构。尽管如此，可能还是有一些人并没有真正的理解这个基础数据结构的精髓所在。

首先，我们都知道，在java中数组是从 0 开始编号的，但是为什么数组要从 0 开始编号，而不是从 1 开始呢？从 1 开始不是更加的符合人类的思维方式吗？

定义

数组（Array）是一种线性表数据结构。它用一组连续的内存空间，来存储一组具有相同类型的数据。

首先，定义中有几个关键词，需要我们关注一下。理解]]></description><category></category></item><item><title><![CDATA[算法基础：复杂度分析（下）浅析最好、最坏、平均、均摊时间复杂度]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/120091305</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/120091305</guid><author>zhourui_1021</author><pubDate>Sat, 04 Sep 2021 20:12:29 +0800</pubDate><description><![CDATA[算法基础：复杂度分析（上）如何分析、统计算法的执行效率和资源消耗

在时间复杂度分析中，还有四个概念也是需要了解的。它们分别是：最好情况时间复杂度（best case time complexity）、最坏情况时间复杂度（worst cast time complexity）、平均情况时间复杂度（average cast time complexity）和 均摊时间复杂度（amortized time complexity）。

最好、最坏情况时间复杂度

下面看一段代码，我们分别从最好和最坏的情况下去分析]]></description><category></category></item><item><title><![CDATA[算法基础：复杂度分析（上）：如何分析、统计算法的执行效率和资源消耗？]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/119968341</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/119968341</guid><author>zhourui_1021</author><pubDate>Sat, 28 Aug 2021 22:05:07 +0800</pubDate><description><![CDATA[前言

我们都知道，数据结构和算法本身解决的是 “快” 和 “省” 的问题，即如何让代码运行得更快，如何让代码更生存储空间。所以，执行效率是算法一个非常重要的考量指标。那如何来衡量我们编写的算法代码的执行效率呢？那就是时间、空间复杂度分析。

为什么需要复杂度分析？

我们知道在算法中有一种事后统计法，这种方法就是将代码执行一遍，通过统计、监控等方式就能得到算法执行时间和占用空间的内存大小。但是这种时候统计方法有非常大的局限性。

1. 测试结果非常依赖测试环境

同样的代码在Intel Core i9处理]]></description><category></category></item><item><title><![CDATA[LeetCode刷题之数组篇（二）三数之和]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/119653604</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/119653604</guid><author>zhourui_1021</author><pubDate>Thu, 12 Aug 2021 22:46:05 +0800</pubDate><description><![CDATA[题目描述：给你一个包含 n 个整数的数组 nums ，判断 nums 中是否存在三个元素 a，b，c，使得 a + b + c = 0 ？请你找出所有和为 0 且不重复的三元组。

注意：答案中不可以包含重复的三元组。

示例1：

输入： nums = [-1, 0, 1, 2, -1, -4];

输出：[[-1, -1, 2]，[-1, 0, 1]]

这道题 “不重复”是一个切入点。正如官方给的题解中提到的，在最坏的情况下，数组中的元素全部为0，如果此时我们还继续使用三重循环来遍历所有的数组。会得到]]></description><category></category></item><item><title><![CDATA[LeetCode刷题之数组篇（一）两数之和]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/119617557</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/119617557</guid><author>zhourui_1021</author><pubDate>Wed, 11 Aug 2021 22:04:54 +0800</pubDate><description><![CDATA[题目描述：给定一个整数数组 nums 和一个整数目标值 target，请你在该数组中找出和为目标值 target 的那两个整数，并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是，数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。

实例1：

输入：nums = [2, 7, 11, 15]，target = 9

输出：[0, 1]

解释：因为 nums[0] + nums[1] == 9，返回 [0, 1]。

这个题目还是比较简单的，一般的我们都能想到使用两层 for .]]></description><category></category></item><item><title><![CDATA[Android 11.0 ActivityManagerService的启动流程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/119282410</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/119282410</guid><author>zhourui_1021</author><pubDate>Sat, 31 Jul 2021 22:42:28 +0800</pubDate><description><![CDATA[首先推荐一篇文章，ActivityManagerService-AMS启动流程-[Android取经之路]。这篇文章是以Android 10.0源码为基础分析的，写的很详细也很棒，我学习AMS的启动就是看着这篇文章学的，在此先感谢作者的分享。

我们都知道，AMS非常重要，关乎着四大组件的启动，同时它也非常非常复杂，如果不是做系统定制开发的想把AMS彻底搞清楚那真的是非常困难的一项巨艰巨的任务。同时，即便是做系统定制开发的人，甚至是Google的开发者也不一定能完全搞清楚。因...]]></description><category></category></item><item><title><![CDATA[Java中取模与取余]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/118710626</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/118710626</guid><author>zhourui_1021</author><pubDate>Tue, 13 Jul 2021 21:43:56 +0800</pubDate><description><![CDATA[概述

通常取模运算也叫取余运算，它们返回的结果都是余数。rem(取余)和mod(取模)唯一的区别在于：当X和Y的正负号一样的时候，两个函数结果是等同的；当X和Y的符号不同时，rem(取余)函数的结果的符号和X的符号一样，而mod(取模)函数的结果符号和Y的符号一样。

这是由于这两个函数的生成机制不同，rem函数采用fix函数，而mod函数采用了floor函数（这两个函数是用来取整的，fix函数向0方向舍入，floor函数向无穷小的方向舍入）。rem(x, y)函数返回的是x-n.*y，如果y不等于0，其]]></description><category></category></item><item><title><![CDATA[Android 11.0 PackageManagerService（三）APK的安装过程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/118465753</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/118465753</guid><author>zhourui_1021</author><pubDate>Tue, 06 Jul 2021 21:32:09 +0800</pubDate><description><![CDATA[一. APK的安装步骤

安装步骤一：把APK的信息通过IO流的形式写入到PackageInstaller.Session中

安装步骤二：调用PackageInstaller.Session的commit方法，把APK的信息交给PackageManagerService处理

安装步骤三：进行Apk的Copy操作，进行安装

二. 点击安装后到完成APK的拷贝流程如下：



点击一个未安装的apk后，会弹出安装界面，点击确定按钮后，会进入PacakageInstallerActivity.java的bi]]></description><category></category></item><item><title><![CDATA[面试官：准备用HashMap存1w条数据，构造时传10000还会触发扩容吗？]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/106150620</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/106150620</guid><author>zhourui_1021</author><pubDate>Wed, 16 Jun 2021 13:44:00 +0800</pubDate><description><![CDATA[// 预计存入 1w 条数据，初始化赋值 10000，避免 resize。
HashMap&lt;String,String&gt; map = new HashMap&lt;&gt;(10000)
// for (int i = 0; i &lt; 10000; i++)


Java 集合的扩容
HashMap 算是我们最常用的集合之一，虽然对于 Android 开发者，Google 官方推荐了更省内存的 SparseArray 和 ArrayMap，但是 HashMap 依然是最常用的...]]></description><category></category></item><item><title><![CDATA[Android 11.0 PackageManagerService（二）APK扫描过程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/117306320</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/117306320</guid><author>zhourui_1021</author><pubDate>Tue, 15 Jun 2021 22:56:44 +0800</pubDate><description><![CDATA[PackageManagerService的构造函数中调用了scanDirTracedLI方法来扫描某个目录的apk文件。

1. 扫描路径

在Android 10.0中，PKMS主要扫描以下路径的APK信息：


/vendor/overlay
/product/overlay
/product_services/overlay
/odm/overlay
/oem/overlay
/system/framework
/system/priv-app
/system/app
/vendor/priv-ap]]></description><category></category></item><item><title><![CDATA[Android 11.0 PackageManagerService（一）工作原理和启动流程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/115145032</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/115145032</guid><author>zhourui_1021</author><pubDate>Tue, 30 Mar 2021 22:40:34 +0800</pubDate><description><![CDATA[1. 概述

 PackageManagerService是Android系统核心服务之一，也是Android中最常用的服务之一。它主要负责的工作如下：

  1. 解析AndroidManifest.xml文件，解析清单文件中的所有节点信息。

  2. 扫描.apk文件，安装系统应用，本地应用等。

  3. 管理本地应用，主要有：安装、卸载、应用信息查询等。

2. 核心源码


/frameworks/base/core/java/android/app/ApplicationP...]]></description><category></category></item><item><title><![CDATA[Android 锁屏状态下启动应用很慢的原因分析]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/114664342</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/114664342</guid><author>zhourui_1021</author><pubDate>Fri, 12 Mar 2021 16:20:54 +0800</pubDate><description><![CDATA[最近在工作中遇到一个需求就是，在锁屏状态下直接拨打电话，功能实现之后测试给的反馈是操作之后到电话拨出的时间太长(大概5秒)。希望能优化一下。后来经过分析确定不是应用层的问题。然后就一顿百度、Google，发现了一篇文章专门在分析这个问题。我也是看了人家写的才准备照着这篇文章的思路分析一下源码。首先声明一下，我写的这篇文章是为了记录此问题分析的一个过程，其源码是基于Android 11(Api 30)。

话不多说，现在开始。。。

拨打电话启动的action为android.intent.action.CA]]></description><category></category></item><item><title><![CDATA[Android ConfigChanges属性简介]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/114255083</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/114255083</guid><author>zhourui_1021</author><pubDate>Mon, 01 Mar 2021 11:05:15 +0800</pubDate><description><![CDATA[我们知道在运行时发生配置变更时，默认情况下会关闭Activity并将其重启，但是使用该属性声明配置将阻止Activity重启。相反，Activity会保持运行状态，并且系统会调用其onConfigurationChanged()方法。任何或所有下列字符串均是该属性的有效值。若有多个值，则使用 "|" 进行分隔，例如："locale|navigation|orientation"。

值
			描述
		"density"
			
			显示密度发生变更 - 用户可能已指定不同的显示比例，或者有不同的显示现处]]></description><category></category></item><item><title><![CDATA[Https单向和双向认证过程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/107198624</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/107198624</guid><author>zhourui_1021</author><pubDate>Wed, 08 Jul 2020 09:57:21 +0800</pubDate><description><![CDATA[一、Http
HyperText Transfer Protocol，超文本传输协议，是互联网上使用最广泛的一种协议，所有WWW文件必须遵循的标准。HTTP协议传...]]></description><category></category></item><item><title><![CDATA[TCP的基础知识]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/106752259</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/106752259</guid><author>zhourui_1021</author><pubDate>Wed, 17 Jun 2020 21:31:24 +0800</pubDate><description><![CDATA[TCP/IP的分层管理

TCP/IP协议族里最重要的一点就是分层。TCP/IP协议族按层次分别分为以下4层：应用层、传输层、网络层和数据链路层。

把TCP/IP层次化是有好处的。比如，如果互联网只由一个协议统筹，某个地方需要改变设计时，就必须要把所有部分整体替换掉。而分层之后只需把变动的层替换掉即可。把各层之间的接口部分规划好之后，每个层次内部的设计就能够自由改动了。

值得一提的是，层次化之后，设计也变得相对简单了。处于应用层上的应用可以只考虑分派给自己的任务，而不需要弄清对方在地球上哪个地方、对方的]]></description><category></category></item><item><title><![CDATA[从源码分析RecyclerView的回收和复用机制]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/106244168</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/106244168</guid><author>zhourui_1021</author><pubDate>Sat, 23 May 2020 23:39:00 +0800</pubDate><description><![CDATA[从RecyclerView的名字就可以看出，它的主要作用就是对View的回收。相比较ListView它的优势是它本身不需要关心视图的问题，它不需要关心如何将子View放在合适的位置，不需要关心如何分割这些子View，更不用关心这些子View的外观。它要做的仅仅是回收和复用的工作。现在我们就来看一下如此优秀强大的RecyclerView它负责的工作都是怎样展开的。

在开始之前先来看一下关于RecyclerView的灵魂三问，带着这三个问题去源码中找答案，这样可以更好的理解源码，理解它的工作流程。

问题一：]]></description><category></category></item><item><title><![CDATA[从源码的角度分析Handler]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/105958936</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/105958936</guid><author>zhourui_1021</author><pubDate>Sat, 09 May 2020 21:31:20 +0800</pubDate><description><![CDATA[对于Java开发人员来说多线程和线程间通信是非常重要也是必须要掌握的不可或缺的知识。Android是基于Java语言进行开发的，所以对于Android开发者而言多线程和线程间通信也同样显得极其重要，但是作为Android开发者在开发过程中好像并没有经常的深层次的接触多线程和线程池。这是因为在Android中线程间通信这一重要的知识点已经被Google开发者封装到了Handler中，Handler已...]]></description><category></category></item><item><title><![CDATA[Android Service的绑定过程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/105644942</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/105644942</guid><author>zhourui_1021</author><pubDate>Wed, 22 Apr 2020 21:37:28 +0800</pubDate><description><![CDATA[本文是《Android进阶解密》一书的读书笔记。书中描述的更加详细再结合其他章节的内容可以更加系统的了解这一部分的内容，所以大家可以移步书本。

Service的绑定过程分为两个部分，分别是ContextImpl到AMS的调用过程和Service的绑定过程。

在开始学习之前，先将几个相关的类介绍一下。

ServiceRecord：跟ActivityRecord功能类似，ActivityRecord用来描述一个Activity，相应的ServiceRecord就是用来描述一个Service。

Proce]]></description><category></category></item><item><title><![CDATA[Android Service的启动过程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/105617527</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/105617527</guid><author>zhourui_1021</author><pubDate>Sun, 19 Apr 2020 21:40:14 +0800</pubDate><description><![CDATA[本文是《Android进阶解密》的读书笔记。所以，可能不会为大家提供多少帮助，我写本文的目的只是为了自己再学习的时候能够有个印象，为自己提供一个方便。

Service的启动过程分为两个部分，分别是ContextImpl到ActivityManagerService的调用过程和ActivityThread启动Service的过程。

一、ContextImpl到ActivityManagerService的调用过程

我们在Activity中启动Service首先调用startService方法，它在Con]]></description><category></category></item><item><title><![CDATA[Android根Activity的启动过程]]></title><link>https://blog.youkuaiyun.com/zhourui_1021/article/details/105590528</link><guid>https://blog.youkuaiyun.com/zhourui_1021/article/details/105590528</guid><author>zhourui_1021</author><pubDate>Sat, 18 Apr 2020 19:52:08 +0800</pubDate><description><![CDATA[本文是《Android进阶解密》一书的读书笔记。所以，大家可以直接去买书看看。

现在开始记录根Activity的启动过程。该过程很复杂，讲述的是从手机开机进入桌面之后，点击屏幕上App的应用图标开始，直到App启动后显示的第一个应用程序Activity的过程。可以分为三个部分，分别是Launcher请求AMS的过程，AMS到ApplicationThread的调用过程和ActivityThread启动Activity的过程。

一、Launcher请求AMS的过程

1. 点击应用图标会调用Launche]]></description><category></category></item></channel></rss>