「 daza.io 」是一款基于技能树(正在实现)的技术内容聚合应用,根据你的技能对内容进行筛选,让你在这个信息过载的时代里更高效地获取你所需的内容。
自上次发文章之后已经过了 2 个月了,我也在 11 月 19 号结束了一个人的旅行(历时 59 天)回到了深圳,专心于完成这个全端项目的客户端开发,终于在 12 月 2 号 iOS 版上线 AppStore , 12 月 7 号 Android 上线到 GooglePlay 。
最初我将 iOS 版定价为 1 元,但是后来和一朋友聊天时聊到这个项目能为用户提供什么价值的问题,后来想想目前这个项目能给用户提供的价值是有限的,所以就调为免费的了。
Star ! Star ! Star !
Android 的界面布局与 iOS 版基本保持一致,但均采用了原生的控件实现,这里就不放截图了。
快速获取(自动识别系统): http://a.app.qq.com/o/simple.jsp?pkgname=io.daza.app
已上架多个国内主流应用市场
以下是我觉得比较值得分享的小技巧。
对于我来说 UI 才是最头痛的,在没有设计师帮忙的情况下一切都得自己来了,下面是我在做 UI 时的一些经验。
我在项目里使用了 Material Design 提供的配色( Blue Grey )和图标,在两个系统上看起来都非常的和谐。
使用第三方服务就是为了减少研发成本,但一定要慎重选用。下面介绍这个项目使用的一些第三方服务。
使用了 Docker 镜像构建,自有主机功能。 当前项目已经完全实现自动部署。
使用了 ECS 云主机
使用了 云存储,免费 SSL 证书
使用了推送服务
用于统计
用于 Crash 收集
广告
使用了 REST 风格进行设计,每个接口所返回的数据结构均保持一致。
数据结构示例:
{
"code": 0,
"message": "...",
"errors": [
{
"code": 10000,
"field": "user",
"message": "用户 不存在。"
}
],
"pagination": {
"total": 10,
"per_page": 10,
"current_page": 1,
"last_page": 1,
"from": 1,
"to": 10
},
"data": {
...
}
}
当错误码不为 0 时代表发生错误。
当发生多个错误时返回错误列表,客户端根据列表返回的进行相应的处理。
仅当 data 字段为数组时才返回。
"total": 总数
"per_page": 每页显示数量
"current_page": 当前页码
"last_page": 最后一页面页码
"from": 开始 Id
"to": 结束 Id
在实现时使用泛型对 data 进行处理。
泛型数据处理示例( Java ):
public class Result<T> {
private int code;
private String message;
private List<Error> errors;
private Pagination pagination;
private T data;
public Result() {
}
public boolean isSuccessful() {
return this.code == 0;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<Error> getErrors() {
return errors;
}
public void setErrors(List<Error> errors) {
this.errors = errors;
}
public Pagination getPagination() {
return pagination;
}
public void setPagination(Pagination pagination) {
this.pagination = pagination;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
// 当 data 为 User 时的示例
new Result<User>();
// 当 data 为 User 列表时的示例
new Result<ArrayList<User>>();
文章详情页面因为排版相关原因,并没有采用原生的开发方式,而是直接加载一个外部链接
外部链接:
https://daza.io/in-app/articles/{id}
因为 WebView 此时是没有保存用户状态的,所以需要将客户端的用户登录 Token 相关信息传递给 WebView ,即在加载完毕后执行 JavaScript 代码写入。
Java:
// 开启 JavaScript 及 localStorage 支持
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);
// 页面加载完毕后将相关数据保存到 localStorage 里。
public void onPageFinished(WebView view, String url) {
String script = "javascript:";
if (Auth.check()) {
script += "localStorage.setItem('auth.id', '" + Auth.id() + "');\n";
script += "localStorage.setItem('auth.user', '" + Auth.user().toJSONString() + "');\n";
script += "localStorage.setItem('auth.jwt_token', '" + Auth.jwtToken().toJSONString() + "');\n";
} else {
script += "localStorage.clear();\n";
}
mWebView.loadUrl(script);
}
Swift:
func webViewDidFinishLoad(webView: UIWebView) {
if (!Auth.check()) {
return
}
let standardUserDefaults = NSUserDefaults.standardUserDefaults()
let authId = Auth.id();
let authUser = standardUserDefaults.stringForKey("auth.user")
let authJwtToken = standardUserDefaults.stringForKey("auth.jwt_token")
var script = ""
script += "localStorage.setItem('auth.id', '\(authId)');\n"
script += "localStorage.setItem('auth.user', '\(authUser!)');\n"
script += "localStorage.setItem('auth.jwt_token', '\(authJwtToken!)');\n"
webView.stringByEvaluatingJavaScriptFromString(script)
}
完整代码: https://github.com/lijy91/daza-ios/blob/master/Daza/Controllers/InAppBrowserController.swift
支持 DeepLink 后在 WebView 里直接可以通过自定义的 URL 来打开相应的页面,避免与 WebView 更麻烦的操作。
目前支持的链接:
daza://users/{user_id}
daza://topics/{topic_id}
daza://articles/{article_id}
daza://articles/{article_id}/comments
由于安卓的 WebView 不支持这个 DeepLink ,所以需要做一些处理:
public WebViewClient mWebViewClient = new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("daza://")) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
目前正处于自由职业的状态,如果有 API 或者客户端的需求欢迎加我微信
如果你有什么好想法想告诉我,或者想加入讨论组(注明加入讨论组),请加我微信。
如果你觉得我的工作对你有帮助,那你可以为项目捐赠运营费用。
1
designer 2016-12-11 21:57:05 +08:00 via iPhone
问我滋不滋持!当然滋持。
|
2
huang5587783 2016-12-11 22:44:30 +08:00 via iPhone
支持,但是感觉真的用处不大
|
3
hebeiround 2016-12-11 22:54:12 +08:00 via iPhone
本来我想喷下这个东西简直有一万家公司在做了,但是你这么认真,我只能说,好好干吧。
|
4
mingyun 2016-12-11 23:27:22 +08:00
全栈啊
|
5
littlewey 2016-12-11 23:40:42 +08:00 via iPhone
赞, 好羡慕 lz 的这个状态 全栈+自由。
|
6
macroideal 2016-12-12 00:08:42 +08:00 via iPhone
比较关心楼主自由职业主要做什么
另外点赞 |
7
fantasy467047 2016-12-12 05:43:46 +08:00 via Android
不错, Android 版 UI 质量很高
|
8
lijy91 OP @huang5587783 是的,慢慢在思考怎么能提供更高的价值!
|
9
lijy91 OP @hebeiround 这只是近段时间一个 Side Project ,然后慢慢思考如何提供更多的价值!
|
10
kitalphaj 2016-12-12 07:45:39 +08:00
支持一下。看了一下楼主的代码,可以看出已经有几年经验开发经验了,质量还挺高的。自己独立完成全栈绝对是终身受用的经验!
|
11
KgM4gLtF0shViDH3 2016-12-12 09:22:38 +08:00 via Android
后端都是 ruby
|
12
maijiawei 2016-12-12 09:38:02 +08:00
我看到了 yii2 的身影
|
13
maijiawei 2016-12-12 09:39:07 +08:00
不过这东西做起来感觉好然并软的样子
|
15
soli 2016-12-12 10:40:39 +08:00
滋次一下。
域名是 打杂 的意思么? |
16
guotie 2016-12-12 10:50:54 +08:00
才华横溢......
|
17
yunbaIO 2016-12-12 11:22:26 +08:00 1
看到了我们产品的身影,那我肯定得过来滋次一波了!
当然如果在使用云巴的过程当中有任何的疑问,欢迎到支持群里提问哟~(✪ω✪) |
18
KgM4gLtF0shViDH3 2016-12-12 12:22:06 +08:00
@lijy91 额,我说的是里面的资讯内容。
|
20
junweigu 2016-12-13 01:45:25 +08:00 via Android
GrowingIO 不是收费吗
|
22
missingbobo 2016-12-13 16:51:29 +08:00
你用 growingIo?怎么收费的
|
23
lijy91 OP @missingbobo 现在用的是入门版。
|
24
xayoung 2016-12-15 14:25:11 +08:00
iOS 安装完之后没来得及点击允许权限就闪退了, 6S 10.1.1
|
25
HLT 2016-12-15 21:29:04 +08:00
laravel?
|