经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Android AutoValue使用和扩展库
来源:jb51  时间:2018/10/19 8:55:14  对本文有异议

一、什么是AutoValue

意思就是自动值,谷歌出品,添加@AutoValue这样的注解 就能够自动生成代码,使得程序可能更短,更清晰。 支持Java1.6+

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

首先看一个bean类,User.java:

  1. public class User{
  2.   private String name;
  3.   private String addr;
  4.   private int age;
  5.   private String gender;
  6.   private String hobby;
  7.   private String sign;
  8.   public String getName() {
  9.     return name;
  10.   }
  11.   public void setName(String name) {
  12.     this.name = name;
  13.   }
  14.   ....(太多就省略了)
  15. }

一堆的getter和setter代码很多,到时候添加toStringhashCodeequals这些代码就更麻烦了(虽然ide有快速生成),这时候AutoValue就来拯救世界了。

二、基本使用

一步一脚印

2.1 导包

初次使用需要注意,官方只说了在module依赖,这样会build失败的,对于新手来说会一脸懵逼,因为需要apt。

项目的build.gradle添加依赖:

  1. dependencies {
  2.     //添加这行
  3.     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  4.   }

在module的build.gradle依赖以下,当前最新是1.4.1

  1. //顶部添加
  2. apply plugin: 'com.neenbedankt.android-apt'
  3. dependencies {
  4.  compile "com.google.auto.value:auto-value:1.4.1"
  5.  apt "com.google.auto.value:auto-value:1.4.1"
  6. }

重新Sync即可

2.2 使用AutoValue标识bean

现在来重新编写User类:

  1. @AutoValue
  2. public abstract class User {
  3.   abstract String name();
  4.   abstract String addr();
  5.   abstract int age();
  6.   abstract String gender();
  7.   abstract String hobby();
  8.   abstract String sign();
  9. }

然后build -> make module一下,这时候就会生成AutoValue_User.java ,在build\generated\source\apt\debug\包名\AutoValue_User.java

里面的代码为:

  1.  final class AutoValue_User extends User {
  2.  private final String name;
  3.  private final String addr;
  4.  private final int age;
  5.  private final String gender;
  6.  private final String hobby;
  7.  private final String sign;
  8.  AutoValue_User(
  9.    String name,
  10.    String addr,
  11.    int age,
  12.    String gender,
  13.    String hobby,
  14.    String sign) {
  15.   if (name == null) {
  16.    throw new NullPointerException("Null name");
  17.   }
  18.   this.name = name;
  19.   if (addr == null) {
  20.    throw new NullPointerException("Null addr");
  21.   }
  22.   this.addr = addr;
  23.   this.age = age;
  24.   if (gender == null) {
  25.    throw new NullPointerException("Null gender");
  26.   }
  27.   this.gender = gender;
  28.   if (hobby == null) {
  29.    throw new NullPointerException("Null hobby");
  30.   }
  31.   this.hobby = hobby;
  32.   if (sign == null) {
  33.    throw new NullPointerException("Null sign");
  34.   }
  35.   this.sign = sign;
  36.  }
  37.  @Override
  38.  String name() {
  39.   return name;
  40.  }
  41.  @Override
  42.  String addr() {
  43.   return addr;
  44.  }
  45.  @Override
  46.  int age() {
  47.   return age;
  48.  }
  49.  @Override
  50.  String gender() {
  51.   return gender;
  52.  }
  53.  @Override
  54.  String hobby() {
  55.   return hobby;
  56.  }
  57.  @Override
  58.  String sign() {
  59.   return sign;
  60.  }
  61.  @Override
  62.  public String toString() {
  63.   return "User{"
  64.     + "name=" + name + ", "
  65.     + "addr=" + addr + ", "
  66.     + "age=" + age + ", "
  67.     + "gender=" + gender + ", "
  68.     + "hobby=" + hobby + ", "
  69.     + "sign=" + sign
  70.     + "}";
  71.  }
  72.  @Override
  73.  public boolean equals(Object o) {
  74.   if (== this) {
  75.    return true;
  76.   }
  77.   if (instanceof User) {
  78.    User that = (User) o;
  79.    return (this.name.equals(that.name()))
  80.       && (this.addr.equals(that.addr()))
  81.       && (this.age == that.age())
  82.       && (this.gender.equals(that.gender()))
  83.       && (this.hobby.equals(that.hobby()))
  84.       && (this.sign.equals(that.sign()));
  85.   }
  86.   return false;
  87.  }
  88.  @Override
  89.  public int hashCode() {
  90.   int h = 1;
  91.   h *= 1000003;
  92.   h ^= this.name.hashCode();
  93.   h *= 1000003;
  94.   h ^= this.addr.hashCode();
  95.   h *= 1000003;
  96.   h ^= this.age;
  97.   h *= 1000003;
  98.   h ^= this.gender.hashCode();
  99.   h *= 1000003;
  100.   h ^= this.hobby.hashCode();
  101.   h *= 1000003;
  102.   h ^= this.sign.hashCode();
  103.   return h;
  104.  }
  105. }

