安卓 LiveData

在本教程中,我们将讨论Android应用程序中的LiveData架构组件。要更好地理解本教程,请快速转到Android ViewModel.

Android LiveData

LiveData是架构模式的一部分。它基本上是一个包含基元/集合类型的数据保持器。它用于观察视图中的更改,并在视图处于活动状态时更新视图。因此,LiveData具有生命周期意识。我们知道,视图模型用于将数据传递给视图。单独使用ViewModel可能是一项乏味且代价高昂的操作,因为每次数据必须更改View时,我们都需要进行多次调用。此外,我们还需要将数据模型存储在不同的位置。LiveData基于观察者Pattern],使得视图模型和视图之间的通信变得容易。它观察数据更改并自动更新数据,而不是我们在从多个位置(例如SQLiteArrayList,视图模型)添加和删除数据引用时进行多次调用。

Android LiveData vs RxJava

Android LiveData类似于RxJava,不同之处在于LiveData是生命周期感知的。如果视图在后台,它不会更新视图中的数据。这有助于我们避免IlLegalStateException等异常。我们在ViewModel中的LiveData是如何更新Activity的? 当我们在Activity中注册观察者时,我们需要重写方法onChanged()。只要LiveData发生更改,onChanged()方法就会触发。因此,在onChanged()中,我们可以将更改后的LiveData更新到View上。

LiveData只是一种数据类型,当数据发生变化时,它会通知它的观察者。LiveData就像一个数据更改通知程序。

LiveData使用setValue()postValue()通知观察者。setValue()在主线程上运行。postValue()在后台线程上运行。在LiveData类型实例上调用getValue()将返回当前数据。

MuableLiveData

MuableLiveData只是一个扩展LiveData类型类的类。MuableLiveData之所以常用,是因为它公开提供了postValue()setValue()方法,这是LiveData类所不提供的。LiveData/MuableLiveData通常用于从集合类型(列表、ArrayList等)更新列表中的数据。在下一节中,我们将创建一个应用程序,该应用程序从SQLite数据库中添加/删除RecillerView中的行。我们将使用MuableLiveData,它会在LiveData发生变化时更新RecclerView记录。我们将使用DiffUtil通过比较旧的和新的ArrayList来更新最小数量的回收视图行。

Android LiveData项目结构示例

Android livedata示例项目structure在您的build.gradle文件中添加以下内容:

1implementation 'com.android.support:design:27.1.1'
2implementation 'com.android.support:cardview-v7:27.1.1'
3implementation 'android.arch.lifecycle:extensions:1.1.1'

Android LiveData代码

Active_main.xml布局的代码如下所示:

 1<?xml version="1.0" encoding="utf-8"?>
 2<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
 3    xmlns:app="https://schemas.android.com/apk/res-auto"
 4    xmlns:tools="https://schemas.android.com/tools"
 5    android:layout_width="match_parent"
 6    android:layout_height="match_parent"
 7    tools:context=".MainActivity">
 8
 9    <android.support.design.widget.AppBarLayout
10        android:layout_width="match_parent"
11        android:layout_height="wrap_content"
12        android:theme="@style/AppTheme.AppBarOverlay">
13
14        <android.support.v7.widget.Toolbar
15            android:id="@+id/toolbar"
16            android:layout_width="match_parent"
17            android:layout_height="?attr/actionBarSize"
18            android:background="?attr/colorPrimary"
19            app:popupTheme="@style/AppTheme.PopupOverlay" />
20
21    </android.support.design.widget.AppBarLayout>
22
23    <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
24        android:layout_width="match_parent"
25        android:layout_height="match_parent"
26        app:layout_behavior="@string/appbar_scrolling_view_behavior">
27
28        <android.support.v7.widget.RecyclerView
29            android:id="@+id/recyclerView"
30            android:layout_width="match_parent"
31            android:layout_height="match_parent" />
32
33    </RelativeLayout>
34
35    <android.support.design.widget.FloatingActionButton
36        android:id="@+id/fab"
37        android:layout_width="wrap_content"
38        android:layout_height="wrap_content"
39        android:layout_gravity="bottom|end"
40        android:layout_margin="@dimen/fab_margin"
41        app:srcCompat="@android:drawable/ic_input_add" />
42
43</android.support.design.widget.CoordinatorLayout>

List_Item_row.xml布局的代码如下:

 1<?xml version="1.0" encoding="utf-8"?>
 2<android.support.v7.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
 3    android:layout_width="match_parent"
 4    android:layout_height="wrap_content">
 5
 6    <RelativeLayout
 7        android:layout_width="match_parent"
 8        android:layout_height="wrap_content"
 9        android:padding="8dp"
