Keep Velocity High | kvh 的个人博客

分享 kvh 对于技术、创业的理解和实践

0%

介绍

在项目中第三方代码的时候,要注意源码许可证问题,因为这涉及到:

  • 使用和修改的条件是什么?
  • 商用和分发的条件是什么?
  • 本项目开源后使用什么许可证?

本文将简要介绍对许可证(license)的理解,着重介绍一个实战项目处理方式。

许可证介绍

许可证的内容,其实是博大精深。

首先许可证的种类特别的多,其次不同的许可的条款和细节也特别的多。

下面主要是介绍若干主流且具有代表性的许可证。

主要参考了开源许可证教程-阮一峰的网络日志

名词解释

  • 分发

    将版权作品从一个人转移到另外一个人;例如提供给别人;如果是本公司使用,不算分发。

  • 传染

    一个项目用到了某个许可证的代码,在某些条件下(例如分发),这个项目本身也需要使用这种许可证

  • 许可证版本

    同一个许可证,有版本号之间的区别,例如 GPL 有 v1/v2/v3。

宽松的许可证

包含 MIT/ISC/BSD/Apache等,基本上这类许可证,是可以放心使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(1)BSD(二条款版)

分发软件时,必须保留原始的许可证声明。

(2) BSD(三条款版)

分发软件时,必须保留原始的许可证声明。不得使用原始作者的名字为软件促销。

(3)MIT

分发软件时,必须保留原始的许可证声明,与 BSD(二条款版)基本一致。

(4)Apache 2

分发软件时,必须保留原始的许可证声明。凡是修改过的文件,必须向用户说明该文件修改过;没有修改过的文件,必须保持许可证不变。

Copyleft 许可证

代码可以随意复制,有如下前提:

1
2
3
- 如果分发二进制格式,必须提供源码
- 修改后的源码,必须与修改前保持许可证一致
- 不得在原始许可证以外,附加其他限制

核心在于,修改后的代码,不得闭源。

  • AGPL

最严格的 GPL,除非获得商业授权,否则无论以何种方式修改或者使用代码,都需要开源。

云服务使用AGPL 的源码,不构成分发,也也需要开源

  • GPL

如果分发软件,则使用和修改都必须开源,整个项目都必须采用 GPL许可。

  • LGPL

分发时,如果是使用动态类库的方式引用,可以不开源

  • MPL

分发时,只要该许可证的代码在单独的文件中,新增的其他文件可以不用开源。

无论是商业应用,还是开源项目,在采用 AGPL/GPL/LGPL/MPL都要特别的小心。

许可证检测

介绍两个工具,分别检测代码文件和依赖的 license。

检测代码

1
go get -u github.com/google/addlicense

可以递归检测当前目录下的所有代码文件,许可证声明的情况,例如:

1
addlicense -check ./ |sort > lc.txt

可以把未添加许可证的文件都输出到lc.txt 文件中,方便后续进行检查。

检测依赖库

1
go get -u github.com/google/go-licenses

查看一个依赖库所使用的版权信息:

1
go-licenses csv repo

可以通过一个这个脚本项目进行批量测试:

1
2
3
4
5
6
cat dep.txt |while read line
do
echo "start checking package $line"
go-licenses csv $line
echo ""
done

许可证添加

笔者主要使用 idea 的 IDE 进行程序开发,它有管理版权信息的功能:

1
Preference->editor->Copyright

可以添加不同的 Profiles,然后使用 IDE的功能进行自动添加和更新。

1
2
3
4
5
6
7
1)设置默认 copyright
preference->editor->Copyright
设置 default project copyright,选中一种配置,这个可能会根据不同的文件需要切换

2)选中代码文件或者文件夹

3)菜单栏->Code->Update Copyright

项目实战

下文介绍对于一个实际项目进行许可证声明。

项目背景

该项目主语言是 golang,采用了自某 LGPL 的项目的一部分类库作为基础代码。

版权主体

也就是版权声明的第一行的关键信息,例如:

  • go-ethereum
1
Copyright 2016 The go-ethereum Authors
  • go-algorand
1
Copyright (C) 2019-2020 Algorand, Inc.

可以选择是作者主体,或者公司主体。

