V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  Glowapp  ›  全部回复第 3 页 / 共 6 页
回复总数  109
1  2  3  4  5  6  
2015-12-11 14:17:36 +08:00
回复了 Glowapp 创建的主题 分享发现 Glow 技术博客 之 动态 Android 编程 by Glow Android tech lead Sol
举个例子,怎么从一个 Cursor 里取出类型为 ClassA 的实例到 List ?
1. 找出 ClassA 对应所有的列和每列在 Cusor 对应的索引。

int columnIndex = cursor.getColumnIndex("columnA");

2. 如果索引存在,根据类型取出正确的值。

if (columnIndex >= 0) {
instance.columnA = cursor.getString(columnIndex);
}

3. 对于每个属性,不断重复上述步骤取出对应的值。
这么做的问题在哪?
* 重复代码
* 重复代码
* 无聊
* 容易出错,不好维护

反射

我就是不想写那么多无聊的代码,怎么办?要不试试范型/反射。
1. 取出所有的属性。

Arrays.asList(cls.getDeclaredFields())

2. 循环属性队列。
3. 把属性设置成 accessible 。

field.setAccessible(true);

4. 找到索引。

int columnIndex = cursor.getColumnIndex(fieldName);
if (columnIndex < 0) {
continue;
}

5. 取出属性的类型,根据类型从 Cursor 里取出正确的值。

