使用 macOS 10.14 编译 Android 9.0

随着移动端设备增量的放缓和市场上各行各业 App 的饱和,连笔者的手机都很久未安装过新 App,更不用提更换手机的频率。与此同时,移动开发也由上半场拼点子拼速度的快捷玩法,进入到下半场拼质量拼生态的高阶玩法。作为一名一线开发人员,大环境的走向与业务形态的发展虽由不得我们控制,但核心技术的掌握则是我们可以身体力行的( 笑~)。 AOSP 是 Android 系统的代码,囊括了 Andorid 系统的全部内容,从应用开发 Framework 层到 Android 虚拟机 ART/Dalvik 层再到 Linux 内核层,所有的疑惑都能在源码中找到答案,其源码的重要性不言而喻。本文作为系列的开篇,主要对 AOSP 的下载、编译过程进行阐述,同时记录下过程中遇到的问题及解决方案。 笔者的系统版本为 macOS 10.14.5,截止文章发布时仍是最新的操作系统,AOSP-android-9.0.0_r42 的源码加编译产物需要 200G 的磁盘空间,请提前预留好磁盘空间。若主机磁盘空间不足,可使用外置移动硬盘来解决。 1. Xcode 的安装 Xcode 包含了编译过程所需的各种工具,如 git、clang、make 等,在编译之前,我们先处理好 Xcode 相关的问题。 截止到2019年6月,AOSP 仍不支持使用 Xcode 10 编译。虽可在 AOSP 配置文件增加 10.14 字段绕过该限制,但后续编译时仍会出现莫名其妙的错误。因此,建议直接使用 Xcode9.4 来避免此类问题。Xcode9.4 的下载地址为Apple Developer Download,登录苹果账号后选中 Xcode 9.4 条目即可看到下载按钮。 若之前已安装 Xcode10 或更高版本,建议在 Xcode9.4 下载成功后进行以下操作: 将原有 Xcode.app 更名为 Xcode10.app 或其他 解压缩 Xcode 9.4 对应的 Xcode.xip 文件,随后将解压缩出的 Xcode....

June 10, 2019

导出七牛云存储的数据

前不久,七牛收回了其测试域名,导致先前创建的外链失效,关联的图片也因此而无法加载。继续使用七牛云的话,需要绑定一个已备案的域名。我没有域名,只能选择将七牛中的数据迁出,本文记录了我的迁出方案,仅供参考。 七牛控制台 登录七牛控制台,看到之前上传的文件都还在,不由松了一口气。 但因七牛提供的测试域名已被收回,这些文件无法直接下载。七牛官方有提供命令行工具 qshell 用来管理七牛云存储中的数据,下面便尝试用 qshell 来取回七牛云存储中的数据。 qshell 操作步骤 下载qshell 设置七牛密钥,AccessKey与SecretKey可在个人中心查看,username即登录名 1 $ ./qshell account $AccessKey $SecretKey $username 查看七牛中的所有的存储空间 1 2 $ ./qshell buckets bucket1 bucket2 查看任意bucket下的所有文件,bucket1即存储空间名称,可在第三步获得 1 2 3 4 5 6 7 8 9 10 11 12 $ ./qshell listbucket bucket1 20180607152835781571470.png 146467 FkomvVSnuHSTqOb5DTP2P-tMfvhu 15283578247522637 image/g> 0 20180607152835853736115.png 275169 FjNQ_IsQ2O1uAJ8GUIWnf4EI_ria 15283585599550264 image/g> 0 20180607152835873357855.png 301775 Fkf66UnqTOE_2qk9JLaK7CmBYGaz 15283587418650741 image/g> 0 20180607152835873635379....

March 19, 2019

Android中同时finish多个Activity - 一次内存泄露的小记