项目 license

由于该项目的代码,部分使用了修改了 LGPL 项目代码作为基础工具。

根据 LGPL 的规定,本项目也需要使用 LGPL 作为 license。

版权声明

声明版权,一般两部分:

1)项目级别的 LICENSE 文件

由于大部分的许可证都是控制包级别的许可,所以这种方式也足够

每一个原创的文件都设置了版权信息

项目级别的 license

一般而言,是找到你的 license 的文本,将文件放置于项目根目录。

不同许可证可能有点不一样

一般而言,在 github 建立公开项目的时候,会提示选择一个 license。

如果初始化忘记了,也可以web 界面上,新建一个名字是LICENSE的文件,来显式触发选择 license。

使用第三方代码,主要分为,fork 引用library 依赖

fork 是指把代码直接放到了代码库中,修改或者不修改。

library 是库依赖,例如通过 go.mod 的方式进行依赖。

fork 引用

代码分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1)项目原创文件

2)项目修改第三方库的文件

3)项目直接引用第三方库的文件

4)fork 过来的开源库
可能存在包内 license,单文件可能没有copyright

5)忽略 license 文件的内容

非 golang 文件
gencode 生成的以 gen_开头的文件
其他自动生成的文件

有了如上的分类,你就可以使用 IDE 创建多个不同的 Copyright Profiles 来处理不同的文件了。

结合 addlicense 工具,相信这部分工作将可以顺利完成。

library 依赖

使用上述的依赖检测工具,查看是否有不合适的许可证。

如果一个依赖库,license 不是MIT/ISC/BSD/Apache 其中的一种,那就要小心了!

TBD-Topics

有一些话题需要进一步讨论:

1
1)如果项目源码计划商用,如何处理?

总结

本文简要介绍了license 的原理,着重介绍了项目实战,希望对读者有启发,欢迎讨论!

参考

https://www.ruanyifeng.com/blog/2017/10/open-source-license-tutorial.html

https://www.gnu.org/licenses/gpl-faq.html

https://choosealicense.com/

欢迎添加博主社交账号,进群交流。

个人微信

kvdoth

请备注:博客

个人QQ

39442811

请备注:博客

个人公众号

KVHTalk

因为业务需要,我司有若干台服务器 Mac 作为服务器。下面总结一下注意事项,作为记录。

取消系统休眠

曾经在这个问题上困扰很长时间。在升级了 macOS High Sierra 之后,发现之前能够正常运行的程序出现问题。

具体表现是:

  1. ssh 登出之后不久,线上就开始报警
  2. 登陆进去查看 log,发现程序又是正常的
  3. socket 连接提示传输错误
  4. 程序并没有 exit

一开始怀疑是后台程序的权限问题,于是更新了后台程序的 launchd 配置,确保了使用 root 用户运行,没有效果。

后来灵机一动,去具体分析程序的 log,发现只要用户登出,输出就会停止,看起来是程序被挂起了。

分析可能被挂起的原因,只可能是系统休眠了。

直接登陆 GUI 系统,在系统设置中将节能设置改成:

节能设置

生效了!

守护进程

作为服务器,大多是需要运行守护进程。这就需要用到 launchd,它能确保这是确保设备重启之后,守护能够自动运行。

launchd 是一个开源框架,用于控制守护进程、程序、或者脚本,在 Mac OS X Tiger 中引入。

使用 launchd,主要需要注意下面几点:

  • 正确区分系统全局的守护和用户相关的守护进程
类型 路径 运行用户
全局的守护进程 /Library/LaunchDaemons root 用户或者使用 UserName key 对应的用户
系统的守护进程 /System/Library/LaunchDaemons root 用户或者使用

一般而言,把 plist 文件放到 /Library/LaunchDaemons 即可。

  • 正确设置 plist 文件的权限
1
-rw-r--r--  1 wheel  947 12  1 14:51 name_of_the_config.plist

也即是:

1
2
3
用户:root
组:wheel
权限: 0644
  • 被运行的程序,需要是处于前台运行,否则 launchd 会认为程序已经退出
  • 如果需要全局性的守护进程,需要使用 sudo 运行

参考文档