Class fieldType = field.getType();
if (fieldType.equals(int.class)) {
field.setInt(instance, cursor.getInt(columnIndex));
} else {
// more type check

6. 结束循环。

这样我们就不用很无聊的把同样的逻辑对于每种类型写一遍又一遍。

Processor

用了反射后,也会有一些其他问题,这样的代码可读性不是太好,不是很容易调试。

既然我们可以通过反射实现这些逻辑,为什么不干脆通过反射把这部分代码直接生成出来呢?
1. 定义你要处理的 annotation 。
2. 定义你的 Processor 类,继承 AbstractProcessor 。

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.glow.android.Annotation")
public class MyProcessor extends AbstractProcessor {

3. 创建要生成的方法。

ClassName currentType = ClassName.get(element);
MethodSpec.Builder builder = MethodSpec.methodBuilder("fromCursor")
.returns(currentType)
.addModifiers(Modifier.STATIC)
.addModifiers(Modifier.PUBLIC)
.addParameter(ClassName.get("android.database", "Cursor"), "cursor");

4. 循环取出每一列,并像下面这样生成代码。

CodeBlock.Builder blockBuilder = CodeBlock.builder();
blockBuilder.beginControlFlow("");
blockBuilder.addStatement("int columnIndex = cursor.getColumnIndex($S)", column);
blockBuilder.beginControlFlow("if (columnIndex >= 0)");
ColumnType columnType = columnTypeMap.get(column);
String cursorType = null;
if (columnType == ColumnType.INT) {
cursorType = "Int";
} else if (columnType == ColumnType.LONG) {
cursorType = "Long";
} else if (columnType == ColumnType.FLOAT) {
cursorType = "Float";
} else if (columnType == ColumnType.STRING) {
cursorType = "String";
} else {
abort("Unsupported type", element);
}

5. 把代码输出到编译时的文件里。

JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(
concatClassName(packageName, className), element);
Writer writer = sourceFile.openWriter();
javaFile.writeTo(writer);
writer.close();

6. 用 apt 工具把我们上面写的库加到编译过程去。

Tips:
* 用 AutoService 可以方便的生成 Processor 方法
* 强推 Javapoet ,用来生成漂亮的代码

AOP
AOP 的做法和 Processor 类似,这里就不详述。你可能用 AspectJ 。

Gradle plugin
最后我还是没有完全采用上面的方法,因为:
* 在编译时生成的代码在打开编译器时找不到
* 有时候有些特殊需求,比如很多属性要在多个地方共享使用,能配置化会更好些

于是我们就用了 Gradle Plugin 来通过可配置文件生成代码

以下是简单的例子:
1. 定义配置文件,这里选用比较简单的 toml 文件

srcDir = "src/main/java"
pkg = "com.glow.android.baby.storage.db"
[[tables]]
name = "user"
[[tables.columns]]
name = "user_id"
type = "long"
isKey = true

2. 在 buildSrc 项目里创建 Plugin

public class DbPlugin implements Plugin<Project> {

@Override
void apply(Project project) {
project.task('initDb') << {
def dir = project.getProjectDir()
def file = new File(dir, "table.toml")
generateCode(dir, new Toml().parse(file).to(DB.class))
}
}

static void generateCode(File dir, DB db) {
def outputDir = new File(dir, db.srcDir)
outputDir.mkdirs()
for (Table table : db.tables) {
// Process it
}
}
}

3. 像在上节讲的那样生成代码,把数据源 从 annotation 换成 toml 里的定义
4. 在项目里把 Plugin 引用进去,并执行
5. 这样就可以得到漂亮的已经生成好的代码
2015-12-02 16:38:25 +08:00
回复了 Glowapp 创建的主题 酷工作 关注女性健康,需要全栈工程师:)
顶尖硅谷创业公司 Glow 诚招 全栈工程师
我们 Glow 是一个来自 硅谷的创业团队,我们这里有着 硅谷 IT 公司的工程师氛围。做为 Glow 的全栈工程师,你需要完成产品开发的整个流程,从移动端 iOS 或是 Android 的开发,到服务器端的 API 设计与数据库设计。如果你不具挑战,对于学习新技术有浓厚的兴趣,志立于打造优秀的互联网产品, Glow 的工程师团队欢迎你的加入。

基础要求
• 熟练掌握 Python / Objc / Swift / Java 中至少一门语言
• 扎实的编程基本功,包括算法与数据结构等
• 较强的英文能力,尤其是读写

加分项
• 有互联网公司,特别是创业公司的工作经验
• 有 iOS / Android 开发经验者优先
• 在 App Store 或是 Google Play Store 有自己的作品
• 有 Web 应用服务器端的开发经验

Glow 独家待遇
• 丰厚薪资: 15 - 30k ,公司期权,具体待遇面议(决不让你失望!)
• 扁平管理:告别级别职称的压迫,远离格子间的烦闷,在 Glow 只有畅所欲言,共同进退
• 全天水果、零食、饮料无限供应及舒适午睡躺椅,沙发
• 免费健康的午餐和晚餐 ( Uncle 5, Wagas , 年代秀,大茴香,港丽等餐厅 )
• 带薪年假: 15 天 /年,探亲访友休闲度假都值得拥有
• 商业医疗保险:告别医院长队,健康工作生活
• 工作地点:上海市人民广场,地铁 1/2/8 号线出站 3 分钟可达


http://ww2.sinaimg.cn/large/005ZBE1Vgw1exukpwt2v6j30no0b4gnc.jpg

http://ww2.sinaimg.cn/large/005ZBE1Vgw1ex9rgerbtsj31kw0znb1e.jpg

http://ww1.sinaimg.cn/large/005ZBE1Vgw1ex9rf5uj8tj31kw11onnr.jpg

http://ww1.sinaimg.cn/large/005ZBE1Vgw1ex9re6ch0xj31kw11o1kx.jpg

http://ww2.sinaimg.cn/large/005ZBE1Vgw1ex9r9x7b5lj31kw11owyn.jpg

http://ww1.sinaimg.cn/large/005ZBE1Vgw1exfnd2js2lj31kw11oauu.jpg

http://ww1.sinaimg.cn/large/005ZBE1Vgw1exfne44f36j31kw11oqqt.jpg
2015-11-26 12:11:23 +08:00
回复了 Glowapp 创建的主题 酷工作 Glow - 关于女性生理健康,全职/实习岗位大汇总:)
捞一下~~~
2015-11-23 10:25:52 +08:00
回复了 Glowapp 创建的主题 酷工作 Glow - 关于女性生理健康,全职/实习岗位大汇总:)
@youdaji 我们产品的市场目前大多还是在国外,所以对英文还是有一定的要求 :)
2015-11-20 13:15:22 +08:00
回复了 Glowapp 创建的主题 酷工作 Glow - 关于女性生理健康,全职/实习岗位大汇总:)
@KylinRoc @WildCat 如果是在上海本地念书的话,非应届在校生也可以申请我们实习生职位的 :)
@rayzworld 我们的工作环境是英文的:)
@KevinSomers 请问你当时投的是什么职位呢?如果是上述职位的话,你可以再次把相关信息发过来 :)
@youkeshen 按照要求把简历及作品发给我们吧 :)
2015-11-20 11:59:53 +08:00
回复了 Glowapp 创建的主题 酷工作 Glow - 关于女性生理健康,全职/实习岗位大汇总:)
2015-11-20 11:59:20 +08:00
回复了 Glowapp 创建的主题 酷工作 Glow - 关于女性生理健康,全职/实习岗位大汇总:)
@WaylanPunch 确实独乐乐不如众乐乐,我们也还在努力:)

