Android 開發者的養成之路

與開源工具

Rick

開發 App 會導入很多第三方

隨著 App 架構越來越複雜

使用第三方工具是必備技能

所以我們稱開發者為

工具人

今天想分享以下內容

ORMLite

OkHttp

EventBus

GreenDAO

MVVM

Crashlytics

MPChart

Picasso

Firebase

Dagger2

Glide

Git

RxJava

Unit4

Room

Gson

 Retrofit

Koin

Kotlin

Volley

今天想分享以下內容

ORMLite

OkHttp

EventBus

GreenDAO

MVVM

Crashlytics

MPChart

Picasso

Firebase

Dagger2

Glide

Git

RxJava

Unit4

Room

Gson

 Retrofit

Koin

Kotlin

Volley

五個主題

  • Architecture

  • Database

  • Image Process

  • Http Connection

  • ReactiveX

Android Architecture

• No Architecture
• MVC(Model-View-Controller)
• MVP(Model-View-Presenter)
MVVM(Model-View-ViewModel)

No Architecture

override fun onCreate(savedInstanceState: Bundle?){
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //100000 lines
}

No Architecture

override fun onCreate(savedInstanceState: Bundle?){
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    try{
        //100000 lines
    } catch (exception: Exception){
        //handle
    }
}

No Architecture

架構軟體如同蓋教堂,完成之後就可以開始祈禱惹。

                                                                  - Sam Redwine

MVC

MVP

MVVM

MVC vs MVP vs MVVM

​MVC ​MVP ​MVVM
​Google  daddy support Yes No No
​Learning curve Easy Normal Normal
​Testability Hard Easy Easy
​Lifecycle-aware Hard Hard Easy

Image Process

  • Universal Image Loader

  • Picasso[Square]

  • Fresco[Facebook]

  • Glide[This is not an official Google product.]

Universal Image Loader

Really have no time for development... so I stop project maintaining since Nov 27 :(

UIL [27.11.2011 - 27.11.2015]

Thanks to all developers for your support :)

Picasso vs Fresco vs Glide

Picasso Fresco Glide
Support gif No Yes Yes
Circle image No Yes Yes(4.0+)
Support webP Yes Yes Yes
Android lifecycle No No Yes
Cache Large Normal Small
Container ImageView DraweeView ImageView

Database

  • SQLite

  • GreenDao

  • ORMLite

  • Realm

  • ...

  • Room[Google support]

早期操作資料庫相當麻煩

public static final String TABLE_NAME = "item";
public static final String KEY_ID = "_id";
public static final String DATETIME_COLUMN = "datetime";
public static final String COLOR_COLUMN = "color";
public static final String CREATE_TABLE = 
    "CREATE TABLE " + TABLE_NAME + " (" + 
    KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
    DATETIME_COLUMN + " INTEGER NOT NULL)";

SQLite 操作

後來 DAO 出現

@DatabaseTable(tableName = "User")
public class User {
    @DatabaseField(generatedId = true) private int id;
    @DatabaseField private String name;
    @DatabaseField private int sort;
    @ForeignCollectionField private ForeignCollection<Group> groups;
}

ORMLite 操作

GreenDAO: 我比ORMLite快

Realm: 我比GreenDAO快

ORMLite: 我最穩定

Database 大亂鬥

ROOM: 他們在吵什麼?

ROOM

Querying multiple tables

@Dao
interface MyDao {
    @Query(
        "SELECT user.name AS userName, pet.name AS petName " +
        "FROM user, pet " +
        "WHERE user.id = pet.user_id"
    )
    fun loadUserAndPetNames(): LiveData<List<UserPet>>

    // You can also define this class in a separate file.
    data class UserPet(val userName: String?, val petName: String?)
}

Http Connection

  • HttpClient

  • HttpURLConnection

  • OkHttp

  • Volley

HttpClient vs HttpURLConnection

For Gingerbread and better, HttpURLConnection is the best choice. New applications should use HttpURLConnection.

  - Google (29 September 2011)

HttpURLConnection

  • 複雜操作

  • 需自己實作 Callback

  • 切換執行緒不方便

Volley and OkHttp

  • 不需自行實作Callback

  • 操作簡單方便

  • 執行緒切換有各自的處理方式

OkHttp Interceptor

Retrofit API interface

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

@POST("users/new")
Call<User> createUser(@Body User user);

class User{
    @SerializedName("user_name")
    private String userName;
    @SerializedName("user_age")
    private int userAge;
}

Retrofit + RxJava

interface ApiService {
    @GET("/posts")
    fun getPosts(): Single<List<Posts>>
}


fun loadInfo(): Single<List<Posts>> {
    return apiService.getPosts()
}

AsyncTask vs Volley vs Retrofit  

HttpURLConnection Volley  OkHttp
Callback No Yes Yes
Easy to use No Yes Yes
Interceptor No No Yes
API Interface No No Yes
RxJava No No Yes

RxJava 

先說明一下什麼是事件

手電筒打開就是一個事件

不同的事件有不同的Interface

public interface OnClickListener {
    void onClick(View var1);
}

public interface OnCapturedPointerListener {
    boolean onCapturedPointer(View var1, MotionEvent var2);
}

public interface OnApplyWindowInsetsListener {
    WindowInsets onApplyWindowInsets(View var1, WindowInsets var2);
}

public interface OnKeyListener {
    boolean onKey(View var1, int var2, KeyEvent var3);
}

如果有一個統一的 Interface就好了

public class Callback<T> {
    public void onResult(T result);
}
subscribe(Consumer<? super T> onNext, 
    Consumer<? super Throwable> onError, Action onComplete, 
    Consumer<? super Disposable> onSubscribe)

subscribe(final Consumer<? super T> onSuccess, 
    final Consumer<? super Throwable> onError)

subscribe(Consumer<? super T> onSuccess)

RxJava 統一所有的Interface

切換執行緒

Thread {
   //do something
   runOnUiThread {
      //update ui
      Thread {
          //do something
          runOnUiThread {
              //update ui
      
          }
      }      
   }
}

RxJava 切換執行緒容易

Observable.just(1,2,3)
    .subscribeOn(Schedulers.newThread())
    .map{}
    .observeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe{}

順便也解決了 Callback Hell

有一天你家來了一個

屁孩

他不斷開關你的手電筒

此時該怎麼辦

第一時間當然是巴蕊

RxView.clicks(throttleFirstButton)
    .throttleFirst(ANIMATION_TIME, TimeUnit.MILLISECONDS)
    .subscribe{
    }

RxJava解決了

手速很快的事件

解決製造問題的人

想像有A,B,C三個class, B觀察A, C觀察B, 就要自己實作兩個觀察者

class A{
    fun main(){
       B().bListener()
          .subscribe{}
    }
}

class B{
    fun test(){
       C().bListener()
          .subscribe{}         
    }
    fun bListener():Single<CData>{
        return getData()
    }
}

class C{
    fun cListener():Single<CData>{
       retrun service()
    }
}

RxJava 優勢

  • 同步以及非同步都相同操作

  • 將回傳機制統一Interface

  • 切換執行緒容易

  • 解決 Callback hell

  • 超多 Operator 提供使用

  • 防止快速連點

  • 簡化程式碼

  • ...太多優勢不一一條列了

Conclusion

Item name Better choice
Architecture MVVM
Http Connection Retrofit
Database Room
Image Process Glide
ReactiveX RxJava

Thank you.

Made with Slides.com