MVVM与JetPacket结合


背景

MVVM是目前谷歌推荐的Android开发层次框架,如果跟JetPacket相互结合 可以更好的进行相关的开发。

使用到的JetPacket组件

LiveData 、Room 、ViewModel、DataBinding

代码分层分包

比如我们需要开发一个用户信息模块 根据mvvm的设计理念我们可以这个分包

|-- api
|-- room
|-- userInfo
|   |-- model
|   |-- repository
|   |-- view
|   |-- viewmodel
  • api 是结合retrofit的请求库 作为全局的网络请求url封装
  • room 作为全局数据库的一个封装包,提供各种持久化数据库的入口
  • model 作为一个小功能的数据层
  • repository 是当这个功能的获取数据比较复杂的时候可以多加一层
  • view是作为数据的显示层
  • viewmodel 作为viewmodel层主要是进行逻辑处理

代码编写

1.别忘了添加权限

<uses-permission android:name="android.permission.INTERNET" />

2.dataBinding 需要打开

dataBinding {
     enabled = true
 }
 
  1. 添加环境依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'androidx.room:room-runtime:2.2.2'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
implementation 'de.hdodenhof:circleimageview:3.0.1'
annotationProcessor 'androidx.room:room-compiler:2.2.2'
  1. 定义model层
@Entity(tableName = "user")
public class User {

    @PrimaryKey()
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    public int id;

    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
    public String name;

    @ColumnInfo(name = "avatar", typeAffinity = ColumnInfo.TEXT)
    @SerializedName("avatar_url")
    public String avatar;




    @ColumnInfo(name = "followers", typeAffinity = ColumnInfo.INTEGER)
    public int followers;


    @ColumnInfo(name = "following", typeAffinity = ColumnInfo.INTEGER)
    public int following;


    @ColumnInfo(name = "blog", typeAffinity = ColumnInfo.TEXT)
    public String blog;


    @ColumnInfo(name = "company", typeAffinity = ColumnInfo.TEXT)
    public String company;


    @ColumnInfo(name = "bio", typeAffinity = ColumnInfo.TEXT)
    public String bio;


    @ColumnInfo(name = "location", typeAffinity = ColumnInfo.TEXT)
    public String location;


    @ColumnInfo(name = "html_url", typeAffinity = ColumnInfo.TEXT)
    @SerializedName("html_url")
    public String htmlUrl;

    public User(
            int id, String name, String avatar, int followers, int following, String blog, String company, String bio, String location, String htmlUrl) {
        this.id = id;
        this.name = name;
        this.avatar = avatar;
        this.followers = followers;
        this.following = following;
        this.blog = blog;
        this.company = company;
        this.bio = bio;
        this.location = location;
        this.htmlUrl = htmlUrl;

    }

}

  1. 创建数据库

@Database(entities = User.class, version = 1, exportSchema = false)
public abstract class UserDatebase extends RoomDatabase {
    private static final String DATABASE_NAME = "user_db";
    private static UserDatebase databaseInstance;

    public static synchronized UserDatebase getInstance(Context context) {
        if (databaseInstance == null) {
            databaseInstance = Room.databaseBuilder(context.getApplicationContext(), UserDatebase.class, DATABASE_NAME).build();

        }
        return databaseInstance;
    }


    public abstract UserDao getUserDao();


}



@Dao
public interface UserDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertUser(User user);


    @Delete
    void deleteStudent(User user);


    @Query("SELECT * FROM user WHERE name= :name")
    LiveData<User> getUserByName(String name);

}

6.定义网络请求


public interface API {


    @GET("users/{userId}")
    Call<User> getUser(@Path("userId") String userId);


}


public class RetrofitClient {

    private static final String BASE_URL = "http://www.baidu.com";
    private static RetrofitClient retrofitClient;
    private Retrofit retrofit;


    private RetrofitClient() {
        retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
    }


    public static synchronized RetrofitClient getInstance() {

        if (retrofitClient == null) {
            retrofitClient = new RetrofitClient();

        }
        return retrofitClient;
    }


    public API getApi() {
        return retrofit.create(API.class);
    }


}

7.将数据库和网络库的获取方式放在application方便获取



public class MyApplication extends android.app.Application {

    private static UserDatebase userDatebase;
    private static API api;

    @Override
    public void onCreate() {
        super.onCreate();
        userDatebase = UserDatebase.getInstance(this);
        api = RetrofitClient.getInstance().getApi();

    }


