美文网首页
哥哥带你学Jatpack

哥哥带你学Jatpack

作者: 哥哥是欧巴Vitory | 来源:发表于2019-04-17 09:54 被阅读0次

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

    相关文章

      网友评论

          本文标题:哥哥带你学Jatpack

          本文链接:https://www.haomeiwen.com/subject/oetbwqtx.html