哥哥带你学Jatpack

声明,这里是我平时日常的笔记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()来读取输出数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容