1. 程式人生 > >前端寫一個月的原生 Android 是怎樣一種體驗?

前端寫一個月的原生 Android 是怎樣一種體驗?

一個前端程式設計師的一個月原生 Android 開發體驗。自從我寫了 Android 應用後,上知乎的時間變得更長了。

自從我寫了 Android 應用後,上知乎的時間變得更長了。哦,不對,你理解錯了,我的意思是:編譯程式碼、打包 APK、執行在裝置上需要時間。可不像前端,一儲存程式碼,就自動重新整理頁面。

是的,從上上週一開始,因為專案缺人的原因,作為一個有 Java 開發經驗的大前端,我又又雙叕進入了原生 Android 開發的世界。

這一個月下來,也算是有一些寫 XML 的心得吧——不對,寫 Java 程式碼,看 Kotlin 程式碼的心得。總的來說,Android 與前端的差異並不是非常大,在某些東西上,他們還是蠻相似的。怪不得像我這樣的程式設計師,會將 Android 開發也歸類到大前端上去。

如果你是一個前端程式設計師,想學習移動開發;又或者是一個移動開發,想接觸前端開發;那麼,本文可能就很適合你去了解兩者間的差異。

本文包含了以下的內容:

  • 編碼效率 vs 可維護度

  • MVP vs MV後天的 MV*

  • 靜態語言 vs 動態語言

  • View 與 DOM

  • 程式碼除錯

  • 相容性

(PS:受限於我只有短暫的經驗,所以有些用詞可能沒有那麼準確。)

:這裡的前端應用特指單頁面應用

編碼效率 vs 可維護度

因為從執行效率上來說,原生應用必須遠遠大於 WebView——畢竟 WebView 的背後還是原生應用,直接等於中間多了一個層級。所以,在這裡直接討論編碼效率。

0?wx_fmt=jpegWeb

從編碼效率上來說,還是前端快,快得不止一點點。

  • 更快的預覽速度。

  • 成熟的生態系統。

  • 大量可用的 UI 框架及元件。

  • 參考別家的實現。Web 前端是開放的世界,在今天來看,要實現的效果基本上已經被實現過了,所以我們可以直接參考

  • 富文字支援好

而考慮到 Android 和 iOS 是各自實現的,那麼一個混合應用的開發效率可能是遠遠大於 2 倍,而跨平臺應用(如 React Native、Weex、NativeScript) 的開發效率會接近他們的 2 倍(原因是:整合某些功能時,需要原生程式碼來實現,這時工作量直接翻倍等同)。

0?wx_fmt=jpegAndroid

從目前的維護程度上來說,還是 Java 的程式碼相對維護。主要是前端領域的變化太快了,並且在軟體工程上的實踐不像 Java 是必需要求的,因此容易出現大量的遺留程式碼。只是考慮到,Java 程式碼的臃腫,還是改用 Kotlin 吧。

0?wx_fmt=png
Android Studio 轉 Kotlin

只需要按下: Command + Alt + Shift + K,輕鬆當爸爸。

MVP vs MV後天的 MV*

MVP,即 Model-View-Presenter,對應於檢視層-資料層-展示層。

在 MVP 上來看,前端應用與 Android 都並非天生的 MVP 架構的。不過,兩者在對業務邏輯上的處理,但是沒有多少差異。唯一能體驗差異的,可能就是 JavaScript 的非同步,以及 Java 的同步帶來的一些差別。

V*

採用了框架的前端應用,則會因此而帶上 MV* 的加成。一旦選用上了某個框架,那麼你只能按照其特有的模式,如 Vue 提供的核心是 MVVM 中的 VM,React 則只是 MVC 中的 View 層,則 Angular 則可能是 MVW(Model-View-Whatever)。在這種情況下,要在框架的基本之上變更,那麼靈活性上可能沒有那麼大。

0?wx_fmt=png

而 Android 方面則是 MVP 架構,其主要依賴於約定俗成,其中一個參考的規範就是 Google 官方的 android-architecture,又或者是社群上推薦的 Clean Architecture。而無論是 Clean Architecture,還是 MVP,其都依賴於約定。一旦我們談及參考的時候,便意味著靈活性——可遵循,可不遵循。

在這種時候,Android 的 MVP 需要我們自己去建立 MVPView,建立 Presenter。

  1. publicclassMainActivityextendsAppCompatActivityimplementsMainView{

  2. ...

  3. }

而整個 MainActivity 只是一個 View 層,真正的業務邏輯要交給 Presenter 來處理。簡單來說,就是你需要手動地建立四五個類,才能完成一個 Activity 的 Hello, world。

Model

與此同時,Android 預設是要對 Model 進行校驗和轉換的。因為取出 JSON 中的某個值,需要將 JSON 轉換為物件——可以直接使用 Retrofit 庫來轉換資料,又或者用 GJSON 轉換成某種物件。算是與前端的一個大的區別,在前端世界裡,這種事情是輕而易舉的,有萬能的 JSON.parse

在使用 JavaScript 編寫的時候,可以不對 Model 進行校驗。不過,在 React 裡會有proptypes,在 Angular 裡可以用 TypeScript 來做相似的事。

與沒有物件校驗的前端相比,一旦出錯,根本不容易察覺。這一點,或者也是一個優勢所在——當你上架了新版本的 API 時,舊的應用不會 NullPointerException。與此同時,在開發的時候,後臺 API 發生變化的時候,也會導致後續的一系列 bug。

靜態語言 vs 動態語言

自從我寫了 Android 應用後,上知乎的時間變得更長了。

編譯與動態執行

當我們編寫 Web 應用的時候,只要一儲存程式碼,網頁就可以由 LiveReload 這樣的工具來幫我們自動重新整理。於是,在諸如 React Native 這樣的跨平臺框架裡,也有 Live Reload 這樣的特性。

而當我開發 Android 應用的時候,每次我想試著在手機上檢視效果的時候,得構建、編譯程式碼、安裝,大概得等上個兩三鍾才能執行在虛擬機器或者真機上。

0?wx_fmt=png
Android Studio Process

可事件往往不會這麼順利,動不動會遇上個 NullPointerException,然後應用就 Crash 了。這個時候,就要去修復程式碼中的問題,加個 blabla!=null,然後編譯,繼續 Crash。

怪不得 Android 的程式設計師喜歡上了 Kotlin,只要一個 view? 就能判斷是不是有值的事:

  1. override fun onCreateView(inflater:LayoutInflater?, container:ViewGroup?, savedInstanceState:Bundle?):View?{

  2.    val view = inflater?.inflate(R.layout.fragment_home, container,false)

  3.    val button:Button= view!!.findViewById(R.id.open_rn_button)

  4.    button.setOnClickListener(this)

  5. return view

  6. }

可由於沒有經驗,我經常把 val 寫成了 var。這就和那些習慣寫 alloc init 的 iOS 程式設計師,一夜間突然喜歡上了寫 ES6 一樣:

  1. let className =NSStringFromClass(MyClass)

  2. let classType =NSClassFromString(className)as?MyClass.Type

  3. iflet type = classType {

  4. letmy= type.init()

  5. }

哦,不對他們寫的是 Swift。

並且作為一個面向物件的語言,Java 天生就意味著,大量的臃腫程式碼。

  1. publicint getId(){

  2. return id;

  3. }

  4. publicvoid setId(int id){

  5. this.id = id;

  6. }

  7. publicString getName(){

  8. return name;

  9. }

  10. publicvoid setName(String name){

  11. this.name = name;

  12. }

大量的程式碼,就意味著大量的 bug,一定量的重複程式碼,一下子又回到設計模式的天下。

IDE 支援

好在,由於 Android Studio 有強大的、良好的 Intellij 支援。在 IDE 上對語言的支援,要比 JavaScript 的第三方庫支援友好得多:

0?wx_fmt=png
靜態語言

要知道 WebStorm 或者 Intellj IDEA 專業版,它們在 JavaScript 第三方類的支援上就是坑。

View 與 DOM

過去,前端在 DOM 操作上存在天然的問題,即在我們使用 $("*") 的時候,全域性。當然現今的框架,在這個問題上比較少,但是考慮到仍然可能會被誤用,或者注入。而 Android 則是區域性頁面的。

樣式複用

前端使用 HTML + CSS 來編寫樣式,而安裝則只使用 XML 來切圖,這並不是一件容易的事。不像 CSS 可以通過 “繼承” 和 “覆寫” 的形式來實現樣式複用。Android 中也有類似於 JavaScript 生成 HTML 的方式,自定義模板。

當我們使用 React 編寫元件的時候,可以傳遞對應的屬性到元件中,這個屬性可以是函式、值、元件等等。

  1. MyComponent.propTypes ={

  2.  optionalArray:PropTypes.array,

  3.  optionalBool:PropTypes.bool,

  4.  optionalFunc:PropTypes.func,

  5.  optionalElement:PropTypes.element

  6. }

而在 Android 的佈局上,這就不是一樣容易的事。為了複用樣式,需要抽取成 UI 元件,還只能是 UI 上的元件。只能實現 HTML + CSS 上的複用。

HTML + CSS 在編寫 UI 的時候,有各種奇技淫巧,比如說樣式的優先順序,或者 important

雙向繫結

從原生的角度來看,前端的 document.getElementById() 與 Android 的 findViewById 並沒有多大的區別。而當前端有了前端框架之後,就不一樣了。好在 Android 有 ButterKnife 這樣的 View 注入框架。

與此同時,Android 還自帶了雙向的 DataBinding,而原生的前端是沒有的。

只是前端有前端框架,在這一點也完全問題也不多。

佈局除錯

還好,已經有寫 React Native 佈局的一些經驗,在寫起 Android 的佈局,倒也還好——沒有那麼坑。

在佈局除錯上,還是前端用瀏覽器調式方便——還可以在瀏覽器實時修改 DOM 結構。Android 也有這樣的工具,叫Layout Inspector

0?wx_fmt=png
Layout Inspector

除此,還可以通過 Facebook 家的 stetho 做與 Web 相關的除錯工作:

0?wx_fmt=png
Stetho 除錯示例

總的來說,還算是不錯的。就是這個結構,看上去和 React Native 怎麼那麼樣呢?

程式碼除錯

在程式碼除錯上來說,Java 底子厚,總的來說會比 JavaScript 好一些。

0?wx_fmt=png
Android 除錯

除此,記得我們在 Chrome 瀏覽器裡可以打斷點,隨後在 Console 中做出一些計算。而得益於 Android Studio 背後的 JetBrain 的 Evaluating Expressions,可以實時計算表示式的值,Android 上的程式碼除錯也是很容易的。

0?wx_fmt=png
Evaluating Expressions

而以我有限的 Objective-C 程式設計經驗來說,XCode 也是可以做到的。

網路除錯

在 Chrome 瀏覽器裡,自帶的 NetWorks 幾乎是萬能的。Android 方面也可以藉助於 Stetho 來使用:

0?wx_fmt=png
Stetho 網路除錯

但是依賴上比較大,需要在頁面上注入,並且除錯不了外掛化的應用。要除錯網路吧,還是 Charles 好用一些。

0?wx_fmt=png

可是,萬一開發環境 HTTPS 了呢,不就更麻煩了。

相容性

前端面臨的是除錯不同的瀏覽器,又或者是相容 IE。總的來說,問題都不大——不會面臨閃退的問題。即使出了點小問題,使用者可以先換個瀏覽器試試。而當你的 Android 應用在使用者的手機上閃退了,那麼使用者只能換個 APP 了。

0?wx_fmt=png

除此,Android 則是面臨碎片化的系統,不同的版本,及不同的螢幕大小,總的來說,要對前端複雜得多。

結論

Android 在軟體工程上做得相當優秀,而前端則是在開發效率上佔優勢。

Web 開發大法好。

0?wx_fmt=jpeg