Ernest的Blog

一个有理想的Android程序员

0%

公司对每个ip限速300k,平时下载文件甚至打开网页都很慢,之前斐讯0元购撸了个k2路由器,刷了openwrt,配了多拨,但是后来重新刷路由器固件后多拨配置丢了,年久未搞,配了很久都单线多拨失败。今日经过百度多篇文章,最终根据文章的图文操作,弄清楚多拨会用到哪些配置文件,特此记录下来,往后搞单线多拨只需按照笔记无脑敲命令就行了,甚至连luci都不需要。

首先更新openwrt的软件源

opkg update

然后按照必要的文件

opkg install luci kmod-macvlan luci-app-mwan3

在/etc/rc.local中exit上一行加入一下配置

sleep 6
ip link add link eth0.2 name vth1 type macvlan
ifconfig vth1 up

ip link add link eth0.2 name vth2 type macvlan
ifconfig vth2 up

ip link add link eth0.2 name vth3 type macvlan
ifconfig vth3 up

ip link add link eth0.2 name vth4 type macvlan
ifconfig vth4 up

ip link add link eth0.2 name vth5 type macvlan
ifconfig vth5 up

ip link add link eth0.2 name vth6 type macvlan
ifconfig vth6 up

在/etc/config/network中,把原有的config interface各项去掉, 修改成一下版本:
//这个wan修改一下

config interface ‘wan’
option proto ‘dhcp’
option ifname ‘eth0.2’
option metric ‘20’

config interface ‘vwan1’
option proto ‘dhcp’
option ifname ‘vth1’
option metric ‘21’

config interface ‘vwan2’
option proto ‘dhcp’
option ifname ‘vth2’
option metric ‘22’

config interface ‘vwan3’
option proto ‘dhcp’
option ifname ‘vth3’
option metric ‘23’

config interface ‘vwan4’
option proto ‘dhcp’
option ifname ‘vth4’
option metric ‘24’

config interface ‘vwan5’
option proto ‘dhcp’
option ifname ‘vth5’
option metric ‘25’

config interface ‘vwan6’
option proto ‘dhcp’
option ifname ‘vth6’
option metric ‘26’

在 /etc/config/firewall 中

config zone
option name ‘wan’
option input ‘REJECT’
option output ‘ACCEPT’
option forward ‘REJECT’
option masq ‘1’
option mtu_fix ‘1’
option network ‘wan wan6 vwan1 vwan2 vwan3 vwan4 vwan5 vwan6’

在 /etc/config/mwan3 中

config interface ‘vwan1’
option enabled ‘1’
option initial_state ‘online’
option family ‘ipv4’
option track_method ‘ping’
option reliability ‘1’
option count ‘1’
option size ‘56’
option max_ttl ‘60’
option check_quality ‘0’
option timeout ‘2’
option interval ‘5’
option failure_interval ‘5’
option recovery_interval ‘5’
option down ‘3’
option up ‘3’
list track_ip ‘8.8.8.8’

config interface ‘vwan2’
option enabled ‘1’
option initial_state ‘online’
option family ‘ipv4’
option track_method ‘ping’
option reliability ‘1’
option count ‘1’
option size ‘56’
option max_ttl ‘60’
option check_quality ‘0’
option timeout ‘2’
option interval ‘5’
option failure_interval ‘5’
option recovery_interval ‘5’
option down ‘3’
option up ‘3’
list track_ip ‘8.8.8.8’

config interface ‘vwan3’
option enabled ‘1’
option initial_state ‘online’
option family ‘ipv4’
option track_method ‘ping’
option reliability ‘1’
option count ‘1’
option size ‘56’
option max_ttl ‘60’
option check_quality ‘0’
option timeout ‘2’
option interval ‘5’
option failure_interval ‘5’
option recovery_interval ‘5’
option down ‘3’
option up ‘3’
list track_ip ‘8.8.8.8’

config interface ‘vwan4’
option enabled ‘1’
option initial_state ‘online’
option family ‘ipv4’
option track_method ‘ping’
option reliability ‘1’
option count ‘1’
option size ‘56’
option max_ttl ‘60’
option check_quality ‘0’
option timeout ‘2’
option interval ‘5’
option failure_interval ‘5’
option recovery_interval ‘5’
option down ‘3’
option up ‘3’
list track_ip ‘8.8.8.8’

config interface ‘vwan5’
option enabled ‘1’
option initial_state ‘online’
option family ‘ipv4’
option track_method ‘ping’
option reliability ‘1’
option count ‘1’
option size ‘56’
option max_ttl ‘60’
option check_quality ‘0’
option timeout ‘2’
option interval ‘5’
option failure_interval ‘5’
option recovery_interval ‘5’
option down ‘3’
option up ‘3’
list track_ip ‘8.8.8.8’

config interface ‘vwan6’
option enabled ‘1’
option initial_state ‘online’
option family ‘ipv4’
option track_method ‘ping’
option reliability ‘1’
option count ‘1’
option size ‘56’
option max_ttl ‘60’
option check_quality ‘0’
option timeout ‘2’
option interval ‘5’
option failure_interval ‘5’
option recovery_interval ‘5’
option down ‘3’
option up ‘3’
list track_ip ‘8.8.8.8’

config member ‘mwan’
option interface ‘wan’

config member ‘mvwan1’
option interface ‘vwan1’

config member ‘mvwan2’
option interface ‘vwan2’