10        android:gravity="center_vertical">
11
12        <TextView
13            android:id="@+id/tvUrl"
14            android:layout_width="wrap_content"
15            android:layout_height="wrap_content"
16            android:layout_alignParentStart="true"
17            android:autoLink="web"
18            android:padding="8dp"
19            android:textColor="@android:color/black"
20            android:textSize="20sp" />
21
22        <TextView
23            android:id="@+id/tvDate"
24            android:layout_width="wrap_content"
25            android:layout_height="wrap_content"
26            android:layout_alignParentLeft="true"
27            android:layout_below="@+id/tvUrl" />
28
29        <ImageButton
30            android:id="@+id/btnDelete"
31            android:layout_width="wrap_content"
32            android:layout_height="wrap_content"
33            android:layout_alignParentEnd="true"
34            android:layout_centerVertical="true"
35            android:src="@android:drawable/ic_menu_delete" />
36
37    </RelativeLayout>
38
39</android.support.v7.widget.CardView>

DbSettings.java类的代码如下:

 1package com.journaldev.androidlivedata.db;
 2
 3import android.provider.BaseColumns;
 4
 5public class DbSettings {
 6
 7    public static final String DB_NAME = "favourites.db";
 8    public static final int DB_VERSION = 1;
 9
10    public class DBEntry implements BaseColumns {
11
12        public static final String TABLE = "fav";
13        public static final String COL_FAV_URL = "url";
14        public static final String COL_FAV_DATE = "date";
15
16    }
17}

FavouritesDbHelper.java类的代码如下:

 1package com.journaldev.androidlivedata.db;
 2
 3import android.content.Context;
 4import android.database.sqlite.SQLiteDatabase;
 5import android.database.sqlite.SQLiteOpenHelper;
 6
 7public class FavouritesDBHelper extends SQLiteOpenHelper {
 8
 9    public FavouritesDBHelper(Context context) {
10        super(context, DbSettings.DB_NAME, null, DbSettings.DB_VERSION);
11    }
12
13    @Override
14    public void onCreate(SQLiteDatabase db) {
15        String createTable = "CREATE TABLE " + DbSettings.DBEntry.TABLE + " ( " +
16                DbSettings.DBEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
17                DbSettings.DBEntry.COL_FAV_URL + " TEXT NOT NULL, " +
18                DbSettings.DBEntry.COL_FAV_DATE + " INTEGER NOT NULL);";
19        db.execSQL(createTable);
20    }
21
22    @Override
23    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
24        db.execSQL("DROP TABLE IF EXISTS " + DbSettings.DBEntry.TABLE);
25        onCreate(db);
26    }
27
28}

Favourites.Java模型类的代码如下:

 1package com.journaldev.androidlivedata;
 2
 3public class Favourites {
 4
 5    public long mId;
 6    public String mUrl;
 7    public long mDate;
 8
 9    public Favourites(long id, String name, long date) {
10        mId = id;
11        mUrl = name;
12        mDate = date;
13    }
14
15    public Favourites(Favourites favourites) {
16        mId = favourites.mId;
17        mUrl = favourites.mUrl;
18        mDate = favourites.mDate;
19    }
20
21}

