Android Weekly Notes Issue #227

来源:http://www.cnblogs.com/mengdd/archive/2016/10/21/5984935.html
-Advertisement-
Play Games

Android Weekly中文筆記, Issue #227. 本期內容包括: Google的Mobile Vision API 人臉檢測; Firebase的Remote Config; 與HashMap有關的優化; 提高RecyclerView幀率的優化; 使用AutoValue生成model代... ...


Android Weekly Issue #227

October 16th, 2016
Android Weekly Issue #227.

本期內容包括: Google的Mobile Vision API 人臉檢測; Firebase的Remote Config; 與HashMap有關的優化; 提高RecyclerView幀率的優化; 使用AutoValue生成model代碼; 開源庫中抽象類和介面的使用討論; Bottom Sheet的使用; Android Studio中的版本控制系統; ConstraintLayout的使用; 應用換Bottom Navigation; Nougat的Messaging Style Notification; 自定義字體; Reductor的使用等.

ARTICLES & TUTORIALS

Face Detection Concepts Overview

這篇文章來自Mobile Vision, 講人臉檢測及相關概念.
API使用Tutorial.
Sample.

Exploring Firebase on Android & iOS: Remote Config

Remote config是Firebase提供的一個feature, 讓我們可以定義參數, 在firebase的console管理, 從而在server端控制應用的UI或者行為, 並且可以選擇生效的用戶範圍.

之前還有這個文章是關於Firebase Analytics的.

本篇文章介紹了Firebase的Remote Config可以乾什麼, 以及怎麼做, 解說很詳細.

參數
我們用Remote Config定義的鍵值對叫參數(parameters). 它提供了這個參數相關的what信息(key, the identifier), 和how信息(value, the configuration).

條件
條件值(conditional value)也是一個鍵值對, 其中condition指定了需要滿足的條件, value指定了滿足條件時需要返回的值.

優先順序
如果單個條件被滿足, 那麼返回對應的值; 如果多個條件都被滿足, 那麼返回主導條件(list上方的條件)對應的值; 如果沒有條件滿足, 則返回預設值; 如果沒有定義預設值, 則什麼也不返回.

文中還詳細介紹了Android和iOS端的實現, 以及console的配置.

Android App Optimization Using ArrayMap and SparseArray

當我們需要存儲鍵值對的時候, 我們總是首先想到用HashMap, 然而IDE(Android Studio)有時候會警告提醒你, 應該用ArrayMapSparseArray.

HashMap vs ArrayMap

ArrayMap比傳統的HashMap更節省記憶體, 因為它把自己的映射放在數組結構中: 一個整型數組放每一個item的hash code, 一個Object數組放key/value對. 這樣避免了為每一個entry創建額外的對象, 而且數組增長也好控制.

註意ArrayMap並不是為很大的數據集設計的, 並且它會比HashMap慢一些, 以為查找需要二分查找, 增刪需要在數組中操作.

HashMap

HashMap是一個HashMap.Entry的數組, 其組成是key, value, HashCode, 還有一個指針.

當進行插入時: 首先計算出key的HashCode, 然後用這個hashCode找到對應的bucket, 如果已經存了元素, 則把舊元素的指針指向新元素, 即把bucket變為一個LinkedList.

當進行查詢時: 複雜度為O(1), 但是這樣是犧牲了更多的空間複雜度得到的.

HashMap的缺點:

  • 因為key和value都不能是原生類型, 所以插入時可能會有自動裝箱, 導致創建額外的對象.
  • HashMap.Entry本身就是一層額外的對象.
  • 每次HashMap的收縮或者擴張, Buckets都要重新排列, 隨著對象變多, 這個操作變得越發昂貴.

ArrayMap

ArrayMap使用兩個數組:
int[] mHashes用來存哈希值; Object[] mArray來存對象.

當插入鍵值對時: Key/Value被自動裝箱, Key被插入到mArray[]數組的下一個位置, Value也被插入到mArray[], 在Key的下一個位置.
計算出的哈希值被放在mHashes[]的下一個位置.

當查詢一個Key時: 首先計算出Key的哈希值, 在mHashes中二分查找這個hashCode(時間複雜度(logN)), 當得到hash的index之後, 我們就知道mArray2*index2*index+1的位置對應的是查找的key和value.

雖然時間複雜度提升了, 但是這樣卻更省空間.

推薦的數據結構:

  • ArrayMap<K,V> in place of HashMap<K,V>
  • ArraySet<K,V> in place of HashSet<K,V>
  • SparseArray<V> in place of HashMap<Integer,V>
  • SparseBooleanArray in place of HashMap<Integer,Boolean>
  • SparseIntArray in place of HashMap<Integer,Integer>
  • SparseLongArray in place of HashMap<Integer,Long>
  • LongSparseArray<V> in place of HashMap<Long,V>