这个类就是生成的类,里面就帮你编写好了各种方法hashCodetoStringequalsgettersetter等等。

2.3 构造方法

这时候构造方法利用自己写的一个方法来实现newAutoValue_User,在User类里面添加create方法进行调用生成的AutoValue_User,这时候bean的方法这样的:

  1. @AutoValue
  2. public abstract class User {
  3.   abstract String name();
  4.   abstract String addr();
  5.   abstract int age();
  6.   abstract String gender();
  7.   abstract String hobby();
  8.   abstract String sign();
  9.   //创建User,内部调用的是AutoValue_User
  10.   static User create(String name,String addr,int age,String gender,String hobby,String sign){
  11.     return new AutoValue_User(name,addr,age,gender,hobby,sign);
  12.   }
  13. }

2.4 使用

使用User.create方法即可创建对应User对象:

  1. public class MainActivity extends AppCompatActivity {
  2.   @Override
  3.   protected void onCreate(Bundle savedInstanceState) {
  4.     super.onCreate(savedInstanceState);
  5.     setContentView(R.layout.activity_main);
  6.     User user = User.create("天平","广东",21,"男","敲代码","没有个性签名");
  7.     Log.e("@@", "onCreate: "+user.toString());
  8.   }
  9. }

即可看到输出

  1. 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

  1. apt 'com.ryanharter.auto.value:auto-value-parcel:0.2.5'
  2. // 需要自定义TypeAdapter就要导入
  3. compile 'com.ryanharter.auto.value:auto-value-parcel-adapter:0.2.5'

基本Parcelable

这时候把User实现接口即可:

  1. @AutoValue
  2. public abstract class User implements Parcelable{
  3.   abstract String name();
  4.   abstract String addr();
  5.   abstract int age();
  6.   abstract String gender();
  7.   abstract String hobby();
  8.   abstract String sign();
  9.   static User create(String name,String addr,int age,String gender,String hobby,String sign){
  10.     return new AutoValue_User(name,addr,age,gender,hobby,sign);
  11.   }
  12. }

重新make一下moduel即可看到生成的AutoValue_User继承的原来的$AutoValue_User类,把Parcelable需要实现的方法放在了AutoValue_User类:

  1. final class AutoValue_User extends $AutoValue_User {
  2.  public static final Parcelable.Creator<AutoValue_User> CREATOR = new Parcelable.Creator<AutoValue_User>() {
  3.   @Override
  4.   public AutoValue_User createFromParcel(Parcel in) {
  5.    return new AutoValue_User(
  6.      in.readString(),
  7.      in.readString(),
  8.      in.readInt(),
  9.      in.readString(),
  10.      in.readString(),
  11.      in.readString()
  12.    );
  13.   }
  14.   @Override
  15.   public AutoValue_User[] newArray(int size) {
  16.    return new AutoValue_User[size];
  17.   }
  18.  };
  19.  AutoValue_User(String name, String addr, int age, String gender, String hobby, String sign) {
  20.   super(name, addr, age, gender, hobby, sign);
  21.  }
  22.  @Override
  23.  public void writeToParcel(Parcel dest, int flags) {
  24.   dest.writeString(name());
  25.   dest.writeString(addr());
  26.   dest.writeInt(age());
  27.   dest.writeString(gender());
  28.   dest.writeString(hobby());
  29.   dest.writeString(sign());
  30.  }
  31.  @Override
  32.  public int describeContents() {
  33.   return 0;
  34.  }
  35. }

其他类型Parcelable

Parcel 这个扩展支持Parcel类支持的所有类型,但有时您可能需要parcel其他类型,如SparseArray或ArrayMap。您可以使用自定义TypeAdapter执行此操作(需要导入auto-value-parcel-adapter)

例如User里面有一个类型Date。这时候需要为Date定义一个TypeAdapters:

  1. public class DateTypeAdapter implements TypeAdapter<Date> {
  2.   public Date fromParcel(Parcel in) {
  3.     return new Date(in.readLong());
  4.   }
  5.   public void toParcel(Date value, Parcel dest) {
  6.     dest.writeLong(value.getTime());
  7.   }
  8. }

然后User添加Date类型:

  1. @AutoValue
  2. public abstract class User implements Parcelable{
  3.   abstract String name();
  4.   abstract String addr();
  5.   abstract int age();
  6.   abstract String gender();
  7.   abstract String hobby();
  8.   abstract String sign();
  9.   //需要注解自定义的TypeAdapter
  10.   @ParcelAdapter(DateTypeAdapter.class)
  11.   public abstract Date date();
  12.   static User create(String name,String addr,int age,String gender,String hobby,String sign,Date date){
  13.     return new AutoValue_User(name,addr,age,gender,hobby,sign,date);
  14.   }
  15. }

这里为延迟数据传递,新建一个SecondActivity,在MainActivit传递user过去

MainActivity.java

  1. public class MainActivity extends AppCompatActivity {
  2.   @Override
  3.   protected void onCreate(Bundle savedInstanceState) {
  4.     super.onCreate(savedInstanceState);
  5.     setContentView(R.layout.activity_main);
  6.     User user = User.create("天平","广东",21,"男","敲代码","没有个性签名",new Date());
  7.     startActivity(new Intent(this,SecondActivity.class).putExtra("bean",user));
  8.   }
  9. }

SecondActivity.java

  1. public class SecondActivity extends Activity {
  2.   @Override
  3.   protected void onCreate(@Nullable Bundle savedInstanceState) {
  4.     super.onCreate(savedInstanceState);
  5.     setContentView(R.layout.activity_main);
  6.     User user = getIntent().getParcelableExtra("bean");
  7.     Log.e("@@two", "onCreate: "+user.toString());
  8.   }
  9. }

即可看到输出:

  1. 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的包存在)

  1. apt 'com.ryanharter.auto.value:auto-value-gson:0.4.6'
  2. provided 'com.ryanharter.auto.value:auto-value-gson:0.4.6'
  3. compile 'com.google.code.gson:gson:2.8.0'

3.2.1 在Bean类添加TypeAdapter

Gson解析AutoValue修饰的对象,

这时候User是这样的:

  1. @AutoValue
  2. public abstract class User implements Parcelable{
  3. abstract String name();
  4. abstract String addr();
  5. abstract int age();
  6. abstract String gender();
  7. abstract String hobby();
  8. abstract String sign();
  9. //需要注解自定义的TypeAdapter
  10. @ParcelAdapter(DateTypeAdapter.class)
  11. public abstract Date date();
  12. //添加一个TypeAdapter<User>,这个TypeAdapter是Gson包里面的。
  13. public static TypeAdapter<User> typeAdapter(Gson gson){
  14. // AutoValue_User.GsonTypeAdapter 需要先make一下module之后才会生成
  15. return new AutoValue_User.GsonTypeAdapter(gson)
  16. .setDefaultAddr("默认地址"); //还可以设置默认值
  17. }
  18. }
  • 注意: TypeAdapter,这个TypeAdapter是Gson包里面的。AutoValue_User.GsonTypeAdapter(gson) 需要先make一下module之后才会生成。