因此,在我们的SQLite数据库中,我们创建了一个包含三条记录的表:ID、URL、DATE。FavouritesViewModel.Java类的代码如下:

  1package com.journaldev.androidlivedata;
  2
  3import android.app.Application;
  4import android.arch.lifecycle.AndroidViewModel;
  5import android.arch.lifecycle.MutableLiveData;
  6import android.content.ContentValues;
  7import android.database.Cursor;
  8import android.database.sqlite.SQLiteDatabase;
  9
 10import com.journaldev.androidlivedata.db.DbSettings;
 11import com.journaldev.androidlivedata.db.FavouritesDBHelper;
 12import java.util.ArrayList;
 13import java.util.List;
 14
 15public class FavouritesViewModel extends AndroidViewModel {
 16
 17    private FavouritesDBHelper mFavHelper;
 18    private MutableLiveData<List<Favourites>> mFavs;
 19
 20    FavouritesViewModel(Application application) {
 21        super(application);
 22        mFavHelper = new FavouritesDBHelper(application);
 23    }
 24
 25    public MutableLiveData<List<Favourites>> getFavs() {
 26        if (mFavs == null) {
 27            mFavs = new MutableLiveData<>();
 28            loadFavs();
 29        }
 30
 31        return mFavs;
 32    }
 33
 34    private void loadFavs() {
 35        List<Favourites> newFavs = new ArrayList<>();
 36        SQLiteDatabase db = mFavHelper.getReadableDatabase();
 37        Cursor cursor = db.query(DbSettings.DBEntry.TABLE,
 38                new String[]{
 39                        DbSettings.DBEntry._ID,
 40                        DbSettings.DBEntry.COL_FAV_URL,
 41                        DbSettings.DBEntry.COL_FAV_DATE
 42                },
 43                null, null, null, null, null);
 44        while (cursor.moveToNext()) {
 45            int idxId = cursor.getColumnIndex(DbSettings.DBEntry._ID);
 46            int idxUrl = cursor.getColumnIndex(DbSettings.DBEntry.COL_FAV_URL);
 47            int idxDate = cursor.getColumnIndex(DbSettings.DBEntry.COL_FAV_DATE);
 48            newFavs.add(new Favourites(cursor.getLong(idxId), cursor.getString(idxUrl), cursor.getLong(idxDate)));
 49        }
 50
 51        cursor.close();
 52        db.close();
 53        mFavs.setValue(newFavs);
 54    }
 55
 56    public void addFav(String url, long date) {
 57
 58        SQLiteDatabase db = mFavHelper.getWritableDatabase();
 59        ContentValues values = new ContentValues();
 60        values.put(DbSettings.DBEntry.COL_FAV_URL, url);
 61        values.put(DbSettings.DBEntry.COL_FAV_DATE, date);
 62        long id = db.insertWithOnConflict(DbSettings.DBEntry.TABLE,
 63                null,
 64                values,
 65                SQLiteDatabase.CONFLICT_REPLACE);
 66        db.close();
 67
 68        List<Favourites> favourites = mFavs.getValue();
 69
 70        ArrayList<Favourites> clonedFavs;
 71        if (favourites == null) {
 72            clonedFavs = new ArrayList<>();
 73        } else {
 74            clonedFavs = new ArrayList<>(favourites.size());
 75            for (int i = 0; i < favourites.size(); i++) {
 76                clonedFavs.add(new Favourites(favourites.get(i)));
 77            }
 78        }
 79
 80        Favourites fav = new Favourites(id, url, date);
 81        clonedFavs.add(fav);
 82        mFavs.setValue(clonedFavs);
 83    }
 84
 85    public void removeFav(long id) {
 86        SQLiteDatabase db = mFavHelper.getWritableDatabase();
 87        db.delete(
 88                DbSettings.DBEntry.TABLE,
 89                DbSettings.DBEntry._ID + " = ?",
 90                new String[]{Long.toString(id)}
 91        );
 92        db.close();
 93
 94        List<Favourites> favs = mFavs.getValue();
 95        ArrayList<Favourites> clonedFavs = new ArrayList<>(favs.size());
 96        for (int i = 0; i < favs.size(); i++) {
 97            clonedFavs.add(new Favourites(favs.get(i)));
 98        }
 99
100        int index = -1;
101        for (int i = 0; i < clonedFavs.size(); i++) {
102            Favourites favourites = clonedFavs.get(i);
103            if (favourites.mId == id) {
104                index = i;
105            }
106        }
107        if (index != -1) {
108            clonedFavs.remove(index);
109        }
110        mFavs.setValue(clonedFavs);
111    }
112
113}