config member ‘mvwan3’
option interface ‘vwan3’

config member ‘mvwan4’
option interface ‘vwan4’

config member ‘mvwan5’
option interface ‘vwan5’

config member ‘mvwan6’
option interface ‘vwan6’

config policy ‘balanced’
option last_resort ‘default’
list use_member ‘mwan’
list use_member ‘mvwan1’
list use_member ‘mvwan2’
list use_member ‘mvwan3’
list use_member ‘mvwan4’
list use_member ‘mvwan5’
list use_member ‘mvwan6’

最后重启一下路由器

reboot


使用大文件测速专用下载测试一下

100g http://repos.mia.lax-noc.com/speedtests/100gb.bin
10t http://repos.mia.lax-noc.com/speedtests/10tb.bin

Git的优点

Git的优点很多,但是这里只列出我认为非常突出的几点。

  1. 由于是分布式,所有本地库包含了远程库的所有内容。
  2. 优秀的分支模型,打分支以及合并分支,机器方便。
  3. 快速,在这个时间就是金钱的时代,Git由于代码都在本地,打分支和合并分支机器快速,使用个SVN的能深刻体会到这种优势。

感兴趣的,可以去看一下Git本身的设计,内在的架构体现了很多的优势,不愧是出资天才程序员Linus (Linux之父) 之手

版本管理的挑战

虽然有这么优秀的版本管理工具,但是我们面对版本管理的时候,依然有非常大得挑战,我们都知道大家工作在同一个仓库上,那么彼此的代码协作必然带来很多问题和挑战,如下:

  1. 如何开始一个Feature的开发,而不影响别的Feature?
  2. 由于很容易创建新分支,分支多了如何管理,时间久了,如何知道每个分支是干什么的?
  3. 哪些分支已经合并回了主干?
  4. 如何进行Release的管理?开始一个Release的时候如何冻结Feature, 如何在Prepare Release的时候,开发人员可以继续开发新的功能?
  5. 线上代码出Bug了,如何快速修复?而且修复的代码要包含到开发人员的分支以及下一个Release?

大部分开发人员现在使用Git就只是用三个甚至两个分支,一个是Master, 一个是Develop, 还有一个是基于Develop打得各种分支。这个在小项目规模的时候还勉强可以支撑,因为很多人做项目就只有一个Release, 但是人员一多,而且项目周期一长就会出现各种问题。

Git Flow

就像代码需要代码规范一样,代码管理同样需要一个清晰的流程和规范

Vincent Driessen 同学为了解决这个问题提出了 A Successful Git Branching Model

下面是Git Flow的流程图

img

上面的图你理解不了? 没关系,这不是你的错,我觉得这张图本身有点问题,这张图应该左转90度,大家应该就很用以理解了。

Git Flow常用的分支

  • Production 分支

也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改

  • Develop 分支

这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支

  • Feature 分支

这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release

  • Release分支

当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支

  • Hotfix分支

当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release

Git Flow如何工作

初始分支

所有在Master分支上的Commit应该Tag

img

Feature 分支

分支名 feature/*

Feature分支做完后,必须合并回Develop分支, 合并完分支后一般会删点这个Feature分支,但是我们也可以保留

img

Release分支

分支名 release/*

Release分支基于Develop分支创建,打完Release分之后,我们可以在这个Release分支上测试,修改Bug等。同时,其它开发人员可以基于开发新的Feature (记住:一旦打了Release分支之后不要从Develop分支上合并新的改动到Release分支)

发布Release分支时,合并Release到Master和Develop, 同时在Master分支上打个Tag记住Release版本号,然后可以删除Release分支了。

img

维护分支 Hotfix

分支名 hotfix/*

hotfix分支基于Master分支创建,开发完后需要合并回Master和Develop分支,同时在Master上打一个tag

img

Git Flow代码示例

a. 创建develop分支

1
2
git branch develop
git push -u origin develop

b. 开始新Feature开发

1
2
3
4
5
6
7
8
git checkout -b some-feature develop
# Optionally, push branch to origin:
git push -u origin some-feature

# 做一些改动
git status
git add some-file
git commit

c. 完成Feature

1
2
3
4
5
6
7
8
9
git pull origin develop
git checkout develop
git merge --no-ff some-feature
git push origin develop

git branch -d some-feature

# If you pushed branch to origin:
git push origin --delete some-feature

d. 开始Relase

1
2
3
4
git checkout -b release-0.1.0 develop

# Optional: Bump version number, commit
# Prepare release, commit

e. 完成Release

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
git checkout master
git merge --no-ff release-0.1.0
git push

git checkout develop
git merge --no-ff release-0.1.0
git push

git branch -d release-0.1.0

# If you pushed branch to origin:
git push origin --delete release-0.1.0


git tag -a v0.1.0 master
git push --tags

f. 开始Hotfix

1
git checkout -b hotfix-0.1.1 master    

g. 完成Hotfix

1
2
3
4
5
6
7
8
9
10
11
12
13
git checkout master
git merge --no-ff hotfix-0.1.1
git push


git checkout develop
git merge --no-ff hotfix-0.1.1
git push

git branch -d hotfix-0.1.1

git tag -a v0.1.1 master
git push --tags

Git flow工具

实际上,当你理解了上面的流程后,你完全不用使用工具,但是实际上我们大部分人很多命令就是记不住呀,流程就是记不住呀,肿么办呢?

总有聪明的人创造好的工具给大家用, 那就是Git flow script.

安装

  • OS X

brew install git-flow

  • Linux

apt-get install git-flow

  • Windows

wget -q -O - –no-check-certificate https://github.com/nvie/gitflow/raw/develop/contrib/gitflow-installer.sh | bash

使用

  • 初始化: git flow init
  • 开始新Feature: git flow feature start MYFEATURE
  • Publish一个Feature(也就是push到远程): git flow feature publish MYFEATURE
  • 获取Publish的Feature: git flow feature pull origin MYFEATURE
  • 完成一个Feature: git flow feature finish MYFEATURE
  • 开始一个Release: git flow release start RELEASE [BASE]
  • Publish一个Release: git flow release publish RELEASE
  • 发布Release: git flow release finish RELEASE
    别忘了git push –tags
  • 开始一个Hotfix: git flow hotfix start VERSION [BASENAME]
  • 发布一个Hotfix: git flow hotfix finish VERSION

img

Git Flow GUI

上面讲了这么多,我知道还有人记不住,那么又有人做出了GUI 工具,你只需要点击下一步就行,工具帮你干这些事!!!

SourceTree

当你用Git-flow初始化后,基本上你只需要点击git flow菜单选择start feature, release或者hotfix, 做完后再次选择git flow菜单,点击Done Action. 我勒个去,我实在想不到还有比这更简单的了。

目前SourceTree支持Mac, Windows, Linux.

这么好的工具请问多少钱呢? 免费!!!!

img

img

转载自csdn博客,查看原文

花有重开日,人无再少年

不知不觉已经2019了。成为社会人也已经有三年有余。成长了很多,但是觉得还不够。在时间管理,目标计划方面,还有很大的改进空间。经常在心里立下FLAG,但是没多久FLAG就倒了。因此本篇用于记录我的目标和计划。那些已经实现的、已经失败的、正在计划的……

已经实现的 SUCCESS✅

OBJECT CREATE TIME START TIME END TIME CURRENT STATUS LINK

已经失败的 FAILURE❌

OBJECT CREATE TIME START TIME END TIME CURRENT STATUS LINK

正在计划的 PLANNING🕛

OBJECT CREATE TIME START TIME END TIME CURRENT STATUS LINK
熟练使用Adobe XD 2019.03.10 2019.3.13 开始阅读vue-js 2.0完全入门记录
熟练使用 vue 2019.03.12
熟练使用docker 2019.03.10 2019.03.14 学习搭建docker-youphptube
熟练使用flutter 2019.03.12

上午京东买了958的KPW3,结果WIFI一直连不上。于是百度解决办法,也不行。问在线客服,并没有本质上解决问题,而且技术支持一点也不懂技术。下面是对话记录。

我: 怎么看是paperwhite是第几代,连接不上wifi
您现在已连接到亚马逊的 高云涛。
高云涛: 您好,欢迎使用亚马逊在线服务,我是亚马逊kindle客服:高云涛,很高兴为您服务,请问有什么可以帮到您的?
麻烦您提供一下设备的序列号我帮您查询下
在这里查询序列号

  1. 从主页(点击小房子回到主页)点击右上角三个点菜单键。
  2. 点击【设置】,然后再次点击【右上角三点菜单键】。
  3. 点击【设备信息】.查看序列号提供一下
    我: ******(保护隐私,此处隐藏)
    高云涛: 目前设备还没有注册
    是kpw3代
    我: 连接不上wifi
    高云涛: 您连接的wifi是家庭路由器吗
    我: 我的手机都能连接上。 是公司的路由器
    密码确定是正确的。
    百度后尝试建立WIFI_NO_NET_PROBE 文件,也不行
    高云涛: kindle对wifi的信道有要求的
    我: 改不了wifi信道,就用不了了?
    高云涛: 重启一下路由器试试
    我: 总不能每次都重启路由器吧
    高云涛: 重启一次,能连接上就行
    不用总重启
    或是您打开手机的流量热点
    我: 升级固件也不能吗?是固件的问题还是硬件的缺陷?
    高云涛: 连接热点注册试试
    目前您还没有注册,看不到固件是哪版的
    我: 5.8.2.1
    高云涛: 太低了
    我: 升级了固件能够解决这个问题吗?
    高云涛: 系统包下载连接
    http://pan.baidu.com/s/1mijs8pM
    • 下载系统包后,将软件更新文件传输至Kindle设备:
    a. 打开Kindle,并使用USB连接线将其连接至电脑。
    b. 将新的更新文件从电脑拖入Kindle驱动器中。
    注意: 不要将更新文件拖入Kindle驱动器中的任何文件夹中。
    •开始软件更新:
    a. 在【主页】中,点击【菜单】图标,然后点击【设置】。
    b. 再次点击【菜单】图标,然后点击【更新您的Kindle】。 如果已安装最新更新,或者更新文件传输失败,此选项将显示为灰色。
    c. 点击【确定】执行更新。 此时您将看到【您的Kindle正在更新】。
    更新完成后,Kindle将立即自动重启。
    我: 我从官网下载着。
    高云涛: 先更新后,在试试
    最好也换网
    我: 重启路由器也不行
    高云涛: 换手机流量热点
    我: KPW3支持的信道是多少
    高云涛: 1.6.11
    我: 不在这三个信道上的话,只能一遍遍的重启路由器?
    高云涛: 您现在先不用考虑那些
    您现在打开手机流量热点
    先连接试试
    我: 注册上了
    高云涛: 请问您还有其他疑问吗?
    我: 有,不在这三个信道上的话,只能一遍遍的重启路由器?
    高云涛: 可以自己设置
    我: 不是自己的路由器不能设置啊。。。
    升级固件也不能吗?是固件的问题还是硬件的缺陷?
    高云涛: 这个问题,建议您和公司协商一下
    可以连接热点,设备是没有问题的
    是wifi的设备问题
    不是通过设备更新,就能单纯解决
    不是同一个问题
    我: 不是吧,手机都没问题。
    高云涛: 之前已经告知您了,kindle对wifi信道有要求
    不是手机可以连接的,kindle就一定可以连接
    我: 这个没有在说明书上有啊
    高云涛: 没有
    这个是您公司的网问题,不是您设备的问题
    重启路由器,就是简单的拔下电源,然后10秒后充电
    高云涛: 没有
    这个是您公司的网问题,不是您设备的问题
    重启路由器,就是简单的拔下电源,然后10秒后充电
    请问您还有其他疑问吗?
    我: 没解决问题

在做项目初期,如果知道有哪些框架/工具可用,对项目的开发会有很大的帮助。这篇帖子收集项目开发用过的,或者有类似需求不知道有对应工具,后来发现这些工具有相见恨晚感觉的。作为一个类似于备忘录的帖子,根据个人判断在需要的时候进行增删改查。介绍时候尽量用一两个关键词。收集的文章可能会因各种原因失效,请以标题自行在搜索引擎搜索。帖子目的不是做到大而全且更新频繁。如果读者有这样的需求,良心推荐干货集中营,每日分享 妹子图 和 ** 技术干货**,还有供大家中午休息的休闲视频

最近更新于 2017-03-24 原文才持续更新哦

工具类

DiffUtil

DiffUtil是一个查找集合变化的工具类,是搭配RecyclerView一起使用的。DiffUtil的作用,就是找出集合中每一个Item发生的变化,然后对每个变化给予对应的刷新。
Android开发学习之路-DiffUtil使用教程

logger

简单,美观,功能强大的安卓打印工具。
GitHub

moco模拟服务器返回数据

灵活配置参数,请求方法,URL,支持挂载文件,自带独立微型服务器,可以用json,java等语言配置。
介绍文章
官网

快速开发

AS插件

ButterKnife Zelezny:ButterKnife 生成器
SelectorChapek:selector 自动生成XML
GsonFormat:json字符串生成相应的实体类
介绍文章

bufferKnife

JakeWharton大神出品。View,资源,事件注解方式初始化。
官网

网络框架

Volley

2013年Google I/O大会上推出的一个新的网络通信框架,简化Http通信,提供异步回调。
个人更推荐使用retrofit`

Retrofit

Retrofit与okhttp共同出自于Square公司,retrofit就是对okhttp做了一层封装。把网络请求都交给给了Okhttp.配置简单灵活,接口返回对象,支持rxJava无缝对接
GitHub

Glide 图片加载

这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app.Glide和Picasso有90%的相似度

Picasso图片加载

推荐用Glide代替。查看对比文章 Picasso VS Glide

数据库优化

GreenDAO

greenDao是一个将对象映射到SQLite数据库中的轻量且快速的ORM解决方案,跟EvenBus师出同门。
介绍文章1
介绍文章2
官网

sqlbrite

square出品。一个轻量级SQLiteOpenHelper包装类,引入reactive 流式语法,完美解决数据库和UI的同步更新!
GitHub

性能优化

Hugo 打印方法参数执行时间

JakeWharton大神出品。只要一行@DebugLog注解打印方法的传参和返回值,甚至方法的执行时间,只在debug版本打印,对release版本无任何影响。
GitHub

Scalpel

JakeWharton大神出品。查看界面层级,3D的效果。方便优化界面布局。
GitHub

leakcanary

square出品。内存泄漏检测工具
GitHub

rxJava/rxAndroid

rxJava/rxAndroid 精品文章

RxJava Essentials 中文翻译版
给 Android 开发者的 RxJava 详解
深入浅出RxJava(一:基础篇)
RxJava 和 RxAndroid 五(线程调度

rxJava 示例代码

rxJava 示例代码集

rxJava框架 rxBinding

架构

精品文章

Android架构探索

近日在搜MVP模式作为安卓项目架构时候,发现GitHub上一篇介绍MVP架构的文章。一看star已经超过2K了。通读全文发现作者用实际项目将MVP架构要怎么分职责讲得十分通俗易懂。为了方便大家查阅,把原文翻译了一下。作者还有一篇文章讲安卓项目和代码风格指南的。欢迎查看相关翻译

架构指南

我们的安卓APP架构基于 MVP (Model View Presenter) 模式。

  • View (UI 层):这是 Activities, Fragments 和其他标准安卓控件所在层。它的职责是呈现从Presenter收到的数据给用户。它也处理用户的交互和输入(点击监听等)以及在需要时触发Presenter中对应的动作。

  • Presenter: Presenter 订阅由 DataManager 提供的 RxJava Observables。他们负责处理订阅的生命周期,分析/修改DataManager返回的数据,调用View中的合适方法以便展示数据。

  • Model (Data 层): 它们的职责是取回,保存,缓存,以及通知数据。它可以与本地数据库以及其他的数据仓库通信,使用restful API或者其他第三方SDK等方式。它分为两部分:一组帮助工具类以及一个 DataManager 。工具类的数量因项目而异,每一个工具类有特定的功能,比如与API通信或在 SharedPreferences 中保存数据. DataManager 把不同工具类的输出使用rx操作符进行组合变换,所以它可以: 1) 提供有意义的数据给 Presenter,2) 把经常一起出现的动作组合在一起。这一层也包含实际的实体类,定义数据结构是怎样的。

从右到左看示意图:

  • Helpers (Model): 一组类的集合,每个类有特定的职责,他们的功能可以从与API或数据库通信,到实现一些特定的业务逻辑等。每个项目会有不同的工具类,但最常见的几个是:

    • DatabaseHelper: 它处理插入,更新,以及获取来自本地SQLite数据库的数据。它的方法返回 Rx Observables ,发射(译者注:Rx术语)简单java对象 (models)。
    • PreferencesHelper: 它保存以及获取来自 SharedPreferences 的数据,他可以返回 Observables 或者直接返回简单java对象。
    • Retrofit services : Retrofit 接口 使用Restful API 通信,每个不同的API拥有自己的 Retrofit 服务. 它们返回 Rx Observables.
  • Data Manager (Model): 这是MVP架构的关键部分。它持有每一个工具类的引用,使用这些工具类满足来自Presenter的请求。 它的方法广泛的使用 Rx 操作符来组合,转换或过滤来自工具类的输出,以便生成Presenter想要的输出。它返回发射数据模型(data models)的 observables。

  • Presenters: 订阅由 DataManager 提供的observables,处理这些数据以便调用View中合适的方法。

  • Activities, Fragments, ViewGroups (View): 实现了一组Presenter可以调用的方法的标准安卓组件。它们也处理用户的交互如点击等,然后调用Presenter中合适的方法来采取相应的行动。这些控件也实现框架相关的任务,如管理安卓生命周期,渲染视图(Views)等。

  • Event Bus: 它使得View控件得到发生在Model的特定事件通知。通常 DataManager 发出事件,然后这些事件可以被Activities 和 Fragments 订阅。 event bus 仅仅 用于非常特别的动作——这些事件不是仅与一个界面相关的,要通知多方,如:用户已经退出登录。
    原文链接
    译文链接

近日在搜MVP模式作为安卓项目架构时候,发现GitHub上一篇介绍MVP架构的文章。一看star已经超过2K了。作者同项目的这篇文章讲安卓项目和代码风格指南,也非常有参考价值。为了方便大家查阅,把原文翻译了一下。点击查看作者介绍安卓MVP架构指南的翻译文章

1. 项目指南

1.1 项目结构

新项目应该遵循安卓Gradle项目结构,定义在这里 :安卓Gradle插件用户指南。项目ribot Boilerplate 是一个很好的参考。

1.2 文件命名

1.2.1 类文件

类名书写方式是 大写的驼峰命名.

继承于安卓组件的类,名称应该以继承的组件命名,比如:SignInActivity, SignInFragment, ImageUploaderService, ChangePasswordDialog.

1.2.2 资源文件

资源文件书写方式:小写_下划线。如:ic_launcher.png

1.2.2.1 Drawable 文件

drawable类型资源命名:

资源类别 前缀 例子
Action bar ab_ ab_stacked.9.png
Button btn_ btn_send_pressed.9.png
Dialog dialog_ dialog_top.9.png
Divider divider_ divider_horizontal.9.png
Icon ic_ ic_star.png
Menu menu_ menu_submenu_bg.9.png
Notification notification_ notification_bg.9.png
Tabs tab_ tab_pressed.9.png

图标(icons)命名方式 (取自 安卓图标官方指南):

资源类别 前缀 例子
Icons ic_ ic_star.png
Launcher icons ic_launcher ic_launcher_calendar.png
Menu icons and Action Bar icons ic_menu ic_menu_archive.png
Status bar icons ic_stat_notify ic_stat_notify_msg.png
Tab icons ic_tab ic_tab_recent.png
Dialog icons ic_dialog ic_dialog_info.png

selector 状态命名方式:

状态 后缀 例子
Normal (正常) _normal btn_order_normal.9.png
Pressed(按下) _pressed btn_order_pressed.9.png
Focused (获得焦点) _focused btn_order_focused.9.png
Disabled (不可用) _disabled btn_order_disabled.9.png
Selected (选中) _selected btn_order_selected.9.png

1.2.2.2 布局文件

布局文件应该跟对应的安卓组件名称匹配,但是把最顶级的组件名称放在前面。比如,当我们创建一个用于 SignInActivity的布局,那布局文件名称应该是activity_sign_in.xml.

组件 类名 布局文件名
Activity UserProfileActivity activity_user_profile.xml
Fragment SignUpFragment fragment_sign_up.xml
Dialog ChangePasswordDialog dialog_change_password.xml
AdapterView item item_person.xml
Partial layout partial_stats_bar.xml

一个稍微不同的地方是当我们创建用于Adapter渲染的布局,如填充一个 ListView, 在这种情况下,布局文件命名应该是用 item_开始。
注意有些地方这些规则没法应用,如,当我们创建一个布局文件,用于渲染另一个布局的一部分,这样的情况下,我们应该使用partial_前缀。

1.2.2.3 菜单文件

跟布局文件相似,按钮文件应该跟对应的组件匹配。比如:当我们定义一个菜单,用于 UserActivity,那么文件名应该是 activity_user.xml

文件名不含有menu是个良好的习惯做法,因为这些文件已经位于 menu 目录了。

1.2.2.4 取值文件(Values files)

在values文件夹的资源文件应该是复数形式(plural),比如,strings.xml, styles.xml, colors.xml, dimens.xml, attrs.xml

2 代码指南

2.1 Java语言规范

2.1.1 不要忽略了异常

你万不该这样做:

1
2
3
4
5
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}

虽然你可能觉得你的代码永远不会抛出这个异常或者这异常不重要,不需要处理它,像上面那样忽略异常会在你的代码中留下雷区,将来其他人会踩到。你必须在你的代码中以某种原则的方式处理每个异常。具体的处理视情况而定。._ - (安卓官方代码指南)

指南中关于不要忽略异常的说明在 这里.

2.1.2 不要捕获一般的异常。

你不应该这样做:

1
2
3
4
5
6
7
8
try {
someComplicatedIOFunction(); // 可能抛出 IOException
someComplicatedParsingFunction(); // 可能抛出 ParsingException
someComplicatedSecurityFunction(); // 可能抛出 SecurityException
//啊哈,统一处理掉
} catch (Exception e) { //我将捕获所有的异常
handleError(); // 使用一个通用的处理手段!
}

查看不这样做的原因以及要怎么做: 猛戳安卓官方指南之不要捕获一般的异常

2.1.3 不要用finalizer

我们不使用finalizer。它什么时候被调用是没有保证的,甚至它不被调用。大部分情况下,你可以使用良好的异常处理来满足需要在finalizer中做的工作。如果你确实需要它,定义一个 close() 方法(或类似的)然后在文档中明确的指出什么时候这个方法需要被调用。可以查看 InputStream 做为例子.这种情况下,在finalizer中打印一个简短的log信息是恰当的但不是必须的,因为我们不希望log泛滥 ._ - (猛戳安卓官方指南之不要用finalizer)

2.1.4 完全的import语句

反例: import foo.*;

正解: import foo.Bar;

点击 这里 查看更多说明

2.2 Java风格规范

2.2.1 全局变量定义及命名

全局变量应该定义在文件的头部,遵循下面的命名规则。

  • 私有的非静态全局变量以m开头
  • 私有的静态全局变量以s开头
  • 其他的全局变量以小写单词开头
  • 常量使用全部大写,单词间用下划线间隔(ALL_CAPS_WITH_UNDERSCORES).

    译者注:这个规则取自Android Open Source Project代码贡献规范。在程序员的圈子里对于要不要前缀这个问题已经吵翻天了。我在使用greendao的时候使用前缀的话,自动生成的getter/setter会是getMxxx/setMxxx这样难看的方法。很多人也觉得这个命名规则没有意义。stackOverFlow 还有 这里 ,以及一本教你怎么写出整洁代码的书中如是说:
    “我觉得如今这些前缀没有意义,尤其是在你的APP中!你的类和方法应该尽量的小,而且你应该使用代码高亮的编辑环境,使得成员变量易于分辨。再者,人们快速适应忽略了前缀或后缀来看名字的有意义部分。我们阅读代码越多,看到前后缀越少。渐渐地,前缀就变成了旧代码看不到的线索和标记。——代码整洁之道(Clean.Code).Robert.C.Martin”

例子:

1
2
3
4
5
6
7
8
public class MyClass {
public static final int SOME_CONSTANT = 42;
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}

2.2.3把首字母缩写当做一个单词来看

好的例子 不好的例子
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
String url String URL
long id long ID

2.2.4 使用空格缩进

使用四个空格做代码块缩进:

1
2
3
if (x == 1) {
x++;
}

换行时使用8个空格缩进:

1
2
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);

2.2.5 使用标准的大括号风格

大括号的开始跟代码同一行

1
2
3
4
5
6
7
8
9
10
11
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}

条件语句使用大括号包住,除非条件体只有一行。
如果条件及只有一行条件体,而且没被换行,大括号是不必的。

1
if (condition) body();

这是不好的:

1
2
if (condition)
body(); // 不好的,没有大括号包围!

2.2.6 注解

2.2.6.1 注解实践规范

根据安卓代码风格指南,一些Java内置的注解标准的实践规范有:

  • @Override: 无论一个方法是重载父类还是实现某个接口的都必须使用@Override 注解。比如,当你使用 @inheritdocs Javadoc 标签,然后 从一个类(不是一个接口)中导出,你也必须声明那个方法 @Overrides 父类的方法。

  • @SuppressWarnings: @SuppressWarnings 注解应该仅当不可能消除一个警告的时候才使用。如果一个警告 通过这个 “不可能消除” 的测试, @SuppressWarnings 注解必不可少, 以便保证所有的警告在代码中反映实际的问题。

关于注解的更多指南可以查看这里.

2.2.6.2 注解风格

类,方法及构造器

当注解被应用到一个类,方法或者构造函数时候,注解在代码注释之后,一行一个注解

1
2
3
4
/* 这是这个类的注解 */
@AnnotationA
@AnnotationB
public class MyAnnotatedClass { }