@youdaji 那您就直接把中英文简历发过来就好 :)
@music 工作地点在人民广场

@xxx027 您可以把中英文简历及相关代码作品或技术博客发到我们的邮箱 jobs.cn@glowing.com
@youdaji 您可以把中英文简历及相关代码作品或技术博客发到我们的邮箱 jobs.cn@glowing.com
@theJian

测试工程师实习生

* 985 / 211 应届生(表现优异可留用)或 近期可以开始实习工作的非应届在校生;
* 期望未来从事移动测试工作,成为全栈测试工程师;
* 较强的自学能力,关注 IT 领域的新技术;
* 较强的团队沟通和协作能力,工作积极主动;


加分项:
* 具备 Linux/Mac 平台下脚本语言编程能力(Python 、 Shell 等);
* 熟悉 iOS 以及 Android 测试环境;
* 只要你足够优秀,以上条件均可酌情考量噢!
@EthanZhu 有移动开发的实习经验会比较好 :)

@youdaji @leassy
感谢二位对我们的关注。
公司在不断的发展,我们的 App 也会越来越多,所以需要的人才也会越来越多。
作为一个创业型公司,我们快速发展,但我们对于优秀人才的真诚是不变的。
求职本来就是一件非常谨慎的事情,也欢迎二位对我们多多了解,提出我们的问题,也为其他看帖子的人擦亮双眼,没准就让大家更喜欢我们了呢 :)
2015-11-16 12:13:50 +08:00
回复了 Glowapp 创建的主题 DevOps 写好 DevOps 的文档 by Glow CTO Ryan YE
Fabric 的任务管理与文档
在以前的文章中,我们曾经介绍过 Glow 使用了 fabric 来执行各种日常管理的任务。 Fabric 提供了非常好用的任务组织以及查阅任务文档的功能。
Fabric 的主文件一般命名为 fabfile.py ,但任务多了,都写在一个文件里显然很难维护。 Fabric 有一个很实用的特性,就是当 fabfile.py 里导入其他模块时,会自动发现里面的 fabric 任务。利用这个特性,可以把各种任务分类写在不同的模块中,然后在 fabfile.py 中统一导入。比如 Glow 的 DevOps 代码库的结构大概长这个样子
http://ww3.sinaimg.cn/large/005ZBE1Vgw1ey2osq3zx2j30jt0ajaaw.jpg

fabfile.py 里除了一些最核心的任务脚本外,主要就是一些 import 语句

http://ww4.sinaimg.cn/large/005ZBE1Vgw1ey2ot5m8kpj30ju075myc.jpg

这样我们就把散落在多个文件里的任务聚集到了一起,我们可以用 fab -l 来列出所有可执行的任务及其描述,其中任务描述来自于对应任务的第一行 docstring 。例如,

http://ww2.sinaimg.cn/large/005ZBE1Vgw1ey2otrp03jj30jv0pyjux.jpg

这里可以看到,将任务分写在不同的模块,模块名就起到了 Namespace 的作用。在显示命令列表时,在同一个 Namespace 下的命令被聚集到了一起,很好地起到了任务分类的作用。使用 fab -d [task_name]可以显示该任务完整的 docstring 。规整的 docstring 可以让执行任务的用户清楚地理解其作用及参数用法。我们在写 fabric 任务的 docstring 时,一般分为三个部分
• 任务的简单介绍
• 任务的参数
• 具体用例

最后一点由为重要,有些任务参数众多,即使读了参数说明,仍会让人有些云里雾里。但几个典型的实际用例,对于用户了解任务的用法会起到至关重要的作用。在下面的例子中,我们展示了 deploy 任务(代码部署)的说明文档

http://ww2.sinaimg.cn/large/005ZBE1Vgw1ey2oulofmxj30jp0j6gp0.jpg

动态 Docstring