RecyclerView: How we achieved 60 FPS in Workable’s Android App

我們經常會用RecyclerView來顯示一個list的數據.
作者他們做的是一個招聘應用: Workable, 其中會用list來顯示candidates.
他們還使用了DataBinding.
本文是作者他們關於RecyclerView的幀率所做的一些優化.

首先他們使用了Android Studio的Allocation Tracking, 然後上下滾動, 從報告發現, 他們佈局中使用的TableLayout花費了很多資源, 於是後來他們改為LinearLayout加權重的方式來解決, 擺脫了耗費資源的TableLayout.

另一個引起很多資源分配的問題是, 對於需要大寫的文字, xml中的:

<TextView
          ...
  android:textAllCaps="true"
          ...
/>

TextView的代碼中會為此生成一個對象:

  if (allCaps) {
      setTransformationMethod(new AllCapsTransformationMethod(getContext()));
  }

這個在靜態的佈局中可能沒有問題, 但是在一個滾動的list中可能會有些影響.

改進方法是改為用java String的.toUpperCase().

然後他們使用了RecyclerView的.onViewRecycled()方法. 這個方法讓我們知道了RecyclerView中的一行何時被回收, 這樣我們就可以釋放一些不需要的資源.
他們使用了DataBinding, 所以這是一個合適的時機來刪除ViewModel中的OnPropertyChangedCallbacks, 然後清理ViewModel自身, 我們還可以清理之前用Glide load到ImageView中的圖片.

@Override
public void onViewRecycled(Candidates holder) {
    if(holder != null) {
        holder.binding.getCandidateVM().removePropertyChangedCallback();
        holder.binding.setCandidateVM(null);
        holder.binding.setHighlightTerm(null);
        holder.binding.setShowJobTitle(false);
        holder.binding.setShowStage(false);
        holder.binding.executePendingBindings();
        Glide.clear(holder.binding.candidateBrowserAvatar);
        holder.binding.candidateBrowserAvatar.setImageDrawable(null);
    }

    super.onViewRecycled(holder);
}

作者他們的應用還有一些cache設置:

binding.fragmentCandidateBrowseList.setItemViewCacheSize(30);
binding.fragmentCandidateBrowseList.setDrawingCacheEnabled(true);
binding.fragmentCandidateBrowseList.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);

之後作者測量了他們的FPS, 顯示是60 FPS, 並且發現去掉這些cache設置仍然是60.

測量幀率FPS的工具: TinyDancer.

No more value classes boilerplate — The power of AutoValue

在Java/Android編程中經常需要寫model對象來存放一些數據, 使用Google的庫AutoValue可以幫你自動生成這些類, 你需要做的就是定義你的欄位, 然後給類加上註解.

Setup

在project的build.gradle中:

buildscript {
    [...]
    dependencies {
        [...]
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

在app的build.gradle中:

apply plugin: 'com.neenbedankt.android-apt' // At the beginning
[...]
dependencies {
    [...]
    provided "com.google.auto.value:auto-value:1.2"
    apt "com.google.auto.value:auto-value:1.2"
}

基本使用

比如要創建Film類, 你可以寫一個這樣的抽象類:

@AutoValue
abstract class Film {
    static Film create(String name, int year) {
        return new AutoValue_Film(name, year);
    }

    abstract String name();
    abstract int year();
}

每一個欄位都對應一個抽象方法.
build一下, AutoValue_Film類就會自動生成, 加上靜態工廠方法(上面的create()方法) 然後就可以使用工廠方法來得到model:

Film matrix = Film.create("The Matrix", 1999);

點進自動生成的類AutoValue_Film里可以看到, 連hashCode()equals()方法都生成了.

用builder模式

上面的例子隨著欄位的增多, create()方法的參數會變得很多, 用起來不方便, 那麼此時就需要用Builder模式:

@AutoValue
abstract class Film {

    static Builder builder() {
        return new AutoValue_Film.Builder();
    }

    @AutoValue.Builder
    abstract static class Builder {
        abstract Builder setName(String value);
        abstract Builder setYear(int value);
        abstract Film build();
    }

    abstract String name();
    abstract int year();
}

這樣就可以很方便地加參數了:

Film matrix = Film.builder()
  .setName("The Matrix")
  .setYear(1999)
  .setCategory(Category.FANTASY)
  .setRating(8.7f)
  .setDuration(136)
  .setReleaseDate(releasedDate)
  .setDirectors(directorsList)
  .setCast(castList)
  .build();

AutoValue擴展 Parcelable

有時候你需要在Activity之間傳數據, 需要你的model是Parcelable的, 此時你就可以用這個auto-value-parcel, 在代碼里也只需要實現這個介面:

@AutoValue
abstract class Film implements Parcelable {
   [...]
}

還有很多的擴展庫: extensions for AutoValue, 比如AutoValue-Gson, AutoValue-Cursor, AutoValue-With, AutoValue-Redacted等.

Consider abstract class instead of interface

這篇文章的作者說, 在library開發中, 應該考慮用抽象類而不是介面. 他的庫是AdapterDelegates.

作者先介紹了通用的概念比較:

  • class vs. interface
    介面更解耦, 更靈活, 只是定義了一個協議, 不限制實現.
  • interface vs. abstract class
    抽象類會有繼承的問題, 基類和子類會共用一些實現, 所以子類的編寫者最好能清楚基類的實現, 這樣才不會在寫子類實現抽象方法的時候打破了基類作者的意圖. 另外就是基類作者仍然可能會更新基類, 所以得時刻檢查子類是否還是符合基類的設計意圖.

但是為什麼作者還是要把自己庫中的介面改為抽象類呢? 這是因為作者的庫依賴於Android的庫, Android的庫中相關代碼改了, 作者就得改自己的public介面, 加一個方法, 導致所有新版的使用者也都必須實現這個方法.

還有一個情況就是比如一個開發者使用了2.1版本, 但是他項目里依賴的另一個第三方庫使用了2.0版本. 編譯不會出錯, 最終的apk中打包的是2.1版本. 然後在這個第三方庫的組件里調用2.1才有的新方法時就會拋出錯誤.

為瞭解決這個問題, Jake Wharton建議在庫的主要更新(major update)中更改發佈的package name和group id: http://jakewharton.com/java-interoperability-policy-for-major-version-updates/

作者覺得那每次Android RecyclerView的Adapter更新都會導致自己的庫major update, 所以他決定把自己的AdapterDelegate介面改為抽象類. 這樣他就可以對新增的方法提供預設空實現.

這樣定義的抽象類只有抽象方法和一些空實現的方法, 並沒有狀態和行為的共用可能會傳播給子類, 其實和介面是一樣的.

Android BottomSheetDialog

實現bottom sheet的時候, 有三種選擇: container view + BottomSheetBehavior, BottomSheetDialogFragment, BottomSheetDialog. 前兩種的例子比較多, 作者要介紹的是第三種.

如何選擇取決你的用途, container view + BottomSheetBehavior 適用於persistent bottom sheet, 而BottomSheetDialogFragmentBottomSheetDialog適用於Modal bottom sheets.

之後作者提供了實現代碼, 附有theme定製和狀態callback的設置.

The VCS client of Android Studio

這篇文章介紹Android Studio的版本控制系統.

在Android Studio 2.2開始, 加入了一個Create command line launcher, 這樣你就可以在命令行或者第三方的版本控制客戶端使用Android Studio的diff/merge tool了.
作者使用的客戶端是SourceTree.

cmd + shift + A可以用來find action, 然後就可以找到Compare with branch: 可以比較當前文件和某個分支上的文件的diff;
另外還可以Compare with..., 來比較和之前某一個特定提交的diff; 以及Compare with Clipboard來和剪貼板做比較.

還有一些其他有用的快捷鍵, 請看原文吧.

Constraint Layout: Icon Label Text

作者想做這樣一個UI, 左邊是一個icon, 右邊是兩行字, icon的top和bottom分別和第一行字的top和bottom對齊.
ConstraintLayout: Icon Label Text

怎麼做呢? 她想到了用ConstraintLayout.
代碼在這裡: iconlabeltext

Bye, Bye Burger

作者他們的應用從burger menu改為bottom navigation, 此篇為心得分享和他們改版時設計中的一些細節討論.

其中狀態保存是一個最主要的技術問題.

改版之後, 作者他們的應用數據表明有以下幾個好處:

  • 用戶參與度提升了;
  • 在底部導航有入口的功能使用率提高了;
  • 並沒有用戶反饋說新的導航不好.

Nougat – Messaging Style Notifications

Messaging Style Notifications是為信息應用特殊設計的, 提供了一個像對話一樣的view.

Messaging-style notifications和Bundled notifications的主要區別是, Bundled notifications中我們持續創建新的notification, 然後它們被grouped together. 但是用Messaging-style notifications的時候, 我們只有一個notification, 然後我們把所有的信息添加進去.

作者展示了實現代碼和效果, 註意這個Messaging style並不是後項相容的, 只在Nougat及以後的版本才支持.

Bottom sheet everything

作者介紹了他的應用中對於Bottom sheet的使用.

Deep linking with bottom sheet Activity
作者用它處理Deep linking, 這樣用戶就不用每次都全屏打開, 只先提供一個peek, 如果真的感興趣再打開.

實現是用一個透明的Activity, 還有狀態欄處理的細節.

Bottom sheet settings menu
關於Settings, 為了節省用戶的trip, 作者它們的應用用了options menu的彈出菜單. 後來他們改用bottom sheet來實現, 並且結合了PreferenceFragmentCompat, 省去了一些SharedPreferences的讀寫操作.

Supporting tablet users
bottom sheet在平板上使用, 尤其是橫屏的時候, 看起來不太好.
所以作者定製了Bottom sheet的寬度, 在平板上時是一個指定寬度, 在手機上維持原狀.

Machine Learning for with the Mobile Vision API— Part 1

基於Google的Mobile Vision APIs現在Android開發者可以在應用里用上機器學習了. 現在這個Mobile Vision API包括三種類型Face Detection API, Barcode Detection API和Text API.

本文主要講人臉檢測部分, 後面會講二維碼檢測和文字的API.

作者的demo展示瞭如何從一個靜態照片中檢測出人臉區域, 並且標記出landmark(眼睛, 鼻子, 嘴巴等), 之後可以根據這些特征位置加上一些覆蓋標記.

sample code.

Custom fonts formatting, the simple way

在Android中自定義字體的一個庫: Calligraphy.

如果你的輸入是html文字, 你想自動處理裡面的tag(比如), 用另一種字體, 怎麼處理呢, 作者給出了代碼.
custom font in one textview

完整的例子代碼見: sample code.

Reductor - Redux for Android. Part 1: Introduction

之前這個文章介紹過Reductor, 在Android Weekly之前也出現過, 我的筆記: Android Weekly Notes Issue 224.

Reductor是一個狀態管理的庫, 用Java重新實現了JavaScript的庫Redux.
它的中心思想:
redux idea

之前的一篇文章做了一個TODO app, 然後作者發現這種mutable的數據會導致失控的數據改變, 然後可能會出現無法預測的行為. 做了一些改動之後, 我們發現可以通過只保存一個immutable的對象和mutable的reference來避免這個問題.

這篇文章用Reductor來重新實現應用, 文中詳細說明瞭代碼實現.

LIBRARIES & CODE

ImageTransition

一個很小的庫, Activity直接的shared element transition動畫, 把一個圓形的ImageView變換到下一個Activity的方形ImageView.

Design-Patterns-In-Kotlin

用Kotlin實現的設計模式.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 關於CAShapeLayer的一些實用案例和技巧實現遮罩音量大小動態改變的控制項圓形進度條iOS 利用CAShapeLayer的FillRule屬性生成一個空心遮罩的layerfillrule屬性。和電腦圖形學有關為視圖添加絲滑的水波紋利用餘弦函數 ...
  • 1.基本思路 ①.創建已加鎖應用的資料庫(欄位:_id,packagename),如果應用已加鎖,將加鎖應用的包名維護到資料庫中 ②.已加鎖+未加鎖 == 手機中所有應用(AppInfoProvider) 2.已加鎖和未加鎖的數據適配器 1 class MyAdapter extends BaseA ...
  • ⌥—> option|alt ⇧—>shift ⌃—>control ⌘—>command ⎋—>esc ↑↓←→ Code ⌥—> option|alt ⇧—>shift ⌃—>control ⌘—>command ⎋—>esc ↑↓←→ Code alt+F7:Find usage alt+co ...
  • 1.AndroidManifest.xml根據窗體小部件廣播接受者關鍵字android.appwidget.action.APPWIDGET_UPDATE 搜索android:resource="@xml/process_widget_provider" 2.找到xml文件夾下process_wid ...
  • 前言 學習本系列內容需要具備一定 HTML 開發基礎,沒有基礎的朋友可以先轉至 "HTML快速入門(一)" 學習 本人接觸 React Native 時間並不是特別長,所以對其中的內容和性質瞭解可能會有所偏差,在學習中如果有錯會及時修改內容,也歡迎萬能的朋友們批評指出,謝謝 文章第一版出自簡書,如果 ...
  • TextView 顯示文本框控制項, EditText 輸入文本框 ...
  • SharedPreferences 一種輕量級的數據保存方式 以鍵值對的方式存儲 用於存儲小批量的數據 使用方法: SharedPreferences sp= getSharedPreferences("myopt"(文件名),Context.MODE_PRIVATE(數據存儲方式)); 存儲文件名 ...
  • 手機APP在日常生活中越來越與我們息息相關,既然這些APP是用戶每天都必須接觸到的,所以APP更應該從用戶的角度來進行設計。APP開發並沒有什麼固定的法則,也沒有現成的模式可依,只有不斷從用戶的實踐中,從用戶的一言一行當中去考慮用戶的體驗才能設計出用戶體驗良好的APP。 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...