V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
lijy91
V2EX  ›  分享创造

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

  •  
  •   lijy91 · 2016-12-11 21:51:03 +08:00 · 3999 次点击
    这是一个创建于 2896 天前的主题,其中的信息可能已经有所发展或是发生改变。

    「 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 )和图标,在两个系统上看起来都非常的和谐。

    参考资源

    第三方服务

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

    • DaoCloud

      使用了 Docker 镜像构建,自有主机功能。 当前项目已经完全实现自动部署。

    • 阿里云

      使用了 ECS 云主机

    • 七牛云

      使用了 云存储,免费 SSL 证书

    • 云巴

      使用了推送服务

    • GrowingIO

      用于统计

    • BugHD

      用于 Crash 收集

    • AdMob

      广告

    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": {
            ...
        }
    }
    
    • code: 错误码

      当错误码不为 0 时代表发生错误。

    • message: 错误消息
    • errors: 错误列表

      当发生多个错误时返回错误列表,客户端根据列表返回的进行相应的处理。

    • pagination: 分页对象

      仅当 data 字段为数组时才返回。

    "total": 总数
    "per_page": 每页显示数量
    "current_page": 当前页码
    "last_page": 最后一页面页码
    "from": 开始 Id
    "to": 结束 Id
    
    • data: 数据(对象 / 数组)

      在实现时使用泛型对 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>>();
    

    客户端

    文章详情( 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 或者客户端的需求欢迎加我微信

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

    捐赠

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

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

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

    域名是 打杂 的意思么?
    guotie
        16
    guotie  
       2016-12-12 10:50:54 +08:00
    才华横溢......
    yunbaIO
        17
    yunbaIO  
       2016-12-12 11:22:26 +08:00   ❤️ 1
    看到了我们产品的身影,那我肯定得过来滋次一波了!
    当然如果在使用云巴的过程当中有任何的疑问,欢迎到支持群里提问哟~(✪ω✪)
    KgM4gLtF0shViDH3
        18
    KgM4gLtF0shViDH3  
       2016-12-12 12:22:06 +08:00
    @lijy91 额,我说的是里面的资讯内容。
    lijy91
        19
    lijy91  
    OP
       2016-12-12 12:53:00 +08:00 via iPhone
    @bestkayle 收集的内容还比较少!
    junweigu
        20
    junweigu  
       2016-12-13 01:45:25 +08:00 via Android
    GrowingIO 不是收费吗
    lijy91
        21
    lijy91  
    OP
       2016-12-13 09:13:52 +08:00
    @junweigu 现在用的是入门版,可以查看一些基本的数据。
    missingbobo
        22
    missingbobo  
       2016-12-13 16:51:29 +08:00
    你用 growingIo?怎么收费的
    lijy91
        23
    lijy91  
    OP
       2016-12-14 14:54:19 +08:00
    @missingbobo 现在用的是入门版。
    xayoung
        24
    xayoung  
       2016-12-15 14:25:11 +08:00
    iOS 安装完之后没来得及点击允许权限就闪退了, 6S 10.1.1
    HLT
        25
    HLT  
       2016-12-15 21:29:04 +08:00
    laravel?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1187 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 18:44 · PVG 02:44 · LAX 10:44 · JFK 13:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.