全局变量

全局变量的注解应该在同一行列出。除非这一行超过了最大字数了。

1
@Nullable @Mock DataManager mDataManager;

2.2.7 限制变量范围

变量的使用范围应该限制到最小(Effective Java 条目 29)。这样做,你可以为你的代码增加可读性和可维护性,减少可能的错误。每个变量应该在最内层的使用到的它代码块中定义

局部变量应该在第一次用到他们的时候定义。几乎每个局部变量都应该初始化。如果你没有足够的信息去初始化一个变量,你应该延迟声明这个变量,直到你有足够的初始化信息。._ - (安卓代码风格指南)

2.2.8 排序import语句

如果你使用IDE,如Android Studio,你不必担心这些规则,这些IDE已经遵循这些规则。如果你不用IDE,往下看这些规则:

  1. import 安卓框架的
  2. Import 来自第三方的(com, junit, net, org)
  3. java 以及 javax
  4. 同一个项目的 imports

为了保持跟IDE的设置一致,这些imports应该是这样的:

  • 首字母分组排序,大写的字母在小写的字母前面(如Z在a前面)。
  • 每个主要分组 (android, com, junit, net, org, java, javax)间应该有一行空行。

更多信息查看这里

2.2.9 日志(Loging)指南

使用 Log 类提供的日志方法来打印错误信息或对开发者分辨问题有用的其他信息:

  • Log.v(String tag, String msg) (verbose)
  • Log.d(String tag, String msg) (debug)
  • Log.i(String tag, String msg) (information)
  • Log.w(String tag, String msg) (warning)
  • Log.e(String tag, String msg) (error)

