「daza.io」个人全端项目的 Android & iOS 客户端都上线了

2016-12-11 21:51:03 +08:00
 lijy91

「 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

iOS 版

Android 版

已上架多个国内主流应用市场

一些小技巧

以下是我觉得比较值得分享的小技巧。

设计

对于我来说 UI 才是最头痛的,在没有设计师帮忙的情况下一切都得自己来了,下面是我在做 UI 时的一些经验。

  1. 选用成熟的配色方案
  2. 使用相同风格的图标(尽量使用比较全的图标库)
  3. 尽量保持简洁的界面设计(实用至上)
  4. 与系统风格保持一致
  5. 合适的字体尺寸以及边距等
  6. 设计要符合使用场景(很多使用侧边栏导航的应用就是反面教材)

我在项目里使用了 Material Design 提供的配色( Blue Grey )和图标,在两个系统上看起来都非常的和谐。

参考资源

第三方服务

使用第三方服务就是为了减少研发成本,但一定要慎重选用。下面介绍这个项目使用的一些第三方服务。

API

使用了 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": {
        ...
    }
}
"total": 总数
"per_page": 每页显示数量
"current_page": 当前页码
"last_page": 最后一页面页码
"from": 开始 Id
"to": 结束 Id

泛型数据处理示例( 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>>();

客户端

文章详情( WebView 交互)

文章详情页面因为排版相关原因,并没有采用原生的开发方式,而是直接加载一个外部链接

外部链接:

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);
}

完整代码: https://github.com/lijy91/daza-android/blob/master/app/src/main/java/io/daza/app/ui/InAppBrowserActivity.java

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 支持

支持 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 或者客户端的需求欢迎加我微信

如果你有什么好想法想告诉我,或者想加入讨论组(注明加入讨论组),请加我微信。

捐赠

如果你觉得我的工作对你有帮助,那你可以为项目捐赠运营费用。

3840 次点击
所在节点    分享创造
25 条回复
designer
2016-12-11 21:57:05 +08:00
问我滋不滋持!当然滋持。
huang5587783
2016-12-11 22:44:30 +08:00
支持,但是感觉真的用处不大
hebeiround
2016-12-11 22:54:12 +08:00
本来我想喷下这个东西简直有一万家公司在做了,但是你这么认真,我只能说,好好干吧。
mingyun
2016-12-11 23:27:22 +08:00
全栈啊
littlewey
2016-12-11 23:40:42 +08:00
赞, 好羡慕 lz 的这个状态 全栈+自由。
macroideal
2016-12-12 00:08:42 +08:00
比较关心楼主自由职业主要做什么

另外点赞
fantasy467047
2016-12-12 05:43:46 +08:00
不错, Android 版 UI 质量很高
lijy91
2016-12-12 06:30:07 +08:00
@huang5587783 是的,慢慢在思考怎么能提供更高的价值!
lijy91
2016-12-12 06:32:23 +08:00
@hebeiround 这只是近段时间一个 Side Project ,然后慢慢思考如何提供更多的价值!
kitalphaj
2016-12-12 07:45:39 +08:00
支持一下。看了一下楼主的代码,可以看出已经有几年经验开发经验了,质量还挺高的。自己独立完成全栈绝对是终身受用的经验!
bestkayle
2016-12-12 09:22:38 +08:00
后端都是 ruby
maijiawei
2016-12-12 09:38:02 +08:00
我看到了 yii2 的身影
maijiawei
2016-12-12 09:39:07 +08:00
不过这东西做起来感觉好然并软的样子
lijy91
2016-12-12 09:41:11 +08:00
@bestkayle 后端是 PHP ,框架是 Laravel
soli
2016-12-12 10:40:39 +08:00
滋次一下。

域名是 打杂 的意思么?
guotie
2016-12-12 10:50:54 +08:00
才华横溢......
yunbaIO
2016-12-12 11:22:26 +08:00
看到了我们产品的身影,那我肯定得过来滋次一波了!
当然如果在使用云巴的过程当中有任何的疑问,欢迎到支持群里提问哟~(✪ω✪)
bestkayle
2016-12-12 12:22:06 +08:00
@lijy91 额,我说的是里面的资讯内容。
lijy91
2016-12-12 12:53:00 +08:00
@bestkayle 收集的内容还比较少!
junweigu
2016-12-13 01:45:25 +08:00
GrowingIO 不是收费吗

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/326888

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX