Android AutoValue使用和擴展庫

一、什麼是AutoValue

意思就是自動值,谷歌出品,添加@AutoValue這樣的註解 就能夠自動生成代碼,使得程序可能更短,更清晰。 支持Java1.6+

github:
https://github.com/google/auto/blob/master/value/userguide/index.md

首先看一個bean類,User.java:

public class User{
    private String name;
    private String addr;
    private int age;
    private String gender;
    private String hobby;
    private String sign;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    ....(太多就省略了)
}   

一堆的getter和setter代碼很多,到時候添加toStringhashCodeequals這些代碼就更麻煩了(雖然ide有快速生成),這時候AutoValue就來拯救世界了。

二、基本使用

一步一腳印

2.1 導包

初次使用需要注意,官方只說了在module依賴,這樣會build失敗的,對於新手來說會一臉懵逼,因爲需要apt。

項目的build.gradle添加依賴:

dependencies {
        //添加這行
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }

在module的build.gradle依賴以下,當前最新是1.4.1

//頂部添加
apply plugin: 'com.neenbedankt.android-apt'


dependencies {
  compile "com.google.auto.value:auto-value:1.4.1"
  apt  "com.google.auto.value:auto-value:1.4.1"
}

重新Sync即可

2.2 使用AutoValue標識bean

現在來重新編寫User類:

@AutoValue
public abstract class User {

    abstract String name();
    abstract String addr();
    abstract int age();
    abstract String gender();
    abstract String hobby();
    abstract String sign();

}

然後build -> make module一下,這時候就會生成AutoValue_User.java ,在build\generated\source\apt\debug\包名\AutoValue_User.java

裏面的代碼爲:

 final class AutoValue_User extends User {

  private final String name;
  private final String addr;
  private final int age;
  private final String gender;
  private final String hobby;
  private final String sign;

  AutoValue_User(
      String name,
      String addr,
      int age,
      String gender,
      String hobby,
      String sign) {
    if (name == null) {
      throw new NullPointerException("Null name");
    }
    this.name = name;
    if (addr == null) {
      throw new NullPointerException("Null addr");
    }
    this.addr = addr;
    this.age = age;
    if (gender == null) {
      throw new NullPointerException("Null gender");
    }
    this.gender = gender;
    if (hobby == null) {
      throw new NullPointerException("Null hobby");
    }
    this.hobby = hobby;
    if (sign == null) {
      throw new NullPointerException("Null sign");
    }
    this.sign = sign;
  }

  @Override
  String name() {
    return name;
  }

  @Override
  String addr() {
    return addr;
  }

  @Override
  int age() {
    return age;
  }

  @Override
  String gender() {
    return gender;
  }

  @Override
  String hobby() {
    return hobby;
  }

  @Override
  String sign() {
    return sign;
  }

  @Override
  public String toString() {
    return "User{"
        + "name=" + name + ", "
        + "addr=" + addr + ", "
        + "age=" + age + ", "
        + "gender=" + gender + ", "
        + "hobby=" + hobby + ", "
        + "sign=" + sign
        + "}";
  }

  @Override
  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }
    if (o instanceof User) {
      User that = (User) o;
      return (this.name.equals(that.name()))
           && (this.addr.equals(that.addr()))
           && (this.age == that.age())
           && (this.gender.equals(that.gender()))
           && (this.hobby.equals(that.hobby()))
           && (this.sign.equals(that.sign()));
    }
    return false;
  }

  @Override
  public int hashCode() {
    int h = 1;
    h *= 1000003;
    h ^= this.name.hashCode();
    h *= 1000003;
    h ^= this.addr.hashCode();
    h *= 1000003;
    h ^= this.age;
    h *= 1000003;
    h ^= this.gender.hashCode();
    h *= 1000003;
    h ^= this.hobby.hashCode();
    h *= 1000003;
    h ^= this.sign.hashCode();
    return h;
  }

}

這個類就是生成的類,裏面就幫你編寫好了各種方法hashCodetoStringequalsgettersetter等等。

2.3 構造方法

這時候構造方法利用自己寫的一個方法來實現newAutoValue_User,在User類裏面添加create方法進行調用生成的AutoValue_User,這時候bean的方法這樣的:

@AutoValue
public abstract class User {

    abstract String name();
    abstract String addr();
    abstract int age();
    abstract String gender();
    abstract String hobby();
    abstract String sign();

    //創建User,內部調用的是AutoValue_User
    static User create(String name,String addr,int age,String gender,String hobby,String sign){
        return new AutoValue_User(name,addr,age,gender,hobby,sign);
    }

}

2.4 使用

使用User.create方法即可創建對應User對象:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        User user = User.create("天平","廣東",21,"男","敲代碼","沒有個性簽名");
        Log.e("@@", "onCreate: "+user.toString());

    }
}

即可看到輸出

onCreate: User{name=天平, addr=廣東, age=21, gender=男, hobby=敲代碼, sign=沒有個性簽名}

三、擴展api

你以爲AutoValue的功能就那麼少嗎 ? 錯,他還有很多擴展api。