通常来说,我们使用一个类的名称作为一个TAG,在文件的开头定义成一个 static final 变量,如:

1
2
3
4
5
6
7
public class MyClass {
private static final String TAG = MyClass.class.getSimpleName();

public myMethod() {
Log.e(TAG, "My error message");
}
}

VERBOSE 和 DEBUG 日志 必须在release版本中禁用。同时也建议禁用INFORMATION, WARNING 和 ERROR 日志,但是你可能觉得它们在release版本中定位问题很有用而保持启用。如果你决定让他们保持启用,你要保证这些日志信息不会泄漏email地址,用户id之类的隐私信息。
仅限debug版本显示Log的配置:

1
if (BuildConfig.DEBUG) Log.d(TAG, "The value of x is " + x);

2.2.10 类成员排序

这虽然没有简单正确的方案,但是使用一个 逻辑的 及 ** 一致的 ** 顺序可以提高代码的可读性和可学习性。下面是推荐的顺序:

  1. 常量
  2. 全局变量
  3. 构造函数
  4. 重载的函数和回调 (public 或 private)
  5. Public 方法
  6. Private 方法
  7. 内部类或内部接口

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainActivity extends Activity {

private String mTitle;
private TextView mTextViewTitle;

public void setTitle(String title) {
mTitle = title;
}

@Override
public void onCreate() {
...
}

private void setUpView() {
...
}

static class AnInnerClass {

}

}

