声明,这里是我平时日常的笔记Zone,所以记录可能会偏向于我认为的重点区域,会有些疏漏或者缺失的地方,或是排版或者文案有些凌乱,但我既然发布出来了,我会尽全力去完善,各位看官大家一起努力,愿意听到各位的批评指正,共同进步……有问题可联系微信dk2582525775,期待骚扰……
1,Room数据库测试
Room ORM框架基于注解和APT在编译时生成代码,用户只需要简单配置实体对象就能够正确生成数据库表,所有数据库操作都只需要用户提供对应的SQL语句,查询工作完全由框架生成模板代码。ROOM框架封装后的数据库逻辑完全是面向对象的实现方式,能够轻松的集成到Android开发项目中。
使用步骤
框架引入
implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion"
annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion"
androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion“
构建数据库对象
Room.databaseBuilder(sContext, AdsDatabase.class, "ads_database.db")
.addCallback(new Callback)
.addMigrations(new Migration(1, 2) )
.allowMainThreadQueries()
.build();
配置实体
@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对象
@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类增加注解实体,添加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,会自动生成AdsDataBase_Impl对象,它继承自RoomDatabase在内部包含了SupportSQLiteOpenHelper对象,该对象的实现类内部包含了SQLiteOpenHelper对象负责管理Sqlite数据库的交互任务,在编译时通过android.arch.persistence.room:compiler库中的APT Processor处理, 这些Processor会查看注解了@DataBase的数据库类,注解@Entity的实体类,注解@Dao的数据请求接口,根据根据实体类生成数据库表,根据Dao接口中的SQL语句生成数据库查询方法,这些都是编译时自动生成的保证了数据库存取的高效率,开发者只需要调用获取Dao接口就能够对数据库做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,生命周期测试Lifecycle框架测试
通常设计的组件都会暴漏出对应的生命周期接口调用,通过在Activity的生命周期函数里回调来感知外部Activity的运行状态。这种设计会导致组件多出那些不需要监控的接口,而且在Activity的生命周期添加额外的代码也会使得二者高度耦合。引入lifecycle组件使用观察者加状态机实现Activity的生命周期感知,所有需要感知外部生命周期的组件都面向这个接口实现,不必关心外部的承载具体是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都已经集成了Lifecycle,之前的兼容包和Android包下Activity和Fragment的都需要手动实现LifecycleOwer接口。
注册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中展示一个竖向轮播控件,并且提供一个Dialog样式的Activity,当轮播控件处于DialogActivity后方时就需要暂停轮播,当DialogActivity退出返回轮播控件需要重新开始播放,可以在Activity的onPause和onResume里做暂停和结束,不过现在使用Lifecyle就只需要将控件和Activity的生命周期绑定,在竖向轮播内部监听到当前Activity进入后台就暂停,回到前台继续竖向轮播。
竖向轮播控件代码
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代码,需要手动实现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);
}
// 在生命周期函数中向Lifecycle发送事件
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
// 在生命周期函数中向Lifecycle发送事件
@Override
protected void onStart() {
super.onStart();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
// 在生命周期函数中向Lifecycle发送事件
@Override
protected void onResume() {
super.onResume();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
}
// 在生命周期函数中向Lifecycle发送事件
@Override
protected void onPause() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
super.onPause();
}
// 在生命周期函数中向Lifecycle发送事件
@Override
protected void onStop() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
super.onStop();
}
// 在生命周期函数中向Lifecycle发送事件
@Override
protected void onDestroy() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
super.onDestroy();
}
// 获取生命周期对象
@NonNull
@Override
public Lifecycle getLifecycle() {
return lifecycleRegistry;
}
public void onShowDialog(View view) {
Intent intent = new Intent(this, DialogActivity.class);
startActivity(intent);
}
}
Demo里的Activity需要加很多代码,实际开发中可以将这些代发都放到BaseActivity里,项目中所有的Activity都继承自BaseActivity这样就不用每个Activity都加入Lifecycle的逻辑,对于使用support26+的SupportActivity已经集成了LifecycleOwner功能,不必再添加实现。
3,WorkManager组件
Android中四大组件之一的Service组件主要负责在后台长时间运行不需要界面的任务,不过Service在后台运行需要消耗电量导致手机的续航能力差,谷歌Android引入了睡眠模式,在这种模式下网络、GPS等耗电功能都被禁止直到用户重新点亮屏幕。为此Android7.0引入了JobSchedule工具,所有的后台任务都提交给JobSchedule服务处理,它会在某些不确定的时间唤醒Android系统并执行提交给它的任务,不过在7.0上JobSchedule在重新启动后无法继续执行之前的任务,到了8.0系统才解决这个BUG,因而8.0之前版本的异步任务都需要提交给AlarmService来实现。WorkManager封装了这两种接口并且提供了工作队列,当多个任务被提交会执行不同的调度方法,确保所有任务的顺利执行。
/** 适用于即使进程退出依然运行在后台的工作,如果进程退出任务不必存在推荐
使用线程池。在>=23版本使用的是Job Schedule实现,低于23版本使用AlarmManager
实现,WorkManager封装了二者的差别提供统一的接口,用户不必担心版本适配问题,
只需要专注于自己的业务。
*/
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“
定义任务
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();
}
}
提交任务
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(DatabaseWorker.class).build();
WorkManager.getInstance().enqueue(request);
4,viewmodel组件
在Android中通常会在Activity或者Fragment里保存View对应的数据,这些数据往往需要从网络或者磁盘请求得到,每当Activity发生配置变化或者进入后台被销毁就会重建它们,之前内存中保存的数据有需要从网络或磁盘重新拉取。Android内置的onSaveInstanceState/onRestoreInstanceState机制只能保存较小的数据或者能够支持序列化的数据类型,对于大量的数据依然很消耗性能。为此提供了局部的全局变量ViewModel组件,它能够跟Activity绑定,即使Activity因为配置变化或者被回收也依然保存在内存中,这样当Activity重建时就能够直接获取上次请求的数据快速展示出来。
使用步骤
组件引入
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$rootProject.lifecycle_version"
// alternatively - just ViewModel
implementation "android.arch.lifecycle:viewmodel:$rootProject.lifecycle_version"
创建ViewModel类
public class TestViewModel extends ViewModel {
public String name;
}
使用ViewModel对象
viewModel = ViewModelProviders.of(this).get(TestViewModel.class);
Demo演示
这里我们定义一个普通的Activity并且让它的内部包含一个name字段,通过一个输入框和点击按钮设置name属性,同时把把输入的值存储到ViewModel中,之后通过旋转Activity方向会发现新的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 组件
通常后台从网络请求到数据会在主线程直接设置到UI元素上,但是当Activity并非当前与用户交互的Activity时在后台默默更新的UI会占用主线程的资源,如果更新量比较大就可能导致交互界面的卡顿。LiveData包装的数据会监听数据变化并且能够感知当前Activity/Fragment的生命周期,当它们处在非可见装填时并不会实时更新,当它们重新变为用户交互界面时才更新界面。这里的监听数据变化采用的是观察者模式,感知生命周期则通过前面的lifecycle组件实现。
使用步骤
引入组件
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内部有一个MutableLiveData类型的name,当用户在当前Activity修改时会立即将数据更新到界面上,当用户打开DialogActivity并且使用更新数据时后台不会立即更新到界面上,当DialogActivity退出回到当前Activity此时数据会被立即更新到界面上。
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发送过来的广播更新数据
LocalBroadcastManager.getInstance(this).registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
changeName();
}
}, new IntentFilter("com.sohu.change_value"));
}
// 当前界面更新数据
public void onChangeName(View view) {
changeName();
}
private void changeName() {
String newValue = "张三" + (count++);
Toast.makeText(getApplicationContext(), "设置新值:" + 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应用中列表是一种很常见的展现形式,多条数据展示就会使用多个界面元素,如果大量的数据一次性全部加入应用中会导致内存极度消耗,而且用户也很难一次性完全浏览一遍,列表数据通常都是通过分页加载的形式展现。JetPack内部包含了支持RecyclerView分页功能的Paging组件,只需要设置数据获取来源和每页数量,组件在用户浏览列表是会自动计算下一页要请求的数据并发送请求。
使用步骤
导入框架
implementation "android.arch.paging:runtime:$rootProject.paging_version“
编写数据库
@Query("select * from tb_movie")
DataSource.Factory<Integer, MovieEntity> getAllMovies();
编写列表界面
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter = new PagedMovieAdapter());
生成LivedPagedList对象
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演示
前面通过Room生成MovieEntity对象的数据库表并且定义获取所有电影数据的接口,需要注意返回的对象类型是DataSource.Factory类型,之后就按照使用步骤中的实现方式将PagedList提交到PagedAdapter中。
7,Navigation组件
几乎所有的应用内部都会包含导航功能,通过导航能够自由的切换用户界面。Android中的Fragment和Activity都可以用来做界面切换,直接使用代码做切换需要针对Activity和Fragment使用不同逻辑,而且导航功能被分散到多个地方很难直观了解界面之间的前后关系。Navigation组件通过资源文件定义所有要导航到的界面,所有可以被导航到的界面都可以注册,用户通过统一的接口访问注册信息并实现界面跳转。
目前的Android开发多采用多Activity+Fragment实现,这种实现方式会暂用较多的资源,而且Activity的创建和销毁都需要认真处理,否则容易引起内存泄漏。针对这个问题就有开发者提出使用单Activity+Fragment的模式开发Android应用,Navigation组件对这种单Activity模式的开发提供了强力的支持。
使用步骤
导入框架
implementation "android.arch.navigation:navigation-fragment:$rootProject.nav_version"
implementation "android.arch.navigation:navigation-ui:$rootProject.nav_version“
编写界面
编写Fragment界面
定义导航图资源
要注意Android Studio需要3.2+版本,低版本的不支持导航编辑功能。如果是3.2版本还需要手动启用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>
导航跳转
Navigation.findNavController(view).navigate(R.id.action_two);
Demo演示
Demo中主要的是需要在承载的Activity中设置NavHostFragment,它会负责将导航资源文件里定义的各种目标和动作解析出来。
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>
可以看到导航图定义在R.navigation.nav_graph_test资源文件中,该资源文件的定义也很简单。app:startDestination代表进入导航图时默认展示的界面也就是fragmentOne,navigation标签里定义了fragment类型和activity类型的目标,在目标内部定义的动作action可以设置跳转的目标,跳转时需要的参数等。
<?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组件
使用步骤
导入框架
在需要应用DataBinding的Module的gradle文件中添加:
dataBinding{
enabled = true
}
DataBinding的使用:
在原本的layout布局文件中将最外层的布局标签替换为:< 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>
同时在布局文件中加入data标签,用于指定要绑定的数据类:
<data>
<variable
name=""
type=""/>
</data>
//例如
<data>
<variable
name="item"
type="com.wei.rxjavademo.Item"/>
<!--name可以随意根据需要命名,type为该类型的全类名-->
<variable
name="presenter"
type="com.wei.rxjavademo.MainActivity.Presenter"/>
</data>
修改完布局文件之后就可以在Java代码中使用DataBinding了。
mItem = new Item(12, "hello");
//AS会为每一个layout标签自动生成一个Binding类,用于绑定视图
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
//向布局文件中的variable设置变量
activityMainBinding.setItem(mItem);
activityMainBinding.setPresenter(new Presenter());
Item只是自定义的一个JavaBean,里边只有Id和Content两个属性及其get和set方法
以上就是DataBinding的数据的绑定,接下来还有方法的绑定。
方法的绑定有两种情况:
方法引用绑定:主要是绑定一些已经原有的方法事件,例如onClick,onTextChanged这些方法
监听器绑定:主要是绑定一些自定义的方法事件,可以支持传入数据类型。
方法引用绑定:
自定义一个Presenter类,并在其中实现onClick,onTextChanged等需要的方法,然后在布局文件中直接声明。
在Java中实现onClick方法
public class Presenter {
public void onClick(View view){
Toast.makeText(MainActivity.this, "onClick点击", Toast.LENGTH_SHORT).show();
}
}
在布局文件中直接声明方法的引用:
<Button
android:onClick="@{presenter.onClick}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="订阅"/>
<!--在onClick方法中直接声明引用的方法为Presenter中的onClick方法-->
监听器绑定:
也是要先在Presenter中定义需要的方法,并定义好参数:
public class Presenter {
//在这个方法中可以添加需要的参数
public void onClickListenerBinding(Item item){
Toast.makeText(MainActivity.this, item.getContent(), Toast.LENGTH_SHORT).show();
}
}
只是在布局文件中引用的时候有所不同:
<Button
android:onClick="@{() -> presenter.onClickListenerBinding(item)}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消订阅"/>
<!--在布局文件中的使用方式为Lambda表达式-->
9,WorkManager组件
1. 易于调度
WorkManager API可以轻松创建可延迟的异步任务,并允许您指定应该何时执行。
你可以创建任务并将该任务交给WorkManager,以便立即或在设备处于特定条件下运行该任务。
WorkManager提供了保证,即使您的应用程序强制退出或设备重新启动,你的任务仍会在特定条件匹配时执行。
2. 易于取消
WorkManager给每个任务分配了UUID,使用这个唯一的ID你就可以随时取消任务。
3.易于查询
你可以使用分配给每个任务的唯一标识来询问任务的状态,无论是正在运行,挂起还是已完成。
WorkManager API超越了任务的当前状态,允许任务一键值对格式返回数据。
WorkManager使用LiveData来干会任务的数据和状态,所以,你的Activity可以观察这个LiveData,并且每当任务完成时都会得到通知。
4.支持Android所有版本
WorkManager支持Android API 14及以上
WorkManager根据设备API级别和应用程序状态等因素选择适当的方式来运行你的任务。
如果应用程序正在运行,WorkManager将创建新的线程来运行任务。
如果应用程序没有运行,那么他将使用JobScheduler API或Firebase Job APIs调度者或Alarm manager API运行调度任务。
添加依赖:
implementation "android.arch.work:work-runtime:$work_version"
implementation "android.arch.work:work-firebase:$work_version"
相关类和概念
Work manager APIs建立在几个类上,你必须继承一些抽象类来安排任务。
Worker:在WorkManager世界中,Worker等同于需要在后台执行的任务或作业。这是一个抽象类。你需要继承它。您的Worker类包含有关如何执行该任务的信息,但它没有关于何时运行的信息。
WorkRequest:它代表了工作调度请求。每个工作必须在安排工作之前创建工作请求。 WorkRequest将包含工作的唯一标识,约束条件说明应在哪种情况下执行任务。这是一个抽象类。该库提供了这个类的两个直接子类:OneTimeWorkRequest和PeriodicWorkRequest。
WorkManager:它是基于WorkRequest中定义的约束来管理和调度任务的类。
WorkStatus:这个类包装了任何work请求的状态,你可以通过唯一的id来查询任何work的状态。
使用流程
1.创建work:
创建一个Worker的子类,这个类有一个抽象方法doWork(),顾名思义,你需要在后台执行你想要做的工作,该方法将在后台/工作线程中调用,编写以执行此方法中的任务。
在返回中,你必须返回WorkerResult。返回WorkerResult.SUCCESS表明您执行的任务已成功完成。返回WorkerResult.RETRY告诉WorkManager再次重试该工作。返回WorkerResult.FAILURE表示发生了一个或多个错误。
2.定义约束:
定义约束条件以告诉WorkManager合适安排任务执行,如果没有提供任何约束条件,那么该任务将立即运行。
以下是仅在设备充电和限制时才运行任务的约束。
3.创建work request
你可以创建OneTimeWorkRequest来运行一次任务。
如果你想定期运行一个任务,那么创建一个PeriodicWorkRequest并设置工作的时间间隔。
观察输出数据
Work manager为所有工作请求管理工作状态和LiveData,应用程序可以观察到LiveData在工作状态发生变化时得到通知。
你也可以通过调用getOutputData()来读取输出数据。