3.1 auto-value-parcel

當User需要實現Parcelable接口的時候,AutoValue也可以幫你搞定了。

在基本的使用基礎上繼續導包(當前最新是0.2.5):

github地址:https://github.com/rharter/auto-value-parcel

apt 'com.ryanharter.auto.value:auto-value-parcel:0.2.5'

// 需要自定義TypeAdapter就要導入
compile 'com.ryanharter.auto.value:auto-value-parcel-adapter:0.2.5'

基本Parcelable

這時候把User實現接口即可:

@AutoValue
public abstract class User implements Parcelable{

    abstract String name();
    abstract String addr();
    abstract int age();
    abstract String gender();
    abstract String hobby();
    abstract String sign();

    static User create(String name,String addr,int age,String gender,String hobby,String sign){
        return new AutoValue_User(name,addr,age,gender,hobby,sign);
    }


}

重新make一下moduel即可看到生成的AutoValue_User繼承的原來的$AutoValue_User類,把Parcelable需要實現的方法放在了AutoValue_User類:

final class AutoValue_User extends $AutoValue_User {
  public static final Parcelable.Creator<AutoValue_User> CREATOR = new Parcelable.Creator<AutoValue_User>() {
    @Override
    public AutoValue_User createFromParcel(Parcel in) {
      return new AutoValue_User(
          in.readString(),
          in.readString(),
          in.readInt(),
          in.readString(),
          in.readString(),
          in.readString()
      );
    }
    @Override
    public AutoValue_User[] newArray(int size) {
      return new AutoValue_User[size];
    }
  };

  AutoValue_User(String name, String addr, int age, String gender, String hobby, String sign) {
    super(name, addr, age, gender, hobby, sign);
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(name());
    dest.writeString(addr());
    dest.writeInt(age());
    dest.writeString(gender());
    dest.writeString(hobby());
    dest.writeString(sign());
  }

  @Override
  public int describeContents() {
    return 0;
  }
}

其他類型Parcelable

Parcel 這個擴展支持Parcel類支持的所有類型,但有時您可能需要parcel其他類型,如SparseArray或ArrayMap。您可以使用自定義TypeAdapter執行此操作(需要導入auto-value-parcel-adapter)

例如User裏面有一個類型Date。這時候需要爲Date定義一個TypeAdapters:

public class DateTypeAdapter implements TypeAdapter<Date> {
    public Date fromParcel(Parcel in) {
        return new Date(in.readLong());
    }

    public void toParcel(Date value, Parcel dest) {
        dest.writeLong(value.getTime());
    }
}

然後User添加Date類型:

@AutoValue
public abstract class User implements Parcelable{

    abstract String name();
    abstract String addr();
    abstract int age();
    abstract String gender();
    abstract String hobby();
    abstract String sign();

    //需要註解自定義的TypeAdapter
    @ParcelAdapter(DateTypeAdapter.class)
    public abstract Date date();

    static User create(String name,String addr,int age,String gender,String hobby,String sign,Date date){
        return new AutoValue_User(name,addr,age,gender,hobby,sign,date);
    }

}

這裏爲延遲數據傳遞,新建一個SecondActivity,在MainActivit傳遞user過去

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        User user = User.create("天平","廣東",21,"男","敲代碼","沒有個性簽名",new Date());

        startActivity(new Intent(this,SecondActivity.class).putExtra("bean",user));

    }
}

SecondActivity.java

public class SecondActivity extends Activity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        User user = getIntent().getParcelableExtra("bean");
        Log.e("@@two", "onCreate: "+user.toString());

    }
}

即可看到輸出:

E/@@: onCreate: User{name=天平, addr=廣東, age=21, gender=男, hobby=敲代碼, sign=沒有個性簽名, date=Mon Mar 13 14:36:19 GMT+08:00 2017}

3.2 auto-value-gson

就是你的用了AutoValues來修飾定義了Bean對象,Gson的就不能按照平常的方式來解析了,需要改變一下。

普及知識:

Gson的TypeAapter可以理解成自定義序列化和返序列化。通過實現JsonSerializer和JsonDeserializer進行序列化和反序列化,在Gson創建的時候registerTypeAdapter(你的自定義TypeAapter)。 具體請百度。

auto-value-gson 的github地址: https://github.com/rharter/auto-value-gson

導包(當前最新是0.4.6,注意,使用需要Gson,就是也要有Gson的包存在)

apt 'com.ryanharter.auto.value:auto-value-gson:0.4.6'
provided 'com.ryanharter.auto.value:auto-value-gson:0.4.6'

compile 'com.google.code.gson:gson:2.8.0'

3.2.1 在Bean類添加TypeAdapter

Gson解析AutoValue修飾的對象,

這時候User是這樣的:

@AutoValue
public abstract class User implements Parcelable{

    abstract String name();
    abstract String addr();
    abstract int age();
    abstract String gender();
    abstract String hobby();
    abstract String sign();

    //需要註解自定義的TypeAdapter
    @ParcelAdapter(DateTypeAdapter.class)
    public abstract Date date();