MuableLiveData保存最喜欢的实例对象列表。在addFav()emoveFav()中,我们将数据更改通知给在MainActivity中定义的观察者。我们创建一个ArrayList的副本,以便比较新旧的。MainActivity.Java类的代码如下:

  1package com.journaldev.androidlivedata;
  2
  3import android.arch.lifecycle.Observer;
  4import android.arch.lifecycle.ViewModelProviders;
  5import android.content.DialogInterface;
  6import android.os.Bundle;
  7import android.support.annotation.Nullable;
  8import android.support.design.widget.FloatingActionButton;
  9import android.support.v7.app.AlertDialog;
 10import android.support.v7.app.AppCompatActivity;
 11import android.support.v7.util.DiffUtil;
 12import android.support.v7.widget.LinearLayoutManager;
 13import android.support.v7.widget.RecyclerView;
 14import android.view.LayoutInflater;
 15import android.view.View;
 16import android.view.ViewGroup;
 17import android.widget.EditText;
 18import android.widget.ImageButton;
 19import android.widget.TextView;
 20
 21import java.util.Date;
 22import java.util.List;
 23
 24public class MainActivity extends AppCompatActivity {
 25
 26    private FavAdapter mFavAdapter;
 27    private FavouritesViewModel mFavViewModel;
 28    private List<Favourites> mFav;
 29    FloatingActionButton fab;
 30
 31    @Override
 32    protected void onCreate(Bundle savedInstanceState) {
 33        super.onCreate(savedInstanceState);
 34        setContentView(R.layout.activity_main);
 35        fab = findViewById(R.id.fab);
 36        final RecyclerView recyclerView = findViewById(R.id.recyclerView);
 37        recyclerView.setLayoutManager(new LinearLayoutManager(this));
 38        mFavViewModel = ViewModelProviders.of(this).get(FavouritesViewModel.class);
 39        final Observer<List<Favourites>> favsObserver = new Observer<List<Favourites>>() {
 40            @Override
 41            public void onChanged(@Nullable final List<Favourites> updatedList) {
 42                if (mFav == null) {
 43                    mFav = updatedList;
 44                    mFavAdapter = new FavAdapter();
 45                    recyclerView.setAdapter(mFavAdapter);
 46                } else {
 47                    DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
 48
 49                        @Override
 50                        public int getOldListSize() {
 51                            return mFav.size();
 52                        }
 53
 54                        @Override
 55                        public int getNewListSize() {
 56                            return updatedList.size();
 57                        }
 58
 59                        @Override
 60                        public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
 61                            return mFav.get(oldItemPosition).mId ==
 62                                    updatedList.get(newItemPosition).mId;
 63                        }
 64
 65                        @Override
 66                        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
 67                            Favourites oldFav = mFav.get(oldItemPosition);
 68                            Favourites newFav = updatedList.get(newItemPosition);
 69                            return oldFav.equals(newFav);
 70                        }
 71                    });
 72                    result.dispatchUpdatesTo(mFavAdapter);
 73                    mFav = updatedList;
 74                }
 75            }
 76        };
 77
 78        fab.setOnClickListener(new View.OnClickListener() {
 79            @Override
 80            public void onClick(View view) {
 81                final EditText inUrl = new EditText(MainActivity.this);
 82                AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
 83                        .setTitle("New favourite")
 84                        .setMessage("Add a url link below")
 85                        .setView(inUrl)
 86                        .setPositiveButton("Add", new DialogInterface.OnClickListener() {
 87                            @Override
 88                            public void onClick(DialogInterface dialog, int which) {
 89                                String url = String.valueOf(inUrl.getText());
 90                                long date = (new Date()).getTime();
 91
 92                                mFavViewModel.addFav(url, date);
 93                            }
 94                        })
 95                        .setNegativeButton("Cancel", null)
 96                        .create();
 97                dialog.show();
 98            }
 99        });
100
101        mFavViewModel.getFavs().observe(this, favsObserver);
102    }
103
104    public class FavAdapter extends RecyclerView.Adapter<FavAdapter.FavViewHolder> {
105
106        @Override
107        public FavViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
108            View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_row, parent, false);
109            return new FavViewHolder(itemView);
110        }
111
112        @Override
113        public void onBindViewHolder(FavViewHolder holder, int position) {
114            Favourites favourites = mFav.get(position);
115            holder.mTxtUrl.setText(favourites.mUrl);
116            holder.mTxtDate.setText((new Date(favourites.mDate).toString()));
117        }
118
119        @Override
120        public int getItemCount() {
121            return mFav.size();
122        }
123
124        class FavViewHolder extends RecyclerView.ViewHolder {
125
126            TextView mTxtUrl;
127            TextView mTxtDate;
128
129            FavViewHolder(View itemView) {
130                super(itemView);
131                mTxtUrl = itemView.findViewById(R.id.tvUrl);
132                mTxtDate = itemView.findViewById(R.id.tvDate);
133                ImageButton btnDelete = itemView.findViewById(R.id.btnDelete);
134                btnDelete.setOnClickListener(new View.OnClickListener() {
135                    @Override
136                    public void onClick(View view) {
137                        int pos = getAdapterPosition();
138                        Favourites favourites = mFav.get(pos);
139                        mFavViewModel.removeFav(favourites.mId);
140                    }
141                });
142            }
143        }
144    }
145
146}

在上面的代码中,我们在活动本身中定义了ReclerView Adapter类。mFavViewModel.getFavs().Watch(this,avesWatch);用于在MainActivity中设置一个观察者,每当更新LiveData时,该观察者都会从ViewModel类中得到通知。bensObserver匿名类由onChanged()方法组成,该方法提供最新的数据,然后在RecillerView中进行更新。运行中的上述应用程序的输出如下所示。Android livedata示例app这将结束Android LiveData教程。您可以从下面的链接下载该项目。

下载AndroidLiveData项目。

您也可以从我们的giHub Repository]签出Android Studio项目代码

Published At
Categories with 技术
Tagged with
comments powered by Disqus