http://www.launchd.info/

launchd.plist

launchctl

内网穿透

很多时候,macOS 服务器并不是托管在机房,而是在某个比较方便管理的地方——例如办公室。

办公室的网络环境又千奇百怪,往往没有固定的外网 ip,往往是处于 NAT 的网络环境或者是防火墙内,如果服务出现了问题,总不能直接跑去办公室吧。

尝试过各种 VPN 方案,动静太大,不稳定,放弃。下面介绍一下 Ngrok

官网简介:

Secure tunnels to localhost ”I want to expose a local server behind a NAT or firewall to the internet.”

基本的构架图如下:

Ngrok

这款软件是 C/S 结构,使用服务器作为中转,只需要设置好服务器和客户端,不需要在路由器做任何处理。软件的2.x 不开源,github 上只有 1.x 版本,已经足够。

使用方式,参考 imququ 的博客

基本流程是:

  1. clone 源码
  2. 生成证书,拷贝 base.pemassets/client/tls/ngrokroot.crt
  3. 运行 make release-server release-client
  4. bin 的 ngrokd 和 ngrok 分别是服务器端和客户端

总结

以上是对于 macOS 作为服务器的一些注意事项。

闲话配置

老司机都喜欢在程序设计中尽可能的把各种参数做成可配置的,等到产品需求发生改动的时候,优雅的修改一行配置,重新加载一次配置,就满足了需求。

配置(Configuration)是不修改代码的情况下,对程序的运行调整的能力。

简单来讲,配置可以分成两类:

  1. 系统配置:包括线程池大小、数据库连接等,变化频率较低
  2. 业务配置:功能开关,功能参数等,变化较为频繁

程序的配置一般而言,分为几个环境:

  1. 开发环境
  2. 测试环境
  3. 生产环境

这几个环境的配置是有差异的,所以配置管理功能应当能够根据当前环境读取对应的配置。

前面提到的不修改代码,只是最低要求,相当多的情况下,程序是不能停机的,这就提出了热更新的需求。

另外,配置的修改应该是有记录可追溯的。

Spring Boot 的配置管理

Spring 实现了非常友好的配置读取方式,

Spring Boot 程序默认使用 application.properties 进行配置。

例如一个参数:

1
flag=0

在程序中可以通过:

1
2
@Value("${flag})
int flag;

来读取。

也可以通过设置多个配置文件:

1
2
3
4
application.properties
application-dev.properties
application-test.properties
application-prod.properties

application-dev.properties

1
flag=1

application-test.properties

1
flag=2

application-prod.properties

1
flag=3

和在 application.properties 中声明当前的活跃的 profile:

1
spring.profiles.active=dev

来实现根据运行环境切换配置信息。

以上只是 Spring 强大的配置能力的冰山一角,有兴趣可以参考外部配置文档

这还不够

其实以上描述的 Spring Boot 的配置能力已经很强了,但是有个很致命的问题——无法热更新。

如果非得实现热更新,那可以把配置做成 JSON 文件,再实现一个 endpoint,重新读取一次配置。

假设有一千个实例呢?

Spring Cloud Config

项目 github:https://github.com/spring-cloud/spring-cloud-config

简介:External configuration (server and client) for Spring Cloud

这个项目包含两部分:

  • Server

负责从 git/svn 等版本管理系统中读取配置,并以 http 的方式提供服务。

  • Client

根据客户端配置,从指定的 Server 中读取对应配置,并且与 Spring 本身的 PropertyResource 和 Environment 无缝结合。同时,提供了统一的方式进行配置热更新。

对于这个系统的基础配置和运行,可以参阅下面两个 url:

https://spring.io/guides/gs/centralized-configuration/

http://blog.didispace.com/spring-cloud-starter-dalston-3/

实操进阶

热更新

Spring Cloud Config 实现了非常优雅的热更新。

  • 客户端的配置 Bean 添加 @RefreshScope
1
2
3
4
5
6
7
8
9
10
@Configuration
@RefreshScope
public class ConsumeConfig {

@Value("${consume.desc}")
private String consumeDesc;

@Value("#{'${api.server.round.card}'.split(',')}")
private List<String> cardList;
}

在配置这个注解之后,调用客户端的 /refresh endpoint,这个 Bean 就会刷新,同时个 Bean 的依赖方下次方法调用时也会更新 Bean 引用。

有些时候,你需要监听这个更新事件,把拿到的最新配置,重新初始化一些部件。那你可以添加:
@EventListener(EnvironmentChangeEvent.class) 这个 annotation。

1
2
3
4
5
6
7
8
9
10
11
@Component
public class ConsumeService {

@Autowired
ConsumeConfig consumeConfig;

@EventListener(EnvironmentChangeEvent.class)
void onEnvChange() {
//do re-init stuff
}
}

添加权限

Config Server 任何人都可以通过 http 访问配置,这个不大好,建议加上认证,最简单是使用 Spring-Security 添加一个 basic authentication。

  • 服务器端配置

build.gradle 添加依赖:

1
2
3
dependencies {
compile('org.springframework.boot:spring-boot-starter-security')
}

application.properties 添加密码(用户名默认是 user):

1
security.user.password=xxx

重启服务器,再次通过 http 访问配置的时候需要验证。

  • 客户端配置

服务器端做了验证,那客户端也需要添加相应的配置:

bootstrap.yml

1
2
3
4
5
6
7
8
9
spring:
application:
name: application
cloud:
config:
uri: http://yourhost.com
profile: dev
username: user
password: xxx

加载多组配置文件

很多时候,为了避免 application.properties 过于臃肿,你可能需要把一些配置文件拆出来,例如专门负责邀请奖励的配置:

1
2
3
4
invite.properties
invite-dev.properties
invite-test.properties
invite-prod.properties

同样,也区分了多个环境。

那在使用 Spring Cloud 的时候如何读取这个文件呢?

1
2
3
spring:
application:
name: application,invite

注意上面的 application.name 是以逗号分隔的两组配置名称。

一些问题

@EventListener 与 SpEL

我在实际使用中发现,在事件监听函数中,使用更新后的配置的时候:

1
2
3
4
5
@Value("${consume.desc}")
private String consumeDesc;

@Value("#{'${api.server.round.card}'.split(',')}")
private List<String> cardList;

第一个配置是使用 @Value 绑定一个字符串类型,第二个配置是使用 @Value 中的 SpEL 去将配置中的字符串,切割成 List。

我发现第二个,无法切割成功。试验了多次,还没有找到答案。我已经在 Spring Cloud Config 的 github 上提交了 issue

如果有了解这个的,请不吝赐教。

update 2017-10-26

权限管理

在不做二次开发的情况下,这个配置中心的数据是对所有的 client 开放的。某些情况下,这种设定并不合适。

JSON 文件的读取和解析

Spring Cloud Config 本身是可以通过 http 来提供 JSON 文件的访问的,但是Spring Boot 原生并不支持 JSON 配置的读取和解析。

相关选择

其实配置管理工具,选择还是不少的,下面列举一下。

配置文件管理,可以热更新,跟 Spring 没有绑定。

专注管理配置文件,也可以实现热更,跟 Spring 没有绑定。

携程开源的,功能很全

百度的一位工程师的开源项目,功能也很全

来自阿里的开源,有些日子了

参考文献

http://blog.didispace.com/spring-cloud-starter-dalston-3/

http://cloud.spring.io/spring-cloud-config/single/spring-cloud-config.html

http://jm.taobao.org/2016/09/28/an-article-about-config-center/

https://blog.coding.net/blog/spring-cloud-config

家有设计师

我家夫人是一位设计师,平时少不了将一张大图导出成多种规格的小图的脏活累活。

看了她导了好几次,实在不能忍。得发挥程序员的能动性,做个小工具。

调研

imagemagick 是一个很好的工具,支持多个平台,功能丰富强大。考虑了一下,如果这个小工具还需要用户安装依赖的话,显然提高了使用门槛。

设计师们大多在 MacOS 下工作,最好是运用这个平台下现成的工具包。搜索了一下,发现今天的主角:sips

sips 具有很友好的使用接口,可以实现大小转换、旋转、翻转等。这里我只需要大小转换功能,主要是如下命令:

1
sips -Z 100 input.png --out output.png

上面的例子可以将 input.png 图片最大边调整成100像素,等比缩放。

实践

整个工具包含如下几个文件:

1
2
3
4
.
├── convert.sh//转换程序
├── icon.png//输入文件
└── sizes.txt//目标尺寸

使用步骤如下:

  1. 替换 icon.png 文件为需要转换的文件
  2. 修改 sizes.txt 文件,编辑目标尺寸,每一行为一个尺寸
  3. 控制台运行 ./convert.sh 即可

为了很方便的让设计师同学使用这个工具,我在她的电脑上安装了一个 cdto 工具,可以在当前 finder 目录下打开 控制台。还把这个工具拖到了 finder 的左侧快捷方式。PS: 用户接口要友好。。。

源码

源码和配置文件都放在 github 上。

TODO

  1. 目前只支持 png

智能家居

大约20年前,在报纸上看到关于比尔盖茨智能豪宅的报道,心生向往。我等平民在20年后,终于迎来了智能家居浪潮普及的浪潮。

亚马逊的 Alexa Echo 无疑是这个浪潮中的当红明星。

它是一个蓝牙音箱,是一个智能语音助手,是一个智能家居管理中心,它还提供了开放平台,允许第三方添加 Skills 来扩充功能。

本篇介绍一些购买和基本使用经验。

购买

在经过几代的发展之后,目前这个产品主要包含 EchoEcho Dot。主要的区别是扬声器的规格。

如下图:
Amazon-Echo

Amazon-Echo-Dot

Echo Dot 价格要亲民一些,500人民币左右,某宝可购买。

下单后很快到货,就是这么一个小盒子:

Echo-Dot-实物

基础介绍

顶部有四个按钮,功能分别是,分别是:静音、操控、音量加减。

背面有两个插槽,分别是 3.5 mm 声频输出口、Micro-USB 电源插口。

Echo Dot 需要一个叫做 Alexa App的手机软件作为控制端,下面提供 Android 和 iOS 平台的下载地址。

Android-App

iOS-App

注意在 iOS 平台上,这个 App 只允许美国区的账户进行下载。你可以折腾一下,注册一个美国区的 Apple ID,可能需要绑定信用卡。你也可以花钱买时间,在万能的某宝上买一个美国区账号。我选择了后者。

基础设置

Echo Dot 是需要连接到 WiFi 进行工作的,这就需要用 Alexa App 进行绑定和设置。

如果你曾经用过小米的空气净化器,你会发现设置的模式类似。

主要步骤为:EchoDot 进入设定模式、手机连上 Echo Dot 共享的 WiFi、操作 Echo Dot 连接家里的 WiFi。

1.长按 Echo Dot 顶部的操控键,会有提示进入设定模式(enter setup mode);

2.在手机的 WiFi 设置页面,可以看到名字为 Amazon-XXX 的 WiFi,连接上去,会有提示:(connected);

3.进入 Alexa App,在里面选择家里的 WiFi,输入密码,确定之后,Echo Dot 会尝试进行连接。

连接中,主要发现两个问题,一是卡,二是根本连不上。因为 Alexa 的服务器在国外,速度受一定的影响,卡是正常的。然而国内的网络环境比较复杂,例如我就遇到过家里的方正宽带下的 WiFi,根本连不上的情况。后来我只能使用手机的联通4G 开热点才能勉强使用。公司的电信宽带也没有问题。后面网络续费的时候,看来要换成电信或者联通了。

基础使用

Echo Dot 连接上网络之后,你就可以跟它对话了。默认唤醒口令是 Alexa。唤醒之后灯会亮。

例如就可以试一下,Alexa, what's the weather。它可能会告诉你美国西雅图现在的天气,地址可以在 Alexa App 中设置。(ps:我还没有找到设置成北京的方式)

一些后续

Alexa Echo 提供了开放平台,开发者可以将服务打包成 Skills 对系统进行扩展,这个就有点像苹果的 iOS 生态了。厂商只提供基础的服务和功能,开发者百花齐放。用户可以在 Alexa App 中添加 Skills,例如你可以添加一个读 CNN 新闻的功能。这一块还有待探索。

网络问题很严重,反应速度比较慢。后面如果想要愉快玩耍,还是得找一个比较好的联网方案。某一天亚马逊可以把服务器放一些到国内,就更好了。

总结

Alexa Echo Dot 入门初步介绍了购买、设置、使用。下面继续探索。

今天在最喜欢的陈老师的《待字闺中》那里读到一首诗,纪伯伦(Kahlil Gibran)的《孩子》,很有感触。将冰心的译文和原文奉上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
孩  子

你们的孩子,都不是你们的孩子。
乃是生命为自己所渴望的儿女。
他们是凭借你们而来,却不是从你们而来,
他们虽和你们同在,却不属于你们。

你们可以给他们以爱,却不可给他们以思想。
因为他们有自己的思想。
你们可以荫庇他们的身体,却不能荫庇他们的灵魂。
因为他们的灵魂,是住在明日的宅中,那是你们在梦中也不能想见的。
你们可以努力去模仿他们,却不能使他们来象你们。
因为生命是不倒行的,也不与昨日一同停留。
你们是弓,你们的孩子是从弦上发出的生命的箭矢。
那射者在无穷之中看定了目标,也用神力将你们引满,使他的箭矢迅速而遥远地射了出去。
让你们在射者手中的弯曲成为喜乐罢;
因为他爱那飞出的箭,也爱了那静止的弓。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
On Children

Your children are not your children.
They are the sons and daughters of Life's longing for itself.
They come through you but not from you,
And though they are with you yet they belong not to you.

You may give them your love but not your thoughts,
For they have their own thoughts.
You may house their bodies but not their souls,
For their souls dwell in the house of tomorrow,
which you cannot visit, not even in your dreams.
You may strive to be like them,
but seek not to make them like you.
For life goes not backward nor tarries with yesterday.

You are the bows from which your children
as living arrows are sent forth.
The archer sees the mark upon the path of the infinite,
and He bends you with His might
that His arrows may go swift and far.
Let your bending in the archer's hand be for gladness;
For even as He loves the arrow that flies,
so He loves also the bow that is stable.

这首诗,来自纪伯伦的名作《先知》。

相信每一个人,在不同的阶段,读到这首诗的感受,是不一样的。

年少的时候,觉得父母老师管束自己,想要自由,可能会以此诗句来抗争——看,纪伯伦说了……。

长大了,离开父母上学,父母还是希望给更多的意见,你却总觉得父母的意见不合时宜。

工作了,经济自由了,在享受自由的那一瞬间,却开始想父母了。你慢慢的发现,自己身上的思想与习惯,很多都是源自来自父母。

若干年后,父母在你该找谁结婚这事儿上,干预未果,你想,抗争终于成功了。

不久,你也终于为人父母,才感叹,当父母真不容易。既当弓,又为箭。

再过若干年,自己的孩子也到了叛逆期,你还是希望像当年自己的父母一样,把自己曾经犯过的错,提前告诉孩子。

你却发现,每个人该犯的错误,该趟的坑,一个都少不了。你,却只能在旁边看着,就像当年你的父母一样。

孩子终于活出了自己的世界,你发现,原来每一个人都有自己的命运,只要努力,还是有机会。

每个人的每个阶段,都有自己的局限和使命。努力,活在当下就好。

笔者在开发和维护 Bugtags SDK 的一年多时间里面,趟过了不少坑,走了不少弯路,也积累了不少经验。这个 SDK 开发最佳实践系列,就是分享经验教训,帮助读者打造一款跟 Bugtags 这样快速成长的 SDK。

SDK 的使用场景

站在用户的角度来看,引入一个 SDK 可能会带来几方面的问题:

  • 包增大
  • 可能不稳定
  • 安全性问题

对于一些 SDK,产品形态是允许在测试阶段开启,上线移除的,例如性能调试工具,测试工具等。

区分版本方案

最简单的,就是手动注释代码,应用中如果只使用了这个 SDK 的一个 API,那还好,如果用到了多个,分布在不同的文件中,
这事儿就麻烦了。很不优雅。

最优雅的方案

思考一下这个需求

  • 应用分为 debug/release 版本
  • 无痛切换,最好能够实现自动化

大部分的编译系统,例如 Gradle/Maven 都可以区分版本,引入不同的包。利用这个特性,我们可以实现一个 No-Op SDK。

这个 SDK 具有如下特点:

  • 具有正式 SDK 的所有可以被外部引用的类
  • 具有正式 SDK 的所有对外 API
  • 足够的精简
  • 提示正在使用 No-Op SDK

最后的结果

以 bugtags 为例:

1
2
debugCompile 'com.bugtags.library:bugtags-lib:2.0.0'
releaseCompile 'com.bugtags.library:bugtags-noop:2.0.0'

以 leakcanary 为例:

1
2
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'

扩展

如果你是一个 SDK 的使用者,即便官方没有提供 No-Op 版本,你完全可以自己写一个。

上个阶段忙一些个人事务,更新的少了。这段时间会集中发一些个人学习心得。

收到邮件

21号收到一封 Google Play 发来的邮件,告知如下信息:

1
2
3
4
5
Hello Google Play Developer,
We're writing to let you know that the apps listed at the end of this email may be affected by an upcoming platform change.
Action required: If your app requires GPS hardware to operate properly, you will need to explicitly add the "android.hardware.location.gps" uses-feature to your manifest.

...

大概意思是:

1
2
3
4
5
对于 `target sdk >= 21(5.0)` 的 APK 包,使用 `ACCESS_FINE_LOCATION` 权限的时候,并不会隐含的使用 `android.hardware.location.gps` 这个 feature。

可能带来的后果是,一些没有 GPG 芯片的设备,也可以安装这个 APK,导致用户体验就很不好。

需要做的是,显式的声明对这个权限的使用。

做做功课

翻了下关于这个权限的文档:

总结一下

  • uses-feature 这个机制,主要是方便 APP 商店过滤和匹配设备与 APK。

  • 官方希望大家显式的声明需要用到的硬件或者软件 feature,但是做了隐含的声明机制。

    例如在使用 ACCESS_WIFI_STATE 这个权限的时候,会隐含的加入 android.hardware.wifi,这一点,可以使用如下命令来确认:

    1
    aapt dump bading <apk-path>
  • target sdk >= 21(5.0) 有了些改变,ACCESS_COARSE_LOCATION 不再隐含加入 android.hardware.location.networkACCESS_FINE_LOCATION 不再隐含加入 android.hardware.location.gps

一点疑问

对于使用 aapt dum badging 命令得出结果,有一点其实有疑问:

  • 编译条件:

    1
    2
    3
    4
    5
    compileSdkVersion 23
    buildToolsVersion '23.0.2'

    minSdkVersion 14
    targetSdkVersion 23
  • dump 命令

    1
    ~/sdk/build-tools/23.0.1/aapt dum badging dev-com-debug.apk
  • dump 结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    uses-feature: name='android.hardware.location'
    uses-implied-feature: name='android.hardware.location' reason='requested android.permission.ACCESS_COARSE_LOCATION permission, and requested android.permission.ACCESS_FINE_LOCATION permission'
    uses-feature: name='android.hardware.location.gps'
    uses-implied-feature: name='android.hardware.location.gps' reason='requested android.permission.ACCESS_FINE_LOCATION permission'
    uses-feature: name='android.hardware.location.network'
    uses-implied-feature: name='android.hardware.location.network' reason='requested android.permission.ACCESS_COARSE_LOCATION permission'
    uses-feature: name='android.hardware.touchscreen'
    uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
    uses-feature: name='android.hardware.wifi'
    uses-implied-feature: name='android.hardware.wifi' reason='requested android.permission.ACCESS_WIFI_STATE permission'

    为什么这里面还是隐含使用了 android.hardware.location.gps 等 feature?可能 aapt 的 print 规则和最新的现在规则不一致?

要做的改变

  • 这个机制只会影响 target sdk >= 21(5.0) 的 APK
  • 最好显式的加入 uses-feature,否则 Google Play 会认为你不使用某些 feature,那就允许不具备某些 feature 的硬件安装这个 APK,可能会囧
  • 在声明 uses-feature 的时候,注意 required,在不显式设置的情况下,是 true。true 代表只有这个 feature 才能安装,false 代表没有也 OK

Bugtags 新增了下面两个 uses-feature

1
2
3
4
5
6
<uses-feature
android:name="android.hardware.location.gps"
android:required="false" />
<uses-feature
android:name="android.hardware.location.network"
android:required="false" />

8月7号,应北京 GDG 邀请,我做了一个题为《Android Gradle 构建系统·初探》的分享。

主要内容是构建系统背景知识,源码库,代码亮点,写插件。

很明显这是一个很大的话题,有超过 2.5GB 的核心代码和超过50个子项目。

第一步,我进行了宏观的介绍。下一步,我会继续深入子项目,尝试发现更多有意思的点。

对这个话题感兴趣的,请加入技术交流 qq 群:583688711。

Slides 在下面,分别是 PDF 版本和图片版本。

PDF here.

图片:

Android-Gradle-Build-System-Overview.001.jpeg
Android-Gradle-Build-System-Overview.002.jpeg
Android-Gradle-Build-System-Overview.003.jpeg
Android-Gradle-Build-System-Overview.004.jpeg
Android-Gradle-Build-System-Overview.005.jpeg
Android-Gradle-Build-System-Overview.006.jpeg
Android-Gradle-Build-System-Overview.007.jpeg
Android-Gradle-Build-System-Overview.008.jpeg
Android-Gradle-Build-System-Overview.009.jpeg
Android-Gradle-Build-System-Overview.010.jpeg
Android-Gradle-Build-System-Overview.011.jpeg
Android-Gradle-Build-System-Overview.012.jpeg
Android-Gradle-Build-System-Overview.013.jpeg
Android-Gradle-Build-System-Overview.014.jpeg
Android-Gradle-Build-System-Overview.015.jpeg
Android-Gradle-Build-System-Overview.016.jpeg
Android-Gradle-Build-System-Overview.017.jpeg
Android-Gradle-Build-System-Overview.018.jpeg
Android-Gradle-Build-System-Overview.019.jpeg
Android-Gradle-Build-System-Overview.020.jpeg
Android-Gradle-Build-System-Overview.021.jpeg
Android-Gradle-Build-System-Overview.022.jpeg
Android-Gradle-Build-System-Overview.023.jpeg
Android-Gradle-Build-System-Overview.024.jpeg
Android-Gradle-Build-System-Overview.025.jpeg
Android-Gradle-Build-System-Overview.026.jpeg
Android-Gradle-Build-System-Overview.027.jpeg
Android-Gradle-Build-System-Overview.028.jpeg
Android-Gradle-Build-System-Overview.029.jpeg
Android-Gradle-Build-System-Overview.030.jpeg
Android-Gradle-Build-System-Overview.031.jpeg
Android-Gradle-Build-System-Overview.032.jpeg
Android-Gradle-Build-System-Overview.033.jpeg
Android-Gradle-Build-System-Overview.034.jpeg
Android-Gradle-Build-System-Overview.035.jpeg
Android-Gradle-Build-System-Overview.036.jpeg
Android-Gradle-Build-System-Overview.037.jpeg
Android-Gradle-Build-System-Overview.038.jpeg
Android-Gradle-Build-System-Overview.039.jpeg
Android-Gradle-Build-System-Overview.040.jpeg
Android-Gradle-Build-System-Overview.041.jpeg
Android-Gradle-Build-System-Overview.042.jpeg
Android-Gradle-Build-System-Overview.043.jpeg
Android-Gradle-Build-System-Overview.044.jpeg
Android-Gradle-Build-System-Overview.045.jpeg
Android-Gradle-Build-System-Overview.046.jpeg
Android-Gradle-Build-System-Overview.047.jpeg
Android-Gradle-Build-System-Overview.048.jpeg
Android-Gradle-Build-System-Overview.049.jpeg
Android-Gradle-Build-System-Overview.050.jpeg
Android-Gradle-Build-System-Overview.051.jpeg
Android-Gradle-Build-System-Overview.052.jpeg
Android-Gradle-Build-System-Overview.053.jpeg
Android-Gradle-Build-System-Overview.054.jpeg

有问题?在文章下留言或者加 qq 群:583688711,希望能帮到你。