    public static API getApi() {

        return api;
    }


    public static UserDatebase getUserDatebase() {
        return userDatebase;
    }
}


8.假设我们要获取的数据非常复杂,我们可以多搞一个repository层(当然这一层也是可以没有的)


public class UserRepository {
    private String TAG = this.getClass().getName();


    private UserDao userDao;

    private API api;


    public UserRepository(UserDao userDao, API api) {
        this.api = api;
        this.userDao = userDao;
    }


    public LiveData<User> getUser(final String name) {

        refresh(name);
        return userDao.getUserByName(name);

    }


    public void refresh(String name) {

        api.getUser(name).enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.body() != null) {
                    insterUer(response.body());
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                Log.i(TAG, "onFailure: ", t);
            }
        });
    }


    private void insterUer(final User user) {
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                userDao.insertUser(user);
            }
        });
    }
}

9.将viewmodel层给实现




public class UserViewModel extends AndroidViewModel {
    private LiveData<User> user;
    private UserRepository userRepository;
    private String userName = "whd";


    public UserViewModel(@NonNull Application application) {
        super(application);

        UserDatebase userDatebase = MyApplication.getUserDatebase();
        UserDao userDao = userDatebase.getUserDao();
        userRepository = new UserRepository(userDao, MyApplication.getApi());
        user = userRepository.getUser(userName);
    }



    public LiveData<User> getUser(){

        return user;
    }

    public void refresh(){
        userRepository.refresh(userName);
    }



}

10.我们再看下view层 包括Activity和xml



class Main2Activity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)

        var binding =
            DataBindingUtil.setContentView<ActivityMain2Binding>(this, R.layout.activity_main2)
        val userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        userViewModel.user.observe(this,
            Observer<User> {
                binding.user = it

            })

        val swipeRefresh = binding.swipeRefresh
        swipeRefresh.setOnRefreshListener {
            userViewModel.refresh()

            swipeRefresh.isRefreshing = false
        }
    }
}

xml包括了一些数据的显示


<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">


    <data>

        <variable
            name="user"
            type="com.example.mvvm_jetpack.userInfo.model.User" />
    </data>

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">


        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitStart"></ImageView>


            <view
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="128dp"
                android:layout_marginTop="128dp"
                android:layout_marginRight="128dp"></view>

            <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
                android:layout_width="match_parent"

                android:layout_height="match_parent"
                android:layout_marginTop="80dp"
                android:gravity="center_horizontal"
                android:orientation="vertical">


                <de.hdodenhof.circleimageview.CircleImageView
                    android:id="@+id/profile_image"
                    android:layout_width="96dp"
                    android:layout_height="96dp"
                    app:civ_border_color="#CCCCCC"
                    app:civ_border_width="2dp"
                    app:image="@{user.avatar}"

                    ></de.hdodenhof.circleimageview.CircleImageView>


                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:text="@{user.name}"
                    android:textSize="22sp"
                    android:textStyle="bold"


                    ></TextView>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:text="@{user.bio}"
                    android:textSize="16sp"></TextView>


                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:text="@{user.company}"
                    android:textSize="16sp"></TextView>


                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:text="@{user.location}"
                    android:textSize="16sp"></TextView>


                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:orientation="horizontal">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@{user.followers}"
                        android:textSize="16sp"></TextView>

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="12dp"
                        android:text="@{user.following}"
                        android:textSize="16dp"


                        ></TextView>


                </LinearLayout>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:text="@{user.htmlUrl}"
                    android:textSize="16dp" />


                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:text="@{user.blog}"
                    android:textSize="16dp" />

            </LinearLayout>


        </RelativeLayout>


    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>


</layout>

我们还将圆形头像的加载给到Picasa来加载


public class ImageViewBindAdapter {

    @BindingAdapter(value = {"image", "defaultImageResource"}, requireAll = false)
    public static void setImage(ImageView imageView, String imageUrl, int imageSource) {


        if (!TextUtils.isEmpty(imageUrl)) {
            Picasso.get().load(imageUrl).placeholder(R.drawable.ic_launcher_background).error(R.drawable.ic_launcher_background).into(imageView);
        }
    }
}

总结

以上就是Mvvm结合Jetpack的使用,希望在平时项目中多运用,写出更高效和优质的代码。


  TOC