Android开发中,有时需要同时finish多个Activity,避免用户点击后退按钮时回退到不该呈现的Activity。对于这样的问题,常见的方式是维护一个单例ActivityList,将多个Activity置入此单例列表,需要关闭多个Activity时可遍历该List依次执行finish操作。 代码如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class ActivityUtil { private static List<Activity> activityList; private static ActivityUtil instance; private ActivityUtil() { } public static ActivityUtil getInstance() { return null == instance ?...

July 17, 2018

Android MVP架构解析

前言 Android Architecture Blueprints 是谷歌官方开源的一组 Android 架构方案,项目以多个分支分别采用 MVP,MVVM 的概念以及 dagger、rxjava、databinding、livedata 等工具库,演示了一个简单的 TodoApp 在不同架构模式下的代码组织方式。这个项目中的代码非常规范,架构模式也非常有借鉴意义,是一个很有价值的学习素材。 本文将通过 项目组织、架构组织与通信、设计原则 这三个层次对项目中的todo-mvp分支进行解析,从而达到学习的目的。 项目概览 在分析之前,我们先通过截图来了解一下TodoApp的功能。 TodoApp 用来跳转列表页与统计页的抽屉窗口 统计页 列表页 详情页 编辑页 知晓了项目功能后,再来看项目的组织。 项目组织 在开始之前推荐大家先了解一下IDEA符号表,熟悉符号表对理解项目组织有很大帮助。 总览 app/src 除了 androidTest、main、test 这三个常见的模板目录外,还有androidTestMock、mock与prod 目录。androidTestMock 虽不常见,但通过名称可推断它是一个测试相关的目录,另外还有 mock、prod 这两个目录,我们稍后会讲到。 ## UI模块划分 app/src/main/java 下的 addedittask、statistics、taskdetail、tasks 这四个目录分别对应 App 内编辑页、统计页、详情页、列表页这四个模块,每个目录都有自己的 Activity、Fragment与Presenter,分别表示 MVP 中的 **View** 层和 **Presenter** 层,Contract 由字面意义推断为契约接口,此处暂且不表,稍后会在源码中了解其实际内涵。 ## Model层 app/src/main/java 下的 data 目录表示 **Model** 层,Task 是经final修饰的实体类模型,data/source 下有表示数据源接口的 TasksDataSource 与表示数据仓库类的 TasksRepository,data/source/local 与 data/source/remote 分别表示本地数据源与远程数据源,由ToDoDatabase及TastsDao可推断其local部分以数据库的形式实现。 最下方的 BasePresenter、BaseView 分别表示Presenter与View的基类,剩下的utils包用到时再进行查阅即可。...

July 8, 2018

编程马拉松 Day05 堆、二叉堆、堆排序

堆 堆排序需要用到二叉堆,在开始之前,我们先来了解一下什么是二叉堆。 当二叉树满足满足如下条件时,我们说这个二叉树是堆有序的: 每一个父结点的值都比它的子结点大(称为大顶堆)或小(称为小顶堆) 子结点的大小与其左右位置无关 堆有序的二叉树,也可称为二叉堆。二叉堆是最常见的堆结构,因此也常将二叉堆直接称为堆,可以采用如下两种方式来表示二叉堆 使用指针,二叉树的每个结点需存储三个指针,分别指向其父结点和两个子结点 使用数组,对二叉树做层序遍历,按层级顺序放入数组中,根结点在数组索引0的位置存放,其子结点分别在索引1和2的位置,1和2个子结点分别在位置3、4和5、6中存放,以此类推 就排序来讲,其所需处理的数据较为连续,没有空隙,可用完全二叉树来表示。对于完全二叉树,采用数组的表示方法也更方便些,下图展示了采用数组实现的两个二叉堆。 对于数组实现的二叉堆,索引为k的结点的父结点的索引为(k-1)/2,它的子结点的索引分别为2k+1和2k+2。 堆有序化 以大顶堆为例,有序化的过程中我们会遇到两种情况 在堆底加入一个较大元素时,我们需要由下至上恢复堆的顺序 当将根结点替换为一个较小元素时,我们需要由上到下恢复堆的顺序 由下至上的堆有序化(上浮) 如果堆的有序状态因为某个结点变的比它的父结点更大而被打破,就需要通过将它与它的父结点交换来恢复堆有序。交换后,这个结点比它的两个子结点都大,但这个结点仍然可能比它现在的父结点更大。我们可以一遍遍的用同样的方式来将其向上移动,直到遇到一个比它更大的父结点或到达了堆的根结点,如下图所示。 上浮操作对应的代码如下 1 2 3 4 5 6 private void swim(Integer arr[], int k) { while(k > 0 && arr[(k - 1) / 2] < arr[k]) { //若k>0且索引为k的结点大于其父结点时,将该结点与其父结点交换 swap(arr, k, (k - 1) / 2); k = (k - 1) / 2; } } 由上至下的堆有序化(下沉) 如果堆的有序状态因为某个结点变的比它的某个子结点更小而被打破,就需要通过将它和它的子结点中较大者交换位置来恢复堆有序。交换可能会在子结点处继续打破堆的有序状态,此时可以采用相同的方式,将结点向下移动直到它的子结点都比它小或是到达了堆的底部,如下图所示。 下沉操作对应的代码如下...

June 27, 2018

编程马拉松 Day04 希尔排序、归并排序、快速排序

本文将介绍三个高级排序算法 希尔排序 归并排序 快速排序 希尔排序 希尔排序(Shell’s Sort)的名称源于它的发明者Donald Shell,这是一种基于插入排序算法的改进。在处理大规模乱序数组时,插入排序的速度不容乐观,因为它只能一点一点的将元素从数组的一端移动到另一端。希尔排序为了加快速度,对插入排序进行了小幅的改动,开始时将数组划分为m相邻的若干个子数组,并对每一个子数组进行插入排序,然后缩小m的值再次划分并排序,循环往复直到完成m = 1时的最后一次插入排序,此时整个数组有序。 插入排序,每次将第k个元素插入前k-1个元素之间。 希尔排序示例,每次将第mk个元素插入到k,2k,3k…mk这m个元素之间。 希尔排序的思路是使数组中任意间隔为m的元素都是有序的,这样的数组被称为m有序数组。一般m的初始值较大,以便我们可以将元素移动到很远的地方,随着希尔排序的进行,m会逐渐变小,当收敛到1时希尔排序完全退化为插入排序,从而完成最后的排序动作。 希尔排序高效的原因是它完全利用了插入排序的特点: 初始时,m值较大,子数组元素个数较小(小规模数据) 初始前几次,构造出了m有序子数组(部分有序) 希尔排序的过程如下图所示 希尔排序代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void shellSort(Integer arr[]) { long before = System.currentTimeMillis(); int m = 1; //获取的m值,与数组长度相关 //子数组的部分有序的程度取决于m的值,而如何选取m的值是一项数学难题,在这里我们采用`1 4 13 40 121 364`这样的序列来作为希尔排序中m的值。 while (m < arr....

June 24, 2018

编程马拉松 Day03 冒泡排序、选择排序、插入排序

排序是科学计算和数据处理必不可少的一个环节,今天起我们就来聊聊排序。 本文将介绍三个初级排序算法 冒泡排序 选择排序 插入排序 先来看下图这样的一组初始数据,每一个矩形的高度都与其下方的数字成比例,数值越大则矩形的高度就越高。 假设有如下两个问题,我们该如何求解。 找出最(小/大)值 找出第k(小/大)的值 显然,在乱序的数组中这两个问题都不太容易求解,但如果数据是有序的就会容易很多。 冒泡排序 冒泡排序是最容易想到的排序算法。以对N个元素的数组进行升序排序为例,其基本思路如下: 从数组内的前两个元素开始,将这两个元素进行比较,如果前一个元素大于后一个元素,则交换两者的位置 接着取数组中的第2-3个元素进行比较,若第2个元素大于第3个元素,则交换两者的位置 循环往复,直到数组中的最后两个元素,此时,若第N-1个元素大于第N个元素,则交换它们的位置。经过一轮的比较与交换,我们已经得到了数组中最大的元素,并将其安置在了数组的第N位。 经过前三个步骤,我们将数组中最大的元素放到了第N位,下边只用排序数组中的前N-1个元素即可。此时我们将N的值减1,并判断新N的值,若新N>0,则循环1-3步骤;若新N=0,则代表我们已经完成了排序。 冒泡排序的过程(剪辑版)如下图所示,你也可以点击这里查看完整的冒泡排序过程。 我们知道,水杯中出现气泡时,越大的气泡浮力越大,上升速度也就越快,最先到达水面,冒牌排序中每轮遴选较大元素放置末尾的行为与水中气泡上升的现象十分相似,因此得名冒泡排序。 冒泡排序代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void bubbleSort(Integer arr[]) { int compareCount = 0;//比较次数 int swapCount = 0;//交换次数 long before = System.currentTimeMillis(); for (int i = 0; i < arr....

June 23, 2018

编程马拉松 Day02 递归

今天是第二天,继续我们的征程。 题目 编写代码,把字符串中的每个空格替换为%20。例如,输入"hello world.",则输出"hello%20world."。 编写代码,给定系数n,求1+2+3+…+n的总和,即∑运算符 编写代码,观察如下数列,给定系数n,求数列中的第n个数字(tips: 斐波那契数列)。 1 1 2 3 5 8 13 21 34 55 89 ... 字符替换 本题是将空格等特殊字符变为转义字符的函数,常用于URL编码中,用来避免URL中可能存在的字符歧义。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 /** * 判断当前字符是否为普通字符 */ public static boolean isPlainChar(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '!...

June 21, 2018

编程马拉松 Day01 面试题小记

打算从今天起开始每日一练,巩固一下算法,数据结构相关的知识,废话少说,开始看题。 以上是我近期面试中遇到的一些题,其中1,3题出自百度系面试官;2,4出自Uber系面试官。 两个水杯倒出3L水的问题 先来看第一个问题,题干是要求倒腾出3L的水。我们可以从4、5两个数字入手,观察通过怎样的方式能够得到3,得到以下两种情况 4 * 2 -5 == 3 ( 5 - 4 ) * 3 == 3 转换为文字 第一种方式 先将4L的杯子接满水,全部倒入到5L的杯子中(tips:此时4L的杯子为空,5L的杯子中装载了4L水,还能再倒入1L) 再将4L的杯子接满水,然后向5L的杯子倒水,当5L杯子倒满时,4L杯子剩下3L水 第二种方式 将5L的杯子接满水,倒入4L的杯子中(tips: 此时5L的杯子剩下1L水),然后将4L杯子的水全部倒掉,再将5L杯子剩余的1L水倒入4L的杯子(tips: 此时4L杯子有1L水) 将5L的杯子接满水,倒入4L的杯子中(tips: 由于上一轮4L杯子已存在1L水,本次5L杯最多只能倒出3L水,倒出后5L杯剩余2L水),然后将4L杯子的水全部的倒掉,再将5L杯子剩余的2L水倒入到4L的杯子(tips: 此时4L杯子有2L水) 将5L的杯子接满水,倒入4L的杯子中(tips: 由于上一轮4L杯子已存在2L水,本次5L杯最多只能倒出2L水,倒出后5L杯正好剩余3L水) 大数四则运算 第二个问题是一个典型的大数运算问题。编程中有时会遇到数字上溢(tips: 如数值的大小超过了Long型可表示的最大数)的问题,此时便需要采用字符串替换原有的数值计算。 以10进制加法为例,解决思路如下: 使用字符串装载参与计算的两个数值 分别取两个字符串的末尾数值 a和b,进行相加计算 sum = a+b ,如结果sum大于9,则设置进位标志 flag = 1;否则设置 flag = 0。记录sum%10,表示当前位的结果 再次取两个字符串的次末尾数值 a和b,进行相加计算 sum = a+b+flag ,如结果sum大于9,则设置进位标志 flag = 1;否则设置 flag = 0。记录sum%10,表示当前位的结果 … 重复以上步骤,若两个数字字符串的长度不一致,则当其中较短字符串s1遍历完成后,只遍历另一个字符串s2,每次取出s2的末位数值与flag相加,得到新的进制标志和当前位的结果。 代码如下:...

June 20, 2018

IntelliJ IDEA 断点调试时查看所有变量

在看Java HashMap工作原理及实现这篇文章时,发现博主在断点模式下来观察hash冲突的效果挺好的,随后便想在IntelliJ IDEA中也试下。 我们先来看下HashMap.Node的代码,有4个成员变量。 1 2 3 4 5 6 7 static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; //省略后续内容 } 如下图所示,对于HashMap.Node类型的对象,IDEA默认只显示了key,value这两个成员变量。 原因在于IDEA默认将HashMap.Node视为了Map.Entry,那么如何在Debug时以HashMap.Node的类型来查看该对象其他变量呢? 方法有二: 一. 在debugger的Variables面板中,右键该对象,找到View As选项,选择其中的Object,然后就可以看到该对象的所有属性了。 二. 采用第一种方式时,每次Debug都需要手动指定其类型,较为繁琐。对于常用的类,我们也可以在View as时点击Create按钮,为其新建类型。这样的话以后每次Debug时,无需任何设置就能在Variables面板内直接看到其准确类型的所有属性。 Create过程也很简单,在Java Type Renders窗口内,设置HashMap$Node对应的完整类名即可,如下图所示。 最后看一下,在View as中Create了HashMap及HashMap.Node后的效果。 现在已经能看到类中的所有成员变量了,那么对于静态变量该如何查看呢?这个也很简单,同样在此处右键,点击Customize Data Views… 然后在Static fields和Static final fields的选项前打勾即可~ 以HashMap为例,在选中了Static fields和Static final fields的选项后,所有的静态变量也显示出来了 :)

June 7, 2018