V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
V2EX  ›  Glowapp  ›  全部回复第 3 页 / 共 6 页
回复总数  109
1  2  3  4  5  6  
❮ ❯
举个例子,怎么从一个 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 月 2 日
回复了 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 日
回复了 Glowapp 创建的主题 › 酷工作 › Glow - 关于女性生理健康,全职/实习岗位大汇总:)
捞一下~~~
2015 年 11 月 23 日
回复了 Glowapp 创建的主题 › 酷工作 › Glow - 关于女性生理健康,全职/实习岗位大汇总:)
@youdaji 我们产品的市场目前大多还是在国外,所以对英文还是有一定的要求 :)
2015 年 11 月 20 日
回复了 Glowapp 创建的主题 › 酷工作 › Glow - 关于女性生理健康,全职/实习岗位大汇总:)
@KylinRoc @WildCat 如果是在上海本地念书的话,非应届在校生也可以申请我们实习生职位的 :)
@rayzworld 我们的工作环境是英文的:)
@KevinSomers 请问你当时投的是什么职位呢?如果是上述职位的话,你可以再次把相关信息发过来 :)
@youkeshen 按照要求把简历及作品发给我们吧 :)
2015 年 11 月 20 日
回复了 Glowapp 创建的主题 › 酷工作 › Glow - 关于女性生理健康,全职/实习岗位大汇总:)
2015 年 11 月 20 日
回复了 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 日
回复了 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 日
回复了 Glowapp 创建的主题 › 酷工作 › [ 硅谷创业公司 Glow ]推荐人才,赢 Macbook 大奖!
@hiStarter 早就已经加入了 hiStarter 的平台上啦 大家合作愉快 :)
@kingzeus 那还麻烦多多自荐或推荐啦 :)
@Loker 我们偶尔可以 work at home 但长期远程目前来说还是不太行哦
#这不是一条广告,这是一条认真的回复#
因为楼主所说的,好像我们 Glow 都有。我们还有免费的午餐晚餐,大量丰厚的期权奖励。
好了,打广告就停不下来噜~
@cnly1987 性别不限,只要有实力,我们都欢迎~
1  2  3  4  5  6  
❮ ❯
About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   999 Online   Highest 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 22ms · UTC 19:36 · PVG 03:36 · LAX 11:36 · JFK 14:36
♥ Do have faith in what you're doing.