如果你的类继承一个安卓组件如Activity或Fragment,对重载的方法进行排序以便跟组件的生命周期匹配是一个最佳实践。比如,当你的Activity实现onCreate(), onDestroy(), onPause()onResume(),正确的顺序是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity extends Activity {

//顺序跟Activity的生命周期匹配
@Override
public void onCreate() {}

@Override
public void onResume() {}

@Override
public void onPause() {}

@Override
public void onDestroy() {}

}

2.2.11 方法的参数顺序

在安卓编码时候,定义一个方法拥有一个Context参数是非常常见的,如果你写一个这样的方法,Context应该是第一个参数。

相反的例子是回调接口,它应该是最后一个参数
例子:

1
2
3
4
5
// Context 总是在第一个位
public User loadUser(Context context, int userId);

// 回调总是在最后一位
public void loadUserAsync(Context context, int userId, UserCallback callback);

2.2.13 字符串常量,命名和取值

很多安卓SDK的元素如SharedPreferences, Bundle, 或 Intent使用键值对实现,所以即使是一个小应用,使用一堆字符串常量也是很常见的。

当使用这些组件,你必须定义这些键是 static final 变量,并且它们应该像下面这样使用前缀

元素 变量名前缀
SharedPreferences PREF_
Bundle BUNDLE_
Fragment Arguments ARGUMENT_
Intent Extra EXTRA_
Intent Action ACTION_

