一磨总、數(shù)據(jù)綁定庫(kù)
數(shù)據(jù)綁定庫(kù)是一種支持庫(kù)嗦明,可以使用聲明格式將布局中的界面組件綁定在應(yīng)用的數(shù)據(jù)源。
布局通常使用該框架方法的代碼在Activity中定義蚪燕;
例如:在一下代碼調(diào)用findViewByid()來(lái)查找TextView空間并將其綁定到viewModel變量的userNama屬性:
對(duì)TextView賦值
//普通方式
findViewById<TextView>(R.id.sample_text).apply {
text = viewModel.userName
}
//數(shù)據(jù)綁定方式
<TextView
android:text="@{viewmodel.userName}" />
1.1布局和綁定表達(dá)式
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.myapp.data.ViewModel" />
</data>
<ConstraintLayout... /> <!-- UI layout's root element -->
</layout>
1.2使用可觀察的數(shù)據(jù)對(duì)象
數(shù)據(jù)綁定庫(kù)提供了觀察數(shù)據(jù)更改情況的類(lèi)和方法娶牌,不必操心底層數(shù)據(jù)源發(fā)生改變時(shí)刷新界面奔浅。你可以將變量或者屬性設(shè)為可觀察
可觀察字段:ObservableBoolean、ObservableByte诗良、ObservableChar汹桦、ObservableShort、ObservableInt鉴裹、ObservableLong舞骆、ObservableFloat、ObservableDouble径荔、ObservableParcelable
可觀察的集合:ObservableArrayMap督禽、ObservableArrayList
可觀察的對(duì)象:
實(shí)現(xiàn)了Observable
接口的類(lèi)允許注冊(cè)一個(gè)監(jiān)聽(tīng)器,當(dāng)可觀察對(duì)象的屬性更改時(shí)通知這個(gè)監(jiān)聽(tīng)器猖凛。
DataBinding庫(kù)提供了BaseObservable
類(lèi),它實(shí)現(xiàn)了監(jiān)聽(tīng)器注冊(cè)機(jī)制赂蠢。繼承了BaseObservable的數(shù)據(jù)類(lèi)負(fù)責(zé)通知屬性何時(shí)更改绪穆。
private static class User extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return this.firstName;
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
1.3生成的綁定類(lèi)
數(shù)據(jù)綁定卡可以生成用于訪問(wèn)布局變量和View視圖的Binding類(lèi),DataBing庫(kù)會(huì)為每個(gè)布局文件生成一個(gè)binding類(lèi)辨泳,生成一個(gè)binding類(lèi)將布局中的View與變量連接起來(lái);
該類(lèi)的名稱(chēng)基于布局文件的名稱(chēng)玖院,將布局名稱(chēng)裝換為pascal格式并向其添加Binding后綴菠红,例如activity_main.layout
相應(yīng)生成MainActivityBinding
類(lèi)。
有時(shí)候預(yù)先不知道綁定類(lèi)型难菌,在這種情況下试溯,可以使用DataBindingUtil類(lèi)創(chuàng)建綁定;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//方式一
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
//方式二
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
//方式三(最常用)
T createDataBinding = DataBindingUtil.setContentView(this, layoutId)
}
1.4綁定適配器
每個(gè)布局表達(dá)式都會(huì)與對(duì)應(yīng)的適配器綁定,要求必須進(jìn)行框架調(diào)用來(lái)設(shè)置對(duì)應(yīng)的屬性監(jiān)聽(tīng)器郊酒;例如setText()設(shè)置文本屬性遇绞,或者setOnClickListener()設(shè)置點(diǎn)擊事件監(jiān)聽(tīng)屬性。
自定義適配器燎窘,例如
舉例一:設(shè)置View隱藏顯示
@BindingAdapter("app:goneUnless")
public static void goneUnless(View view, Boolean visible) {
view.visibility = visible ? View.VISIBLE : View.GONE;
}
舉例二:設(shè)置ImageView加載圖片
//代碼中
@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
Picasso.with(view.getContext()).load(url).error(error).into(view);
}
//布局中調(diào)用
<ImageView
app:imageUrl="@{venue.imageUrl}"
app:error="@{@drawable/venueError}" />
1.5將布局視圖綁定到架構(gòu)組件
AndoridX庫(kù)中包含了架構(gòu)組件摹闽,DataBinding可以與架構(gòu)組件無(wú)縫協(xié)作,進(jìn)一步簡(jiǎn)化UI的開(kāi)發(fā)褐健;
1.5.1使用LiveData通知UI有關(guān)數(shù)據(jù)更新
與上面提到的Observable觀察者對(duì)象不同付鹿,LiveData對(duì)象知道訂閱數(shù)據(jù)更改的觀察者的生命周期蚜迅。
在使用LiveData對(duì)象前舵匾,需要指定生命周期所有者來(lái)定義LiveData對(duì)象的范圍,下面以Activity作為生命周期所有者為例:
class ViewModelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Inflate view and obtain an instance of the binding class.
UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);
// Specify the current activity as the lifecycle owner.
binding.setLifecycleOwner(this);
}
}
我們可以使用ViewModel組件將數(shù)據(jù)綁定到布局谁不,在ViewModel組件中坐梯,我們可以使用LiveData對(duì)象來(lái)轉(zhuǎn)換數(shù)據(jù)或合并多個(gè)數(shù)據(jù)源;
class ScheduleViewModel extends ViewModel {
LiveData username;
public ScheduleViewModel() {
String result = Repository.userName;
userName = Transformations.map(result, result -> result.value);
}
1.5.2使用ViewModel管理與UI相關(guān)數(shù)據(jù)
使用ViewModel庫(kù)可以將UI邏輯移出布局并放入易于測(cè)試的組件中刹帕,確保View在需要從數(shù)據(jù)源綁定和解綁吵血;
class ViewModelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Obtain the ViewModel component.
UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
// Inflate view and obtain an instance of the binding class.
UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);
// Assign the component to a property in the binding class.
binding.viewmodel = userModel;
}
}
二馏段、Lifecycle+LiveData+ViewModel庫(kù)
Lifecycle:生命周期感知組件可執(zhí)行操作來(lái)響應(yīng)另一個(gè)組件(Activity或Fragment)的生命周期狀態(tài)的變化。
LiveData:在底層數(shù)據(jù)庫(kù)更改時(shí)通知視圖践瓷;
ViewModel:以注重生命周期的方式管理界面相關(guān)數(shù)據(jù)院喜;
Lifecycle
是一個(gè)類(lèi),用于存儲(chǔ)有個(gè)組件(Activity晕翠、Fragment)的生命周期狀態(tài)信息喷舀,并允許其他對(duì)象觀察此狀態(tài);
2.1通過(guò)注解監(jiān)控組件生命周期
你可以桐楠格Lifeceycle類(lèi)的addObserver()方法并專(zhuān)遞觀察者的實(shí)例來(lái)添加觀察者淋肾;
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
Lifecycles+LiveData+ViewModel 在項(xiàng)目中的組合使用代碼案例 https://codelabs.developers.google.com/codelabs/android-lifecycles/index.html?index=..%2F..%2Findex#0
三硫麻、Navigation導(dǎo)航
支持用戶(hù)導(dǎo)航、進(jìn)入樊卓、退出應(yīng)用中不同內(nèi)容片斷的交互拿愧,導(dǎo)航組件主要由三部分組成:
- 導(dǎo)航圖:在一個(gè)集中位置包含所有導(dǎo)航相關(guān)信息XML資源,應(yīng)用內(nèi)單個(gè)內(nèi)容區(qū)域以及用戶(hù)可以通過(guò)應(yīng)用獲取的可能路勁碌尔;
- NavHost:顯示導(dǎo)航圖中目標(biāo)的空白容器浇辜,導(dǎo)航組件包含一個(gè)默認(rèn)的NavHost實(shí)現(xiàn)(NavHostFragment),可顯示Fragment目標(biāo)唾戚;
- NavController:在NavHost中管理應(yīng)用導(dǎo)航的對(duì)象柳洋,當(dāng)用戶(hù)在整個(gè)應(yīng)用中移動(dòng)時(shí),NavController會(huì)安排NavHost中的目標(biāo)內(nèi)容交換叹坦;
優(yōu)勢(shì):
- 處理Fragment事務(wù)熊镣;
- ViewModel支持,在范圍內(nèi)先訂導(dǎo)航圖募书,在圖表目標(biāo)之間共享與界面相關(guān)的數(shù)據(jù)绪囱;
- 實(shí)現(xiàn)處理深層鏈接;
- 默認(rèn)情況下莹捡,正確處理往返操作鬼吵;
官方代碼實(shí)現(xiàn)流程:https://codelabs.developers.google.com/codelabs/android-navigation/index.html?index=..%2F..%2Findex#0
四、Paging分頁(yè)組件
分頁(yè)庫(kù)支持當(dāng)加載大數(shù)據(jù)時(shí)道盏,一次加載或顯示一小塊數(shù)據(jù)而柑,按需載入部分?jǐn)?shù)據(jù)減少網(wǎng)絡(luò)帶寬和系統(tǒng)資源使用量;減少內(nèi)存資源的消耗
PagedList類(lèi)
荷逞,用于加載應(yīng)用數(shù)據(jù)塊或頁(yè)面媒咳,隨著所需數(shù)據(jù)的增加,系統(tǒng)會(huì)將分頁(yè)到現(xiàn)有的PagedList對(duì)象中种远,如果任何已加載的數(shù)據(jù)發(fā)生改變涩澡,會(huì)沖LiveData或RXJava2
的對(duì)象向可觀察數(shù)據(jù)存儲(chǔ)發(fā)出一個(gè)新的PagedList實(shí)例。隨著PagedList對(duì)象的生成坠敷,應(yīng)用界面會(huì)呈現(xiàn)其內(nèi)容妙同,同時(shí)還會(huì)考慮界面控件的生命周期射富;
下面使用PagedList與LiveData存儲(chǔ)加載和顯示數(shù)據(jù):
public class ConcertViewModel extends ViewModel {
private ConcertDao concertDao;
public final LiveData<PagedList<Concert>> concertList;
// Creates a PagedList object with 50 items per page.
public ConcertViewModel(ConcertDao concertDao) {
this.concertDao = concertDao;
concertList = new LivePagedListBuilder<>(
concertDao.concertsByDate(), 50).build();
}
}
每個(gè)PageList實(shí)例對(duì)象都對(duì)應(yīng)DataSource對(duì)象加載應(yīng)用數(shù)據(jù)的最新快照。數(shù)據(jù)從后端或者數(shù)據(jù)庫(kù)流向PagedList對(duì)象粥帚;
使用 LiveData 觀察分頁(yè)數(shù)據(jù)+Room持久性庫(kù):
@Dao
public interface ConcertDao {
// The Integer type parameter tells Room to use a PositionalDataSource
// object, with position-based loading under the hood.
@Query("SELECT * FROM concerts ORDER BY date DESC")
DataSource.Factory<Integer, Concert> concertsByDate();
}
public class ConcertViewModel extends ViewModel {
private ConcertDao concertDao;
public final LiveData<PagedList<Concert>> concertList;
public ConcertViewModel(ConcertDao concertDao) {
this.concertDao = concertDao;
concertList = new LivePagedListBuilder<>(
concertDao.concertsByDate(), /* page size */ 50).build();
}
}
public class ConcertActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConcertViewModel viewModel =
new ViewModelProvider(this).get(ConcertViewModel.class);
RecyclerView recyclerView = findViewById(R.id.concert_list);
ConcertAdapter adapter = new ConcertAdapter();
viewModel.concertList.observe(this, adapter::submitList);
recyclerView.setAdapter(adapter);
}
}
public class ConcertAdapter
extends PagedListAdapter<Concert, ConcertViewHolder> {
protected ConcertAdapter() {
super(DIFF_CALLBACK);
}
@Override
public void onBindViewHolder(@NonNull ConcertViewHolder holder,
int position) {
Concert concert = getItem(position);
if (concert != null) {
holder.bindTo(concert);
} else {
// Null defines a placeholder item - PagedListAdapter automatically
// invalidates this row when the actual object is loaded from the
// database.
holder.clear();
}
}
private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK =
new DiffUtil.ItemCallback<Concert>() {
// Concert details may have changed if reloaded from the database,
// but ID is fixed.
@Override
public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) {
return oldConcert.getId() == newConcert.getId();
}
@Override
public boolean areContentsTheSame(Concert oldConcert,
Concert newConcert) {
return oldConcert.equals(newConcert);
}
};
}
官方代碼流程:https://codelabs.developers.google.com/codelabs/android-paging/index.html?index=..%2F..%2Findex#0
五胰耗、Room持久性庫(kù)
基于SQLite基礎(chǔ)上提供的一個(gè)抽象層,讓用戶(hù)利用SQLiter強(qiáng)大功能同事獲取更強(qiáng)健的數(shù)據(jù)庫(kù)訪問(wèn)機(jī)制芒涡。Room包含三個(gè)主要組件:
- 數(shù)據(jù)庫(kù):數(shù)據(jù)庫(kù)持有者柴灯,用于保留持久關(guān)系型數(shù)據(jù)的底層連接;
- Entity:數(shù)據(jù)庫(kù)中的表费尽;
-
DAO:訪問(wèn)數(shù)據(jù)庫(kù)的方法赠群;
工作流程:使用Room數(shù)據(jù)庫(kù)來(lái)獲取與該數(shù)據(jù)庫(kù)關(guān)聯(lián)的數(shù)據(jù)訪問(wèn)對(duì)象DAO,然后,應(yīng)用使用每個(gè)DAO對(duì)象從數(shù)據(jù)庫(kù)中獲取實(shí)體旱幼,然后再將對(duì)這些實(shí)體的所有更改保存回?cái)?shù)據(jù)庫(kù)中查描,最后,應(yīng)用使用實(shí)體獲取和設(shè)置與數(shù)據(jù)庫(kù)中的表列相對(duì)應(yīng)的值柏卤;
下面是代碼舉例:
//實(shí)體對(duì)象User
@Entity
public class User {
@PrimaryKey
public int uid;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
}
//對(duì)應(yīng)的DAO數(shù)據(jù)庫(kù)-----UesrDao
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
//AppDatabase
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
//使用一下代碼獲取已創(chuàng)建的數(shù)據(jù)庫(kù)的實(shí)例:
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
官方提供代碼:https://github.com/android/architecture-components-samples
六:WorkManager任務(wù)調(diào)度
用處:可以清楚調(diào)度冬三,即時(shí)在應(yīng)用退出或設(shè)備重啟時(shí),仍應(yīng)運(yùn)行的可延遲異步任務(wù)闷旧。
應(yīng)用場(chǎng)景:
- 向后端服務(wù)器上傳日志和分析數(shù)據(jù)(應(yīng)用崩潰時(shí))长豁;
- 定期將應(yīng)用數(shù)據(jù)與服務(wù)器同步
不適用場(chǎng)景:
- 應(yīng)用進(jìn)程結(jié)束時(shí)能夠安全終止的運(yùn)行中后臺(tái)工作;
- 需要立即執(zhí)行的任務(wù)忙灼;
代碼應(yīng)用:
6.1創(chuàng)建后臺(tái)任務(wù)
任務(wù)使用Worker類(lèi)定義,doWork()方法在WorkManager提供的后臺(tái)線(xiàn)程上同步運(yùn)行钝侠;
doWork()返回的Result會(huì)通知WorkManager任務(wù)该园;
- 已完成:Result.success();
- 已失敗:Result.failure();
- 需要稍后重試:Result.retry();
//上次圖像的Worker
public class UploadWorker extends Worker {
public UploadWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
// Do the work here--in this case, upload the images.
uploadImages()
// Indicate whether the task finished successfully with the Result
return Result.success()
}
}
6.2可以配置運(yùn)行任務(wù)的方式和時(shí)間
Work定義是工作單元帅韧,WorkRequest定義工作運(yùn)行方式和時(shí)間里初。任務(wù)可以是一次性或者周期性的;
一次性使用OneTimeWorkRequest
忽舟,周期性使用:PeriodicWorkRequest
示例:
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
.build()
``
#####6.3提交任務(wù)給系統(tǒng)
WorkManager.getInstance(myContext).enqueue(uploadWorkRequest);