3.2.2 编写TypeAdapterFactory

然后编写对应的编写TypeAdapterFactory类,使用@GsonTypeAdapterFactory注解去修饰。

  1. @GsonTypeAdapterFactory
  2. public abstract class UserAdapterFactory implements TypeAdapterFactory {
  3. // 静态工厂方式
  4. public static TypeAdapterFactory create() {
  5. return new AutoValueGson_UserAdapterFactory();
  6. }
  7. }

3.2.3 Gson解析

上面搞好了之后,尝试来解析json为User看看。

  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. //json字符串
  7. String json = "{\"name\":\"天平\",\"addr\":\"广东\",\"age\":21,\"gender\":\"男\",\"hobby\":\"打代码\",\"sign\":\"签名\",\"date\":\"2017-3-13 14:36:19\"}";
  8. //初始化Gson
  9. Gson gson = new GsonBuilder()
  10. .registerTypeAdapterFactory(UserAdapterFactory.create()) //注册自定义的TypeAdapterFactory
  11. .setDateFormat("yyyy-MM-dd HH:mm:ss") //设置json里面的Date格式
  12. .create();
  13. //开始解析
  14. User user = gson.fromJson(json,User.class);
  15. //输出结果
  16. Log.e("@@", "onCreate: "+user.toString());
  17. }
  18. }

即可看到:

  1. 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,例如:

  1. @AutoValue public abstract class Foo<A, B, C> {
  2. abstract A data();
  3. abstract List<B> dataList();
  4. abstract Map<String, List<C>> dataMap();
  5. public static <A, B, C> TypeAdapter<Foo<A, B, C>> typeAdapter(Gson gson,
  6. TypeToken<? extends Foo<A, B, C>> typeToken) {
  7. return new AutoValue_Foo.GsonTypeAdapter(gson, typeToken);
  8. }
  9. }

4.2 可选配置

添加了下面的设置,maps/collections将默认为它们的空类型(例如List - > Collections.emptyList()) 值为true或false。

  1. apt {
  2. arguments {
  3. autovaluegson.defaultCollectionsToEmpty 'true'
  4. }
  5. }

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最新的数据进来:

  1. @AutoValue
  2. public abstract class User {
  3. abstract String name();
  4. abstract String addr();
  5. abstract int age();
  6. abstract String gender();
  7. abstract String hobby();
  8. abstract String sign();
  9. //创建方法
  10. public static User create(String name, String addr, int age, String gender, String hobby, String sign) {
  11. return builder()
  12. .name(name)
  13. .addr(addr)
  14. .age(age)
  15. .gender(gender)
  16. .hobby(hobby)
  17. .sign(sign)
  18. .build();
  19. }
  20. //setter的时候传递当前的user过来,这里重新builder,再设置
  21. public static Builder create(User user){
  22. return builder()
  23. .name(user.name())
  24. .addr(user.addr())
  25. .age(user.age())
  26. .gender(user.gender())
  27. .hobby(user.hobby())
  28. .sign(user.sign());
  29. }
  30. public static Builder builder() {
  31. return new AutoValue_User.Builder();
  32. }
  33. @AutoValue.Builder
  34. public abstract static class Builder {
  35. public abstract Builder name(String name);
  36. public abstract Builder addr(String addr);
  37. public abstract Builder age(int age);
  38. public abstract Builder gender(String gender);
  39. public abstract Builder hobby(String hobby);
  40. public abstract Builder sign(String sign);
  41. public abstract User build();
  42. }
  43. }

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

  1. private void updateSign(User user){
  2.     user = User.create(user).sign("新签名").build();
  3.   }

方法2: 不要用AutoValue了

这种情况适用于你需要频繁的调用setter,如果用第一种方案的话,就需要频繁的new对象,对程序效率有大大的影响。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对w3xue的支持。如果你想了解更多相关内容请查看下面相关链接

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号