「daza.io」是一款基於技能樹(正在實現)的技術內容聚合應用,根據你的技能對內容進行篩選,讓你在這個信息過載的時代裏更高效地獲取你所需的內容。javascript
自上次發文章以後已通過了2個月了,我也在11月19號結束了一我的的旅行(歷時59天)回到了深圳,專心於完成這個全端項目的客戶端開發,終於在12月2號 iOS 版上線 AppStore ,12月7號 Android 上線到 GooglePlay。html
最初我將 iOS 版訂價爲1元,可是後來和一朋友聊天時聊到這個項目能爲用戶提供什麼價值的問題,後來想一想目前這個項目能給用戶提供的價值是有限的,因此就調爲免費的了。java
https://daza.ioandroid
Star ! Star ! Star !git
daza-backendgithub
daza-iosswift
daza-androidapi
Android 的界面佈局與 iOS 版基本保持一致,但均採用了原生的控件實現,這裏就不放截圖了。
快速獲取(自動識別系統):
http://a.app.qq.com/o/simple....
已上架多個國內主流應用市場
如下是我以爲比較值得分享的小技巧。
對於我來講 UI 纔是最頭痛的,在沒有設計師幫忙的狀況下一切都得本身來了,下面是我在作 UI 時的一些經驗。
選用成熟的配色方案
使用相同風格的圖標(儘可能使用比較全的圖標庫)
儘可能保持簡潔的界面設計(實用至上)
與系統風格保持一致
合適的字體尺寸以及邊距等
設計要符合使用場景(不少使用側邊欄導航的應用就是反面教材)
我在項目裏使用了 Material Design 提供的配色(Blue Grey)和圖標,在兩個系統上看起來都很是的和諧。
使用第三方服務就是爲了減小研發成本,但必定要慎重選用。下面介紹這個項目使用的一些第三方服務。
DaoCloud
> 使用了 Docker 鏡像構建,自有主機功能。 > 當前項目已經徹底實現自動部署。
阿里雲
> 使用了 ECS 雲主機
七牛雲
> 使用了 雲存儲,免費 SSL 證書
雲巴
> 使用了推送服務
GrowingIO
> 用於統計
BugHD
> 用於 Crash 收集
AdMob
> 廣告
使用了 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>>();
文章詳情頁面由於排版相關緣由,並無採用原生的開發方式,而是直接加載一個外部連接
外部連接:
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/daz...
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/daz...
支持 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或者客戶端的需求歡迎加我微信
若是你有什麼好想法想告訴我,或者想加入討論組(註明加入討論組),請加我微信。
若是你以爲個人工做對你有幫助,那你能夠爲項目捐贈運營費用。