注意Fragment的参数——Fragment.getArguments()也是一个Bundle。然而,因为这是Bundle非常常见的用法,我们为它定义一个不同的前缀。

例子:

1
2
3
4
5
6
7
8
//注意这些变量的值应该和名称一致来避免问题
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";

//Intent相关的使用完全包名作为值
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";

2.2.14 Fragment和Activity的参数

当数据通过IntentBundle传递到一个Activity Fragment,这些不同值的键必须 遵循上面描述的规则。

当一个 ActivityFragment 想要参数,它应该提供一个public static方法来简化对应的IntentFragment创建
在Activity中这个方法通常叫 getStartIntent():

1
2
3
4
5
public static Intent getStartIntent(Context context, User user) {
Intent intent = new Intent(context, ThisActivity.class);
intent.putParcelableExtra(EXTRA_USER, user);
return intent;
}

对于Fragment它的名称是newInstance(),处理使用合适的参数创建Fragment。

1
2
3
4
5
6
7
public static UserFragment newInstance(User user) {
UserFragment fragment = new UserFragment;
Bundle args = new Bundle();
args.putParcelable(ARGUMENT_USER, user);
fragment.setArguments(args)
return fragment;
}

注意1: 这些方法应该在类的 onCreate()前面

注意2: 如果我们提供上面说的方法,这些extras对应的键和参数应该是private 的,因为他们不需要暴露到类以外。