    //添加一個TypeAdapter<User>,這個TypeAdapter是Gson包裏面的。
    public static TypeAdapter<User> typeAdapter(Gson gson){
        // AutoValue_User.GsonTypeAdapter 需要先make一下module之後纔會生成
        return new AutoValue_User.GsonTypeAdapter(gson)
                .setDefaultAddr("默認地址");  //還可以設置默認值

    }

}

注意: TypeAdapter,這個TypeAdapter是Gson包裏面的。AutoValue_User.GsonTypeAdapter(gson) 需要先make一下module之後纔會生成。

3.2.2 編寫TypeAdapterFactory

然後編寫對應的編寫TypeAdapterFactory類,使用@GsonTypeAdapterFactory註解去修飾。

@GsonTypeAdapterFactory
public abstract class UserAdapterFactory implements TypeAdapterFactory {

    // 靜態工廠方式
    public static TypeAdapterFactory create() {
        return new AutoValueGson_UserAdapterFactory();
    }

}

3.2.3 Gson解析

上面搞好了之後,嘗試來解析json爲User看看。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //json字符串
        String json = "{\"name\":\"天平\",\"addr\":\"廣東\",\"age\":21,\"gender\":\"男\",\"hobby\":\"打代碼\",\"sign\":\"簽名\",\"date\":\"2017-3-13 14:36:19\"}";

        //初始化Gson
        Gson gson = new GsonBuilder()
                .registerTypeAdapterFactory(UserAdapterFactory.create()) //註冊自定義的TypeAdapterFactory
                .setDateFormat("yyyy-MM-dd HH:mm:ss")   //設置json裏面的Date格式
                .create();

        //開始解析
        User user = gson.fromJson(json,User.class);

        //輸出結果
        Log.e("@@", "onCreate: "+user.toString());

    }
}

即可看到:

onCreate: User{name=天平, addr=廣東, age=21, gender=男, hobby=打代碼, sign=簽名, date=Mon Mar 13 14:36:19 GMT+08:00 2017}

四、小細節

4.1 Gson泛型支持

如果你的bean類裏面有泛型,這時候你的TypeAdapter也需要泛型,還要添加參數TypeToken,例如:

@AutoValue public abstract class Foo<A, B, C> {

  abstract A data();
  abstract List<B> dataList();
  abstract Map<String, List<C>> dataMap();

  public static <A, B, C> TypeAdapter<Foo<A, B, C>> typeAdapter(Gson gson,
      TypeToken<? extends Foo<A, B, C>> typeToken) {
    return new AutoValue_Foo.GsonTypeAdapter(gson, typeToken);
  }
}

4.2 可選配置

添加了下面的設置,maps/collections將默認爲它們的空類型(例如List - > Collections.emptyList()) 值爲true或false。

apt {
 arguments {
   autovaluegson.defaultCollectionsToEmpty 'true'
 }
}

4.3 AutoValue plugin插件

可以生成create Builder等代碼,不過不能生成TypeAdapter代碼:

插件倉庫搜索: AutoValue plugin

開源地址: https://github.com/afcastano/AutoValuePlugin

使用方法: 安裝插件重啓了As之後,在Bean裏面Alt+回車 即可ADD

4.4 配合SqlDelight

AutoValue配合SqlDelight效果會更好噢。

五 setter方法變種實現

AutoValue修飾的類是都是immutable不變的,所以就沒有了setter的方法。 我們應該怎麼樣補救呢?

方法1: 重新new

這種情況適用於 不是頻繁的需要setter的話,重新new是個不錯的方法。

例如還是上面的bean,添加了兩個create方法,和Builder。第二個create方法就可以用來重新new,然後setter最新的數據進來:

@AutoValue
public abstract class User {

    abstract String name();
    abstract String addr();
    abstract int age();
    abstract String gender();
    abstract String hobby();
    abstract String sign();


    //創建方法
    public static User create(String name, String addr, int age, String gender, String hobby, String sign) {
        return builder()
                .name(name)
                .addr(addr)
                .age(age)
                .gender(gender)
                .hobby(hobby)
                .sign(sign)
                .build();
    }


    //setter的時候傳遞當前的user過來,這裏重新builder,再設置
    public static Builder create(User user){
        return builder()
                .name(user.name())
                .addr(user.addr())
                .age(user.age())
                .gender(user.gender())
                .hobby(user.hobby())
                .sign(user.sign());
    }



    public static Builder builder() {
        return new AutoValue_User.Builder();
    }


    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder name(String name);

        public abstract Builder addr(String addr);

        public abstract Builder age(int age);

        public abstract Builder gender(String gender);

        public abstract Builder hobby(String hobby);

        public abstract Builder sign(String sign);

        public abstract User build();
    }




}

使用,例如我要更新簽名:

private void updateSign(User user){
        user = User.create(user).sign("新簽名").build();
    }

方法2: 不要用AutoValue了

這種情況適用於你需要頻繁的調用setter,如果用第一種方案的話,就需要頻繁的new對象,對程序效率有大大的影響。

發佈了106 篇原創文章 · 獲贊 380 · 訪問量 75萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章