在 Python 中, docstring 其实就是函数的__doc__的属性,所以我们可以像修改普通变量那样动态修改 docstring ,这给我们生成动态文档或是重用公共的文档提供了可能。例如,我们的 services 模块下有 cycle,start,stop 三个任务,分别用来重启,开始,停止我们的 microservice 。我们当然希望在用 fab -d 来查看任务的文档说明时,同时可以显示所有可用的 microservice 。但 hard-coded 现有的 microservice 是一个愚蠢的做法,这样我们不但需要把同一段文档复制三份,并且每次新增一个 microservice 时还要记得来更新文档。这里我们用 Python 的 decorator 来动态地把可用服务的信息添加到 docstring 中。比如 cycle 任务的定义是这样的

http://ww1.sinaimg.cn/large/005ZBE1Vgw1ey2ov4w9itj30jt0ajaba.jpg

注意到这里用了 @service_doc 这个 decorator ,它的定义如下

http://ww2.sinaimg.cn/large/005ZBE1Vgw1ey2ovnptw4j30js081my5.jpg

我们通过 get_available_services 来动态取得当前环境下可用的 microservice (这里我们不关心 get_available_service 是如何实现的),并将其添加到函数的 docstring 之后。这样,当我们查看 cycle 的方法时,所有可用的 microservice 也会显示出来。

http://ww1.sinaimg.cn/large/005ZBE1Vgw1ey2ow5mi21j30ju0ev762.jpg

动态外部文档

除了 docstring ,我们也经常需要写独立的外部文档。在 Glow ,这些文档绝大部分都是用 Markdown 来写的。例如,我们需要写一个介绍生产环境架构的文档,其中肯定会加入生产环境中有哪些服务器,每个服务器的功能描述以及它们的 hostname 。我们可以用手动的方式来写,但每当为生产环境添加新服务器时,我们必须记得更新这份文档。而实际情况是,我们从来不在 AWS 的控制台手动创建服务器,所有的服务器都是由 Ansible 来创建与维护的。也就是说,所有的服务器配置信息及其功能描述都已经存在于 Ansible 的 playbook 中。当我们写外部文档时,应该去引用 Ansible 中的信息,而不是重写手写一遍。
所以在我们的生产环境文档中会利用 HTML 注释来指定需要外部引用的部分,然后通过执行脚本将这些引用的内容填充至文档里。例如,在我们的生产环境文档中有这样一段

http://ww2.sinaimg.cn/large/005ZBE1Vgw1ey2owt7wcrj30jt08wjss.jpg

这里<!-- BEGIN EC2-SERVER-LIST -->和<!-- END EC2-SERVER-LIST -->之间的表格就是一个外部引用,每次 Ansible 更新服务器配置时,会执行一个脚本,它会自动在文档中查找这对标签,并更新其中的内容。这是一个很简单的技术,但对于保持文档与实际环境同步很有帮助。

小结

几乎所有人都承认文档的重要性,但真正愿意在文档上花费精力的团队却十分有限。写这篇文章是希望大家能意识到,文档其实也是代码的一部分,也应该遵循 DRY 原则,也可以通过编程的手段来动态生成与自动更新。
如果各位对这种文档与代码结合的概念有兴趣,你也许听过 Literate programming ,( https://en.wikipedia.org/wiki/Literate_programming )那 Literate programming 与 DevOps 结合会是什么样子,有兴趣的可以看看这个视频( https://www.youtube.com/watch?v=dljNabciEGg
2015-11-16 11:48:12 +08:00
回复了 Glowapp 创建的主题 酷工作 [ 硅谷创业公司 Glow ]推荐人才,赢 Macbook 大奖!
@hiStarter 早就已经加入了 hiStarter 的平台上啦 大家合作愉快 :)
@kingzeus 那还麻烦多多自荐或推荐啦 :)
@Loker 我们偶尔可以 work at home 但长期远程目前来说还是不太行哦
#这不是一条广告,这是一条认真的回复#
因为楼主所说的,好像我们 Glow 都有。我们还有免费的午餐晚餐,大量丰厚的期权奖励。
好了,打广告就停不下来噜~
@cnly1987 性别不限,只要有实力,我们都欢迎~
1  2  3  4  5  6  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   869 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 24ms · UTC 19:21 · PVG 03:21 · LAX 12:21 · JFK 15:21
Developed with CodeLauncher
♥ Do have faith in what you're doing.