2.2.15 代码行长度限制

代码行不应该超过100 个字符。如果代码行超过这个限制长度,通常有两种方式来降低长度:

  • 提取一个局部变量或方法(推荐方式).
  • 把单行换行成多行。

有两种例外可以让一行超过100字符:

  • 这行不可分割,如长URL。
  • packageimport 语句.

2.2.15.1 换行策略

没有精确的公式解释怎么换行,很多不同的方案是有效的。然而,这里有几个规则可以应用到常见情况。
在操作符前断开

当一行被操作符断开,断开处应该在操作符之前,例子如下:

1
2
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;

赋值符号(=)例外

在操作符前断开有一个例外,那就是赋值符号 =,应该在赋值符号后面断开。

1
2
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;

方法链的情况

当多个方法被链接在同一行的时候——如使用Builder时,每一个方法的调用应该在独立一行,在 .之前断开。

1
Picasso.with(context).load("http://ribot.co.uk/images/sexyjoe.jpg").into(imageView);
1
2
3
Picasso.with(context)
.load("http://ribot.co.uk/images/sexyjoe.jpg")
.into(imageView);

长参数的情况

当一个方法有很多参数或它的参数非常长,我们应该在每个逗号 ,后面断开

1
loadPicture(context, "http://ribot.co.uk/images/sexyjoe.jpg", mImageViewProfilePicture, clickListener, "Title of the picture");
1
2
3
4
5
loadPicture(context,
"http://ribot.co.uk/images/sexyjoe.jpg",
mImageViewProfilePicture,
clickListener,
"Title of the picture");

2.2.16 RxJava 链式风格

Rx链式操作符要求换行。每一个操作符必须在新的一行,断行应该在.之前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Observable<Location> syncLocations() {
return mDatabaseHelper.getAllLocations()
.concatMap(new Func1<Location, Observable<? extends Location>>() {
@Override
public Observable<? extends Location> call(Location location) {
return mRetrofitService.getLocation(location.id);
}
})
.retry(new Func2<Integer, Throwable, Boolean>() {
@Override
public Boolean call(Integer numRetries, Throwable throwable) {
return throwable instanceof RetrofitError;
}
});
}

2.3 XML 风格规定

2.3.1 使用自关闭的标签

当一个XML元素没有任何内容时,你必须使用自关闭标签。
这是好例子:

1
2
3
4
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

这是不好的例子 :

1
2
3
4
5
6
<!-- 不要这样做! -->
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</TextView>

2.3.2 资源命名

资源 ID 和名称使用小写加下划线方式 lowercase_underscore.

2.3.2.1 ID 命名

ID应该使用小写加下划线命名,加上元素名称为前缀,如:

元素 前缀
TextView text_
ImageView image_
Button button_
Menu menu_

Image view 例子:

1
2
3
4
<ImageView
android:id="@+id/image_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

Menu 例子:

1
2
3
4
5
<menu>
<item
android:id="@+id/menu_done"
android:title="Done" />
</menu>

2.3.2.2 字符串

字符串名称以它们所属的部分为前缀命名,比如registration_email_hintregistration_name_hint。如果一个字符串不属于任何部分,那么你应该遵循下面的规则:

前缀 描述
error_ An error message
msg_ A regular information message
title_ A title, i.e. a dialog title
action_ An action such as “Save” or “Create”

2.3.2.3 Styles 和 Themes

不像其他的资源,style命名方式是大写的驼峰是命名UpperCamelCase

2.3.3 属性顺序

作为通用的规则,你应该把相似的属性放在一起分组,一个对常见属性良好的排序方式是

  1. View Id
  2. Style
  3. 布局的宽和高
  4. 其他的布局属性,按字母排序
  5. 剩下的属性,按字母排序

2.4 测试风格规定

2.4.1单元测试

测试类应该跟需要测试的类名字对应,以Test做后缀。比如我们创建一个测试类包含对DatabaseHelper的测试,我们应该把它命名为 DatabaseHelperTest

测试方法用@Test注解,通常以要测试的方法名为命名开始,以测试的前置条件及/或期望结果做后缀。

  • 模板: @Test void methodNamePreconditionExpectedBehaviour()
  • 例子: @Test void signInWithEmptyEmailFails()

如果没有它们测试也能够清楚的表达,前置条件和/或期望结果不总是必须的。

有时候一个类可能包含一大堆方法,同时每个方法要求多个测试,在这种情况,推荐把测试类分割成几个。比如,如果 DataManager包含一大堆方法,我们可能想把它分到 DataManagerSignInTest, DataManagerLoadUsersTest等等。通常你能够知道哪些测试应该在一起,因为它们有共同的test fixtures.

2.4.2 Espresso 测试

每一个 Espresso 测试类目标通常是一个Activity,所以它的名称应该跟目标Activity的名称匹配,以Test结束,如 SignInActivityTest

当使用 Espresso API ,把链式方法调用放新行是一个最佳实践。

1
2
3
onView(withId(R.id.view))
.perform(scrollTo())
.check(matches(isDisplayed()))

原文链接
译文链接