聲明,這里是我平時(shí)日常的筆記Zone油啤,所以記錄可能會(huì)偏向于我認(rèn)為的重點(diǎn)區(qū)域,會(huì)有些疏漏或者缺失的地方蟀苛,或是排版或者文案有些凌亂益咬,但我既然發(fā)布出來(lái)了,我會(huì)盡全力去完善帜平,各位看官大家一起努力幽告,愿意聽到各位的批評(píng)指正,共同進(jìn)步……有問(wèn)題可聯(lián)系微信dk2582525775裆甩,期待騷擾……
1冗锁,Room數(shù)據(jù)庫(kù)測(cè)試
Room ORM框架基于注解和APT在編譯時(shí)生成代碼,用戶只需要簡(jiǎn)單配置實(shí)體對(duì)象就能夠正確生成數(shù)據(jù)庫(kù)表嗤栓,所有數(shù)據(jù)庫(kù)操作都只需要用戶提供對(duì)應(yīng)的SQL語(yǔ)句冻河,查詢工作完全由框架生成模板代碼。ROOM框架封裝后的數(shù)據(jù)庫(kù)邏輯完全是面向?qū)ο蟮膶?shí)現(xiàn)方式茉帅,能夠輕松的集成到Android開發(fā)項(xiàng)目中叨叙。
使用步驟
框架引入
? implementation? "android.arch.persistence.room:runtime:$rootProject.roomVersion"
? annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion"
? androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion“
構(gòu)建數(shù)據(jù)庫(kù)對(duì)象
Room.databaseBuilder(sContext, AdsDatabase.class, "ads_database.db")
? ? ? ? .addCallback(new Callback)
? ? ? ? .addMigrations(new Migration(1, 2) )
? ? ? ? .allowMainThreadQueries()
? ? ? ? .build();
配置實(shí)體
@Entity(tableName = "tb_download")
public class DownloadEntity {
? ? @PrimaryKey(autoGenerate = true)
? ? private int id;
? ? private String url;
? ? private long startTime;
? ? private long downloadTime;
? ? private int status;
? ? private int loadType;
? ? private String description;
}
定義Dao對(duì)象
@Dao
public interface DownloadDao {
? ? @Insert
? ? void insert(DownloadEntity entity);
? ? @Query("delete from tb_download")
? ? void deleteAll();
? ? @Query("select * from tb_download where status in (:status)")
? ? List<DownloadEntity> queryByStatus(int[] status);
}
DB類增加注解實(shí)體,添加Dao返回接口
@Database(entities = { DownloadEntity.class, MovieEntity.class }, version = 3)
public abstract class AdsDatabase extends RoomDatabase {
? ? public abstract DownloadDao getDownloadDao();
? ? public abstract MovieDao getMovieDao();
}
完成上面的配置步驟Build一下Project堪澎,會(huì)自動(dòng)生成AdsDataBase_Impl對(duì)象擂错,它繼承自RoomDatabase在內(nèi)部包含了SupportSQLiteOpenHelper對(duì)象,該對(duì)象的實(shí)現(xiàn)類內(nèi)部包含了SQLiteOpenHelper對(duì)象負(fù)責(zé)管理Sqlite數(shù)據(jù)庫(kù)的交互任務(wù)樱蛤,在編譯時(shí)通過(guò)android.arch.persistence.room:compiler庫(kù)中的APT Processor處理马昙, 這些Processor會(huì)查看注解了@DataBase的數(shù)據(jù)庫(kù)類桃犬,注解@Entity的實(shí)體類,注解@Dao的數(shù)據(jù)請(qǐng)求接口行楞,根據(jù)根據(jù)實(shí)體類生成數(shù)據(jù)庫(kù)表,根據(jù)Dao接口中的SQL語(yǔ)句生成數(shù)據(jù)庫(kù)查詢方法土匀,這些都是編譯時(shí)自動(dòng)生成的保證了數(shù)據(jù)庫(kù)存取的高效率子房,開發(fā)者只需要調(diào)用獲取Dao接口就能夠?qū)?shù)據(jù)庫(kù)做CRUD操作。
public void onInsert(View view) {
DownloadDao dao = AdsDatabase.getInstance().getDownloadDao();
? ? DownloadEntity downloadEntity =new DownloadEntity("http://www.baidu.com",
? ? ? ? ? ? 1000000, 2900000, 3, 1, "test");
? ? Log.e(TAG, "Insert " + downloadEntity.toString());
? ? Logger.d(TAG, "Insert " + downloadEntity.toString());
? ? dao.insert(downloadEntity);
}
public void onQuery(View view) {
DownloadDao dao = AdsDatabase.getInstance().getDownloadDao();
? ? List list = dao.queryByStatus(new int[] {3 });
? ? Log.e(TAG, list.toString());
? ? Logger.d(TAG, list.toString());
}
public void onDelete(View view) {
DownloadDao dao = AdsDatabase.getInstance().getDownloadDao();
? ? dao.deleteAll();
? ? Log.e(TAG, "Delete all download entities!");
? ? Logger.d(TAG, "Delete all download entities!");
}
2就轧,生命周期測(cè)試Lifecycle框架測(cè)試
通常設(shè)計(jì)的組件都會(huì)暴漏出對(duì)應(yīng)的生命周期接口調(diào)用证杭,通過(guò)在Activity的生命周期函數(shù)里回調(diào)來(lái)感知外部Activity的運(yùn)行狀態(tài)。這種設(shè)計(jì)會(huì)導(dǎo)致組件多出那些不需要監(jiān)控的接口妒御,而且在Activity的生命周期添加額外的代碼也會(huì)使得二者高度耦合解愤。引入lifecycle組件使用觀察者加狀態(tài)機(jī)實(shí)現(xiàn)Activity的生命周期感知,所有需要感知外部生命周期的組件都面向這個(gè)接口實(shí)現(xiàn)乎莉,不必關(guān)心外部的承載具體是Activity或者Fragment送讲。注意要在api24以后。
使用步驟
組件引入
implementation "android.arch.lifecycle:runtime:$rootProject.lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$rootProject.lifecycle_version"
// use kapt for Kotlin
// alternately - if using Java8, use the following instead of compiler
implementation "android.arch.lifecycle:common-java8:$rootProject.lifecycle_version"
// optional - ReactiveStreams support for LiveData
implementation "android.arch.lifecycle:reactivestreams:$rootProject.lifecycle_version“
接入LifecycleOwner接口
Support26.0.1之后的兼容包里的Activity惋啃、Fragment都已經(jīng)集成了Lifecycle哼鬓,之前的兼容包和Android包下Activity和Fragment的都需要手動(dòng)實(shí)現(xiàn)LifecycleOwer接口。
注冊(cè)LifecycleObserver
getLifecycle().addObserver(new DefaultLifecycleObserver() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onStart(@NonNull LifecycleOwner owner) {
? ? ? ? ? ? ? ? Log.e(TAG, "onStart");
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onStop(@NonNull LifecycleOwner owner) {
? ? ? ? ? ? ? ? Log.e(TAG, "onStop");
? ? ? ? ? ? }
});
Demo演示
這里使用普通的Activity中展示一個(gè)豎向輪播控件边灭,并且提供一個(gè)Dialog樣式的Activity异希,當(dāng)輪播控件處于DialogActivity后方時(shí)就需要暫停輪播,當(dāng)DialogActivity退出返回輪播控件需要重新開始播放绒瘦,可以在Activity的onPause和onResume里做暫停和結(jié)束称簿,不過(guò)現(xiàn)在使用Lifecyle就只需要將控件和Activity的生命周期綁定,在豎向輪播內(nèi)部監(jiān)聽到當(dāng)前Activity進(jìn)入后臺(tái)就暫停惰帽,回到前臺(tái)繼續(xù)豎向輪播憨降。
豎向輪播控件代碼
public class VerticalScrollView extends FrameLayout {
? ? public void bindLifecycle(LifecycleOwner lifecycleOwner) {
? ? ? ? lifecycleOwner.getLifecycle().addObserver(new DefaultLifecycleObserver() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onResume(@NonNull LifecycleOwner owner) {
? ? ? ? ? ? ? ? if (adapter != null) {
? ? ? ? ? ? ? ? ? ? resumePlay();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onPause(@NonNull LifecycleOwner owner) {
? ? ? ? ? ? ? ? if (adapter != null) {
? ? ? ? ? ? ? ? ? ? pausePlay();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onDestroy(@NonNull LifecycleOwner owner) {
? ? ? ? ? ? ? ? destroy();
? ? ? ? ? ? }
? ? ? ? });
? ? }
普通Activity代碼,需要手動(dòng)實(shí)現(xiàn)LifecycleOwner
public class CommonActivityTestActivity extends Activity implements LifecycleOwner {
? ? private static final String TAG = "CommonActivityTestActiv";
? ? private LifecycleRegistry lifecycleRegistry;
? ? private VerticalScrollView verticalScrollView;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? lifecycleRegistry = new LifecycleRegistry(this);
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_common_test);
? ? ? ? verticalScrollView = findViewById(R.id.verticalScrollView);
? ? ? ? verticalScrollView.setAdapter(new VerticalAdapter(this));
? ? ? ? // 將豎向輪播控件綁定到Activity生命周期
? ? ? ? verticalScrollView.bindLifecycle(this);
? ? }
// 在生命周期函數(shù)中向Lifecycle發(fā)送事件
? ? protected void onSaveInstanceState(Bundle outState) {
? ? ? ? super.onSaveInstanceState(outState);
? ? ? ? lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
? ? }
// 在生命周期函數(shù)中向Lifecycle發(fā)送事件
? ? @Override
? ? protected void onStart() {
? ? ? ? super.onStart();
? ? ? ? lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
? ? }
// 在生命周期函數(shù)中向Lifecycle發(fā)送事件
? ? @Override
? ? protected void onResume() {
? ? ? ? super.onResume();
? ? ? ? lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
? ? }
// 在生命周期函數(shù)中向Lifecycle發(fā)送事件
? ? @Override
? ? protected void onPause() {
? ? ? ? lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
? ? ? ? super.onPause();
? ? }
// 在生命周期函數(shù)中向Lifecycle發(fā)送事件
? ? @Override
? ? protected void onStop() {
? ? ? ? lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
? ? ? ? super.onStop();
? ? }
// 在生命周期函數(shù)中向Lifecycle發(fā)送事件
? ? @Override
? ? protected void onDestroy() {
? ? ? ? lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
? ? ? ? super.onDestroy();
? ? }
// 獲取生命周期對(duì)象
? ? @NonNull
? ? @Override
? ? public Lifecycle getLifecycle() {
? ? ? ? return lifecycleRegistry;
? ? }
? ? public void onShowDialog(View view) {
? ? ? ? Intent intent = new Intent(this, DialogActivity.class);
? ? ? ? startActivity(intent);
? ? }
}
Demo里的Activity需要加很多代碼善茎,實(shí)際開發(fā)中可以將這些代發(fā)都放到BaseActivity里券册,項(xiàng)目中所有的Activity都繼承自BaseActivity這樣就不用每個(gè)Activity都加入Lifecycle的邏輯,對(duì)于使用support26+的SupportActivity已經(jīng)集成了LifecycleOwner功能垂涯,不必再添加實(shí)現(xiàn)烁焙。
3,WorkManager組件
Android中四大組件之一的Service組件主要負(fù)責(zé)在后臺(tái)長(zhǎng)時(shí)間運(yùn)行不需要界面的任務(wù)耕赘,不過(guò)Service在后臺(tái)運(yùn)行需要消耗電量導(dǎo)致手機(jī)的續(xù)航能力差骄蝇,谷歌Android引入了睡眠模式,在這種模式下網(wǎng)絡(luò)操骡、GPS等耗電功能都被禁止直到用戶重新點(diǎn)亮屏幕九火。為此Android7.0引入了JobSchedule工具赚窃,所有的后臺(tái)任務(wù)都提交給JobSchedule服務(wù)處理,它會(huì)在某些不確定的時(shí)間喚醒Android系統(tǒng)并執(zhí)行提交給它的任務(wù)岔激,不過(guò)在7.0上JobSchedule在重新啟動(dòng)后無(wú)法繼續(xù)執(zhí)行之前的任務(wù)勒极,到了8.0系統(tǒng)才解決這個(gè)BUG,因而8.0之前版本的異步任務(wù)都需要提交給AlarmService來(lái)實(shí)現(xiàn)虑鼎。WorkManager封裝了這兩種接口并且提供了工作隊(duì)列辱匿,當(dāng)多個(gè)任務(wù)被提交會(huì)執(zhí)行不同的調(diào)度方法,確保所有任務(wù)的順利執(zhí)行炫彩。
/** 適用于即使進(jìn)程退出依然運(yùn)行在后臺(tái)的工作匾七,如果進(jìn)程退出任務(wù)不必存在推薦
使用線程池。在>=23版本使用的是Job Schedule實(shí)現(xiàn)江兢,低于23版本使用AlarmManager
實(shí)現(xiàn)昨忆,WorkManager封裝了二者的差別提供統(tǒng)一的接口,用戶不必?fù)?dān)心版本適配問(wèn)題杉允,
只需要專注于自己的業(yè)務(wù)邑贴。
*/
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
? ? scheduler = new SystemJobScheduler(context, workManager);
? ? setComponentEnabled(context, SystemJobService.class, true);
? ? Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
} else {
? ? scheduler = new SystemAlarmScheduler(context);
? ? enableSystemAlarmService = true;
? ? Logger.get().debug(TAG, "Created SystemAlarmScheduler");
使用步驟
引用組件
implementation "android.arch.work:work-runtime:$rootProject.work_version“
定義任務(wù)
public class DatabaseWorker extends Worker {
? ? @Override
? ? public Result doWork() {
? ? ? ? for (int i = 0; i < 200; i++) {
? ? ? ? ? ? MovieEntity entity = data.get(i % 3);
? ? ? ? ? ? AdsDatabase.getInstance().getMovieDao().save(entity);
? ? ? ? }
? ? ? ? return Result.success();
? ? }
}
提交任務(wù)
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(DatabaseWorker.class).build();
WorkManager.getInstance().enqueue(request);
4,viewmodel組件
在Android中通常會(huì)在Activity或者Fragment里保存View對(duì)應(yīng)的數(shù)據(jù)夺颤,這些數(shù)據(jù)往往需要從網(wǎng)絡(luò)或者磁盤請(qǐng)求得到痢缎,每當(dāng)Activity發(fā)生配置變化或者進(jìn)入后臺(tái)被銷毀就會(huì)重建它們,之前內(nèi)存中保存的數(shù)據(jù)有需要從網(wǎng)絡(luò)或磁盤重新拉取世澜。Android內(nèi)置的onSaveInstanceState/onRestoreInstanceState機(jī)制只能保存較小的數(shù)據(jù)或者能夠支持序列化的數(shù)據(jù)類型独旷,對(duì)于大量的數(shù)據(jù)依然很消耗性能。為此提供了局部的全局變量ViewModel組件寥裂,它能夠跟Activity綁定嵌洼,即使Activity因?yàn)榕渲米兓蛘弑换厥找惨廊槐4嬖趦?nèi)存中,這樣當(dāng)Activity重建時(shí)就能夠直接獲取上次請(qǐng)求的數(shù)據(jù)快速展示出來(lái)封恰。
使用步驟
組件引入
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$rootProject.lifecycle_version"
// alternatively - just ViewModel
implementation "android.arch.lifecycle:viewmodel:$rootProject.lifecycle_version"
創(chuàng)建ViewModel類
public class TestViewModel extends ViewModel {
? ? ? public String name;
}
使用ViewModel對(duì)象
viewModel = ViewModelProviders.of(this).get(TestViewModel.class);
Demo演示
這里我們定義一個(gè)普通的Activity并且讓它的內(nèi)部包含一個(gè)name字段麻养,通過(guò)一個(gè)輸入框和點(diǎn)擊按鈕設(shè)置name屬性,同時(shí)把把輸入的值存儲(chǔ)到ViewModel中诺舔,之后通過(guò)旋轉(zhuǎn)Activity方向會(huì)發(fā)現(xiàn)新的Activity里name值為空而ViewModel中的值依然存在鳖昌。
public class ViewModelRotateActivity extends AppCompatActivity {
? ? private static final String TAG = "ViewModelRotateActivity";
? ? private String name;
? ? private TestViewModel viewModel;
? ? private EditText text;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_view_model_rotate);
? ? ? ? text = findViewById(R.id.text);
? ? ? ? viewModel = ViewModelProviders.of(this).get(TestViewModel.class);
? ? ? ? Log.e(TAG, viewModel.toString());
? ? ? ? Toast.makeText(this, "name = " + name + ", viewModel.name = " + viewModel.name, Toast.LENGTH_LONG).show();
? ? }
? ? public void saveName(View view) {
? ? ? // 分別將輸入值保存再Activity的name里和ViewModel里
? ? ? ? name = text.getText().toString();
? ? ? ? viewModel.name = name;
? ? ? ? if (!TextUtils.isEmpty(name) || !TextUtils.isEmpty(viewModel.name)) {
? ? ? ? ? ? Toast.makeText(this, "name = " + name + ", viewModel.name = " + viewModel.name, Toast.LENGTH_LONG).show();
? ? ? ? }
? ? }
// 屏幕豎向展示
? ? public void rotatePortrait(View view) {
? ? ? ? setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
? ? }
? // 屏幕橫向展示
? ? public void rotateLandscape(View view) {
? ? ? ? setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
? ? }
}
5,liveData 組件
通常后臺(tái)從網(wǎng)絡(luò)請(qǐng)求到數(shù)據(jù)會(huì)在主線程直接設(shè)置到UI元素上低飒,但是當(dāng)Activity并非當(dāng)前與用戶交互的Activity時(shí)在后臺(tái)默默更新的UI會(huì)占用主線程的資源许昨,如果更新量比較大就可能導(dǎo)致交互界面的卡頓。LiveData包裝的數(shù)據(jù)會(huì)監(jiān)聽數(shù)據(jù)變化并且能夠感知當(dāng)前Activity/Fragment的生命周期褥赊,當(dāng)它們處在非可見裝填時(shí)并不會(huì)實(shí)時(shí)更新糕档,當(dāng)它們重新變?yōu)橛脩艚换ソ缑鏁r(shí)才更新界面。這里的監(jiān)聽數(shù)據(jù)變化采用的是觀察者模式拌喉,感知生命周期則通過(guò)前面的lifecycle組件實(shí)現(xiàn)速那。
使用步驟
引入組件
implementation "android.arch.lifecycle:livedata:$rootProject.lifecycle_version“
定義變量
private MutableLiveData<String> name = new MutableLiveData<>();
name.observe(this, new Observer<String>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onChanged(@Nullable String newName) {
? ? ? ? ? ? ? ? text.setText("姓名:" + newName);
? ? ? ? ? ? }
? ? ? ? });
更新變量
name.setValue(newValue);
Demo演示
Activity內(nèi)部有一個(gè)MutableLiveData類型的name俐银,當(dāng)用戶在當(dāng)前Activity修改時(shí)會(huì)立即將數(shù)據(jù)更新到界面上,當(dāng)用戶打開DialogActivity并且使用更新數(shù)據(jù)時(shí)后臺(tái)不會(huì)立即更新到界面上,當(dāng)DialogActivity退出回到當(dāng)前Activity此時(shí)數(shù)據(jù)會(huì)被立即更新到界面上。
public class LiveDataTestActivity extends AppCompatActivity {
? ? private MutableLiveData<String> name = new MutableLiveData<>();
? ? private TextView text;
? ? private int count;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_live_data_test);
? ? ? ? text = findViewById(R.id.text);
? ? ? ? text.setText("姓名: 張三" + count++);
? ? ? ? name.observe(this, new Observer<String>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onChanged(@Nullable String newName) {
? ? ? ? ? ? ? ? text.setText("姓名:" + newName);
? ? ? ? ? ? }
? ? ? ? });
// 接收從DialogActivity發(fā)送過(guò)來(lái)的廣播更新數(shù)據(jù)
? ? ? ? LocalBroadcastManager.getInstance(this).registerReceiver(new BroadcastReceiver() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onReceive(Context context, Intent intent) {
? ? ? ? ? ? ? ? changeName();
? ? ? ? ? ? }
? ? ? ? }, new IntentFilter("com.sohu.change_value"));
? ? }
? // 當(dāng)前界面更新數(shù)據(jù)
? ? public void onChangeName(View view) {
? ? ? ? changeName();
? ? }
? ? private void changeName() {
? ? ? ? String newValue = "張三" + (count++);
? ? ? ? Toast.makeText(getApplicationContext(), "設(shè)置新值:" + newValue, Toast.LENGTH_SHORT).show();
? ? ? ? name.setValue(newValue);
? ? }
? ? public void onShowDialog(View view) {
? ? ? ? Intent intent = new Intent(this, ChangeDialogActivity.class);
? ? ? ? startActivity(intent);
? ? }
}
6,paging組件
Android應(yīng)用中列表是一種很常見的展現(xiàn)形式,多條數(shù)據(jù)展示就會(huì)使用多個(gè)界面元素翎卓,如果大量的數(shù)據(jù)一次性全部加入應(yīng)用中會(huì)導(dǎo)致內(nèi)存極度消耗,而且用戶也很難一次性完全瀏覽一遍,列表數(shù)據(jù)通常都是通過(guò)分頁(yè)加載的形式展現(xiàn)道媚。JetPack內(nèi)部包含了支持RecyclerView分頁(yè)功能的Paging組件,只需要設(shè)置數(shù)據(jù)獲取來(lái)源和每頁(yè)數(shù)量回窘,組件在用戶瀏覽列表是會(huì)自動(dòng)計(jì)算下一頁(yè)要請(qǐng)求的數(shù)據(jù)并發(fā)送請(qǐng)求诺擅。
使用步驟
導(dǎo)入框架
implementation "android.arch.paging:runtime:$rootProject.paging_version“
編寫數(shù)據(jù)庫(kù)
@Query("select * from tb_movie")
DataSource.Factory<Integer, MovieEntity> getAllMovies();
編寫列表界面
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter = new PagedMovieAdapter());
生成LivedPagedList對(duì)象
pagedList = new LivePagedListBuilder<>(repository.getAllMovies(), new PagedList.Config.Builder()
? ? ? ? ? ? ? ? .setPageSize(10).setPrefetchDistance(2).setInitialLoadSizeHint(20).build()).build();
pagedList.observe(this, new Observer<PagedList<MovieEntity>>() {
public void onChanged(@Nullable PagedList<MovieEntity> movieEntities) {
? ? ? ? ? ? ? ? adapter.submitList(movieEntities);
? ? ? }});
Demo演示
前面通過(guò)Room生成MovieEntity對(duì)象的數(shù)據(jù)庫(kù)表并且定義獲取所有電影數(shù)據(jù)的接口,需要注意返回的對(duì)象類型是DataSource.Factory類型啡直,之后就按照使用步驟中的實(shí)現(xiàn)方式將PagedList提交到PagedAdapter中烁涌。
7,Navigation組件
幾乎所有的應(yīng)用內(nèi)部都會(huì)包含導(dǎo)航功能酒觅,通過(guò)導(dǎo)航能夠自由的切換用戶界面撮执。Android中的Fragment和Activity都可以用來(lái)做界面切換,直接使用代碼做切換需要針對(duì)Activity和Fragment使用不同邏輯舷丹,而且導(dǎo)航功能被分散到多個(gè)地方很難直觀了解界面之間的前后關(guān)系抒钱。Navigation組件通過(guò)資源文件定義所有要導(dǎo)航到的界面,所有可以被導(dǎo)航到的界面都可以注冊(cè)颜凯,用戶通過(guò)統(tǒng)一的接口訪問(wèn)注冊(cè)信息并實(shí)現(xiàn)界面跳轉(zhuǎn)谋币。
目前的Android開發(fā)多采用多Activity+Fragment實(shí)現(xiàn),這種實(shí)現(xiàn)方式會(huì)暫用較多的資源症概,而且Activity的創(chuàng)建和銷毀都需要認(rèn)真處理蕾额,否則容易引起內(nèi)存泄漏。針對(duì)這個(gè)問(wèn)題就有開發(fā)者提出使用單Activity+Fragment的模式開發(fā)Android應(yīng)用彼城,Navigation組件對(duì)這種單Activity模式的開發(fā)提供了強(qiáng)力的支持诅蝶。
使用步驟
導(dǎo)入框架
? ? implementation "android.arch.navigation:navigation-fragment:$rootProject.nav_version"
? ? implementation "android.arch.navigation:navigation-ui:$rootProject.nav_version“
編寫界面
編寫Fragment界面
定義導(dǎo)航圖資源
要注意Android Studio需要3.2+版本,低版本的不支持導(dǎo)航編輯功能募壕。如果是3.2版本還需要手動(dòng)啟用Navigation編輯器调炬。
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:id="@+id/nav_graph_test"
? ? app:startDestination="@id/fragmentThree">
<fragment
? ? ? ? android:id="@+id/fragmentThree"
? ? ? ? android:name="com.sohu.jetpacktest.nav.FragmentThree“>
<action android:id="@+id/action_two“ app:destination="@id/fragmentTwo" />
? ? </fragment>
? ? </navigation>
導(dǎo)航跳轉(zhuǎn)
Navigation.findNavController(view).navigate(R.id.action_two);
Demo演示
Demo中主要的是需要在承載的Activity中設(shè)置NavHostFragment,它會(huì)負(fù)責(zé)將導(dǎo)航資源文件里定義的各種目標(biāo)和動(dòng)作解析出來(lái)司抱。
public class NavigationTestActivity extends AppCompatActivity {
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_navigation_test);
? ? }
}
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? tools:context=".NavigationTestActivity">
? ? <fragment
? ? ? ? android:id="@+id/my_nav_host_fragment"
? ? ? ? android:name="androidx.navigation.fragment.NavHostFragment"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="match_parent"
? ? ? ? app:defaultNavHost="true"
? ? ? ? app:navGraph="@navigation/nav_graph_test" />
</FrameLayout>
可以看到導(dǎo)航圖定義在R.navigation.nav_graph_test資源文件中筐眷,該資源文件的定義也很簡(jiǎn)單。app:startDestination代表進(jìn)入導(dǎo)航圖時(shí)默認(rèn)展示的界面也就是fragmentOne习柠,navigation標(biāo)簽里定義了fragment類型和activity類型的目標(biāo)匀谣,在目標(biāo)內(nèi)部定義的動(dòng)作action可以設(shè)置跳轉(zhuǎn)的目標(biāo)照棋,跳轉(zhuǎn)時(shí)需要的參數(shù)等。
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:id="@+id/nav_graph_test"
? ? app:startDestination="@id/fragmentOne">
? ? <action android:id="@+id/action_global_one"
? ? ? ? app:destination="@id/fragmentOne" />
? ? <fragment
? ? ? ? android:id="@+id/fragmentOne"
? ? ? ? android:name="com.sohu.jetpacktest.nav.FragmentOne"
? ? ? ? android:label="fragment_fragment_one"
? ? ? ? tools:layout="@layout/fragment_fragment_one" >
? ? ? ? <action android:id="@+id/action_two"
? ? ? ? ? ? app:destination="@id/fragmentTwo" />
? ? ? ? <action android:id="@+id/action_room"
? ? ? ? ? ? app:destination="@id/roomActivity" />
? ? </fragment>
? ? <fragment
? ? ? ? android:id="@+id/fragmentTwo"
? ? ? ? android:name="com.sohu.jetpacktest.nav.FragmentTwo"
? ? ? ? android:label="fragment_fragment_two"
? ? ? ? tools:layout="@layout/fragment_fragment_two">
? ? </fragment>
? ? // ... fragmentThree fragmentFour
? ? <activity
? ? ? ? android:id="@+id/roomActivity"
? ? ? ? android:name="com.sohu.jetpacktest.RoomActivity"
? ? ? ? android:label="activity_room"
? ? ? ? tools:layout="@layout/activity_room" >
? ? ? ? <argument
? ? ? ? ? ? android:name="name"
? ? ? ? ? ? android:defaultValue="lisi"
? ? ? ? ? ? app:type="string" />
? ? </activity>
</navigation>
// fragmentOne
view.findViewById(R.id.gotoTwo).setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View view) {
? ? ? ? ? ? ? ? Navigation.findNavController(view).navigate(R.id.action_two);
? ? ? ? ? ? }
? ? ? ? });
view.findViewById(R.id.gotoRoom).setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View view) {
? ? ? ? ? ? ? ? Navigation.findNavController(view).navigate(R.id.action_room);
? ? ? ? ? ? }
? ? ? ? });
8武翎,DataBinding組件
使用步驟
導(dǎo)入框架
在需要應(yīng)用DataBinding的Module的gradle文件中添加:
dataBinding{
? ? ? ? enabled = true
? ? }
DataBinding的使用:
在原本的layout布局文件中將最外層的布局標(biāo)簽替換為:< layout >
<layout xmlns:android="http://schemas.android.com/apk/res/android"
? ? ? ? xmlns:tools="http://schemas.android.com/tools">
? ? <data>
? ? ? ? <variable
? ? ? ? ? ? name="item"
? ? ? ? ? ? type="com.wei.rxjavademo.Item"/>
? ? ? ? <variable
? ? ? ? ? ? name="presenter"
? ? ? ? ? ? type="com.wei.rxjavademo.MainActivity.Presenter"/>
? ? </data>
? ? <LinearLayout
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="match_parent"
? ? ? ? android:orientation="vertical"
? ? ? ? android:paddingBottom="@dimen/activity_vertical_margin"
? ? ? ? android:paddingLeft="@dimen/activity_horizontal_margin"
? ? ? ? android:paddingRight="@dimen/activity_horizontal_margin"
? ? ? ? android:paddingTop="@dimen/activity_vertical_margin"
? ? ? ? tools:context="com.wei.rxjavademo.MainActivity">
? ? ? ? <TextView
? ? ? ? ? ? android:layout_width="wrap_content"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:text="@{item.content}"/>
? ? ? ? <Button
? ? ? ? ? ? android:layout_width="match_parent"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:text="訂閱"/>
? ? ? ? <Button
? ? ? ? ? ? android:onClick="@{() -> presenter.onClickListenerBinding(item)}"
? ? ? ? ? ? android:layout_width="match_parent"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:text="取消訂閱"/>
? ? ? ? <EditText
? ? ? ? ? ? android:onTextChanged="@{presenter.onTextChanged}"
? ? ? ? ? ? android:layout_width="match_parent"
? ? ? ? ? ? android:layout_height="50dp"/>
? ? </LinearLayout>
</layout>
同時(shí)在布局文件中加入data標(biāo)簽烈炭,用于指定要綁定的數(shù)據(jù)類:
<data>
<variable
name=""
type=""/>
</data>
//例如
<data>
? ? ? ? <variable
? ? ? ? ? ? name="item"
? ? ? ? ? ? type="com.wei.rxjavademo.Item"/>
? ? ? ? <!--name可以隨意根據(jù)需要命名,type為該類型的全類名-->
? ? ? ? <variable
? ? ? ? ? ? name="presenter"
? ? ? ? ? ? type="com.wei.rxjavademo.MainActivity.Presenter"/>
</data>
修改完布局文件之后就可以在Java代碼中使用DataBinding了宝恶。
mItem = new Item(12, "hello");
? ? ? ? //AS會(huì)為每一個(gè)layout標(biāo)簽自動(dòng)生成一個(gè)Binding類符隙,用于綁定視圖
? ? ? ? activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
? ? ? ? //向布局文件中的variable設(shè)置變量
? ? ? ? activityMainBinding.setItem(mItem);
? ? ? ? activityMainBinding.setPresenter(new Presenter());
Item只是自定義的一個(gè)JavaBean,里邊只有Id和Content兩個(gè)屬性及其get和set方法
以上就是DataBinding的數(shù)據(jù)的綁定垫毙,接下來(lái)還有方法的綁定霹疫。
方法的綁定有兩種情況:
方法引用綁定:主要是綁定一些已經(jīng)原有的方法事件,例如onClick综芥,onTextChanged這些方法
監(jiān)聽器綁定:主要是綁定一些自定義的方法事件丽蝎,可以支持傳入數(shù)據(jù)類型。
方法引用綁定:
自定義一個(gè)Presenter類膀藐,并在其中實(shí)現(xiàn)onClick屠阻,onTextChanged等需要的方法,然后在布局文件中直接聲明额各。
在Java中實(shí)現(xiàn)onClick方法
public class Presenter {
public void onClick(View view){
? ? ? ? ? ? Toast.makeText(MainActivity.this, "onClick點(diǎn)擊", Toast.LENGTH_SHORT).show();
? ? ? ? }
}
在布局文件中直接聲明方法的引用:
<Button
? android:onClick="@{presenter.onClick}"
? android:layout_width="match_parent"
? android:layout_height="wrap_content"
? android:text="訂閱"/>
<!--在onClick方法中直接聲明引用的方法為Presenter中的onClick方法-->
監(jiān)聽器綁定:
也是要先在Presenter中定義需要的方法国觉,并定義好參數(shù):
public class Presenter {
//在這個(gè)方法中可以添加需要的參數(shù)
public void onClickListenerBinding(Item item){
? ? ? ? ? ? Toast.makeText(MainActivity.this, item.getContent(), Toast.LENGTH_SHORT).show();
? ? ? ? }
}
只是在布局文件中引用的時(shí)候有所不同:
<Button
? ? ? ? ? ? android:onClick="@{() -> presenter.onClickListenerBinding(item)}"
? ? ? ? ? ? android:layout_width="match_parent"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:text="取消訂閱"/>
<!--在布局文件中的使用方式為L(zhǎng)ambda表達(dá)式-->
9,WorkManager組件
1. 易于調(diào)度
WorkManager API可以輕松創(chuàng)建可延遲的異步任務(wù),并允許您指定應(yīng)該何時(shí)執(zhí)行虾啦。
你可以創(chuàng)建任務(wù)并將該任務(wù)交給WorkManager麻诀,以便立即或在設(shè)備處于特定條件下運(yùn)行該任務(wù)。
WorkManager提供了保證缸逃,即使您的應(yīng)用程序強(qiáng)制退出或設(shè)備重新啟動(dòng)针饥,你的任務(wù)仍會(huì)在特定條件匹配時(shí)執(zhí)行。
2. 易于取消
WorkManager給每個(gè)任務(wù)分配了UUID需频,使用這個(gè)唯一的ID你就可以隨時(shí)取消任務(wù)丁眼。
3.易于查詢
你可以使用分配給每個(gè)任務(wù)的唯一標(biāo)識(shí)來(lái)詢問(wèn)任務(wù)的狀態(tài),無(wú)論是正在運(yùn)行昭殉,掛起還是已完成苞七。
WorkManager API超越了任務(wù)的當(dāng)前狀態(tài),允許任務(wù)一鍵值對(duì)格式返回?cái)?shù)據(jù)挪丢。
WorkManager使用LiveData來(lái)干會(huì)任務(wù)的數(shù)據(jù)和狀態(tài)蹂风,所以,你的Activity可以觀察這個(gè)LiveData乾蓬,并且每當(dāng)任務(wù)完成時(shí)都會(huì)得到通知惠啄。
4.支持Android所有版本
WorkManager支持Android API 14及以上
WorkManager根據(jù)設(shè)備API級(jí)別和應(yīng)用程序狀態(tài)等因素選擇適當(dāng)?shù)姆绞絹?lái)運(yùn)行你的任務(wù)。
如果應(yīng)用程序正在運(yùn)行,WorkManager將創(chuàng)建新的線程來(lái)運(yùn)行任務(wù)撵渡。
如果應(yīng)用程序沒(méi)有運(yùn)行融柬,那么他將使用JobScheduler API或Firebase Job APIs調(diào)度者或Alarm manager API運(yùn)行調(diào)度任務(wù)。
添加依賴:
implementation "android.arch.work:work-runtime:$work_version"
implementation "android.arch.work:work-firebase:$work_version"
相關(guān)類和概念
Work manager APIs建立在幾個(gè)類上趋距,你必須繼承一些抽象類來(lái)安排任務(wù)粒氧。
Worker:在WorkManager世界中,Worker等同于需要在后臺(tái)執(zhí)行的任務(wù)或作業(yè)节腐。這是一個(gè)抽象類外盯。你需要繼承它。您的Worker類包含有關(guān)如何執(zhí)行該任務(wù)的信息翼雀,但它沒(méi)有關(guān)于何時(shí)運(yùn)行的信息饱苟。
WorkRequest:它代表了工作調(diào)度請(qǐng)求。每個(gè)工作必須在安排工作之前創(chuàng)建工作請(qǐng)求狼渊。 WorkRequest將包含工作的唯一標(biāo)識(shí)掷空,約束條件說(shuō)明應(yīng)在哪種情況下執(zhí)行任務(wù)。這是一個(gè)抽象類囤锉。該庫(kù)提供了這個(gè)類的兩個(gè)直接子類:OneTimeWorkRequest和PeriodicWorkRequest。
WorkManager:它是基于WorkRequest中定義的約束來(lái)管理和調(diào)度任務(wù)的類护锤。
WorkStatus:這個(gè)類包裝了任何work請(qǐng)求的狀態(tài)官地,你可以通過(guò)唯一的id來(lái)查詢?nèi)魏蝫ork的狀態(tài)。
使用流程
1.創(chuàng)建work:
創(chuàng)建一個(gè)Worker的子類烙懦,這個(gè)類有一個(gè)抽象方法doWork()驱入,顧名思義,你需要在后臺(tái)執(zhí)行你想要做的工作氯析,該方法將在后臺(tái)/工作線程中調(diào)用亏较,編寫以執(zhí)行此方法中的任務(wù)。
在返回中掩缓,你必須返回WorkerResult雪情。返回WorkerResult.SUCCESS表明您執(zhí)行的任務(wù)已成功完成。返回WorkerResult.RETRY告訴WorkManager再次重試該工作你辣。返回WorkerResult.FAILURE表示發(fā)生了一個(gè)或多個(gè)錯(cuò)誤巡通。
2.定義約束:
定義約束條件以告訴WorkManager合適安排任務(wù)執(zhí)行,如果沒(méi)有提供任何約束條件舍哄,那么該任務(wù)將立即運(yùn)行宴凉。
以下是僅在設(shè)備充電和限制時(shí)才運(yùn)行任務(wù)的約束。
3.創(chuàng)建work request
你可以創(chuàng)建OneTimeWorkRequest來(lái)運(yùn)行一次任務(wù)表悬。
如果你想定期運(yùn)行一個(gè)任務(wù)弥锄,那么創(chuàng)建一個(gè)PeriodicWorkRequest并設(shè)置工作的時(shí)間間隔。
觀察輸出數(shù)據(jù)
Work manager為所有工作請(qǐng)求管理工作狀態(tài)和LiveData,應(yīng)用程序可以觀察到LiveData在工作狀態(tài)發(fā)生變化時(shí)得到通知籽暇。
你也可以通過(guò)調(diào)用getOutputData()來(lái)讀取輸出數(shù)據(jù)温治。