jetpack组件——入门篇

Lifecycle

  • 添加依赖
    def lifecycle_version = "2.2.0"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
  • 管理fragment和activity的生命周期
    OnLifecycleEvent注解表示监听哪个生命周期
  • 使用
class LifecycleObserver : LifecycleObserver {
    private val TAG = LifecycleObserver::class.simpleName

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun create() {
        Log.e(TAG, "LifecycleObserver->onCreate")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun start(){
        Log.e(TAG, "LifecycleObserver->onStart")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume(){
        Log.e(TAG, "LifecycleObserver->onResume")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stop(){
        Log.e(TAG, "LifecycleObserver->onStop")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destory(){
        Log.e(TAG, "LifecycleObserver->onDestory")
    }
}

activity中使用

class MainActivity : AppCompatActivity(){
    protected override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val observer=LifecycleObserver()
        lifecycle.addObserver(observer)
    }
}

使用很简单,实际就是用一个类统一管理了activity或者fragment的生命周期

LifecycleService解耦Service组件

  • LocationServer
class LocationServer : LifecycleService() {
    init {
        val observer=LocationObserver(this)
        lifecycle.addObserver(observer)
    }

}
  • LocationObserver
class LocationObserver(var mContext: Context) : LifecycleObserver {
    private lateinit var locationListener: MyLocationListener
    private lateinit var localManager: LocationManager

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun startLocation() {
        localManager = mContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return
        }
        locationListener = MyLocationListener()
        localManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1f, locationListener)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stop() {
        localManager.removeUpdates(locationListener)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destory() {
        Log.e("TAG", "LifecycleObserver->onDestory")
    }

    class MyLocationListener : LocationListener {
        override fun onLocationChanged(location: Location?) {

        }

        override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
        }

        override fun onProviderEnabled(provider: String?) {
        }

        override fun onProviderDisabled(provider: String?) {
        }

    }
}

ProcessLifecycleOwner

  • 针对整个应用程序的监听,与Activity的数量无关
  • Lifecycle.Event.ON_CREATE只会被调用一次,Lifecycle.Event.ON_DESTROY永远不会被调用
class App : Application() {
    override fun onCreate() {
        super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(ApplicationObserver())
    }
}
class ApplicationObserver :LifecycleObserver{
    private val TAG = LifecycleObserver::class.simpleName

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun create() {
        Log.e(TAG, "LifecycleObserver->onCreate")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun start(){
        Log.e(TAG, "LifecycleObserver->onStart")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume(){
        Log.e(TAG, "LifecycleObserver->onResume")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stop(){
        Log.e(TAG, "LifecycleObserver->onStop")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destory(){
        Log.e(TAG, "LifecycleObserver->onDestory")
    }
}

ViewModel

viewmodel_view_model之间的关系
  • ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据
  • 使用
class MainViewModel : ViewModel() {
    var number = 0
}

activity的代码

    lateinit var mainViewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        value.text= mainViewModel.number.toString()
        button1.setOnClickListener {
            mainViewModel.number++
            value.text = mainViewModel.number.toString()
        }
        button2.setOnClickListener{
            mainViewModel.number--
            value.text = mainViewModel.number.toString()
        }
    }

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/value"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="0"
        android:textSize="18sp" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:text="加" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:text="减" />


</LinearLayout>

LiveData

viewmodel和liveData之间的关系.png
  • 在底层数据库更改时通知视图(实际是观察者和被观察者)
  • 使用
class MainViewModel : ViewModel() {
    private var numbers: MutableLiveData<Int>? = null

    fun getNumber(): MutableLiveData<Int>? {
        if (numbers == null) {
            numbers = MutableLiveData()
            numbers!!.value = 0
        }
        return numbers
    }

    fun addNumber(data: Int) {
        numbers?.value = numbers?.value?.plus(data)
    }
}
    lateinit var mainViewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        mainViewModel.getNumber()?.observe(this, Observer {
            value.text = it.toString()
        })
        button1.setOnClickListener {
            mainViewModel.addNumber(1)
        }
        button2.setOnClickListener {
            mainViewModel.addNumber(-1)
        }
    }

DataBinding

  • 关于dataBinding详细使用可以看我以前写的博客数据绑定技术DataBinding
    需要在build.gradle中的 defaultConfig添加以下代码
   dataBinding {
            enabled true
        }

修改布局

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
       <variable
           name="data"
           type="com.peakmain.jetpack.MainViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{String.valueOf(data.number)}"
            android:textSize="18sp" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="10dp"
            android:onClick="@{()->data.addNumber(1)}"
            android:text="加" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="10dp"
            android:onClick="@{()->data.addNumber(-1)}"
            android:text="减" />


    </LinearLayout>
</layout>

MainViewModel管理界面中的数据

class MainViewModel : ViewModel() {
    private var number: MutableLiveData<Int>? = null
    fun getNumber(): MutableLiveData<Int> {
        if (number == null) {
            number = MutableLiveData()
            number!!.value = 0
        }
        return number!!
    }

    fun addNumber(data: Int) {
        number!!.value = number!!.value!! + data
    }
}

activity的使用

    lateinit var binding: ActivityMainBinding
    private lateinit var mainViewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        mainViewModel=ViewModelProviders.of(this).get(MainViewModel::class.java)//创建mainViewModel
        binding.data = mainViewModel
        binding.lifecycleOwner = this
    }

ViewModelSavedState

即使进程在后台被系统杀死数据也存活
修改MainViewModel

class MainViewModel(state: SavedStateHandle) : ViewModel() {
    private val handle: SavedStateHandle = state

    companion object {
        const val KEY_NUMBER = "key_number"
    }

    fun getNumber(): MutableLiveData<Int> {
        if (!handle.contains(KEY_NUMBER)) {
            handle.set(KEY_NUMBER, 0)
        }
        return handle.getLiveData(KEY_NUMBER)
    }

    fun addNumber(data: Int) {
        getNumber().value = getNumber().value!!.plus(data)
    }

}

activity中修改mainViewModel

 mainViewModel = ViewModelProvider(this, SavedStateViewModelFactory(this.application, this)).get(MainViewModel::class.java)

AndroidViewModel

方便传context
修改mainViewModel

class MainViewModel(application: Application,state: SavedStateHandle) : AndroidViewModel(application) {
    private val handle: SavedStateHandle = state
    private val context: Application = application

    companion object {
        const val KEY_NUMBER = "key_number"
    }

    fun getNumber(): MutableLiveData<Int> {
        if (!handle.contains(KEY_NUMBER)) {
            handle.set(KEY_NUMBER, 0)
        }
        return handle.getLiveData(KEY_NUMBER)
    }

    fun addNumber(data: Int) {
        getNumber().value = getNumber().value!!.plus(data)
    }

    fun startActivity() {
        val intent=Intent(context, MainActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        context.startActivity(intent)
    }
}

Room

  • 持久性库在 SQLite 的基础上提供了一个抽象层
  • 添加依赖
  apply plugin: 'kotlin-kapt'
    def room_version = "2.2.5"

    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:$room_version"
    // Test helpers
    testImplementation "androidx.room:room-testing:$room_version"

Entity、Dao、Database
1.Entity:实体类,对应的是数据库的一张表结构
2.Dao:包含访问一系列访问数据库的方法
3.DataBase:数据持有者,作为与应用持久化相关数据的底层连接的主要接入点。
使用 @Database 注释的类应满足以下条件:

  • 是扩展 RoomDatabase 的抽象类。
  • 在注释中添加与数据库关联的实体列表。
  • 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。

Entity表示数据库中的表,Dao包含用于访问数据库的方法

@Entity
class Word() {

    @PrimaryKey(autoGenerate = true)
     var id: Int = 0
    @ColumnInfo(name = "english_word")
    var word: String = ""
    @ColumnInfo(name = "chinese_mean")
    var chineseMean: String = ""

    constructor(word: String, chineseMean: String) : this() {
        this.word = word
        this.chineseMean=chineseMean
    }
}
  • Dao包含用于访问数据库的方法
@Dao
interface WordDao {
    @Insert
    fun insertWord(vararg words: Word?)

    @Update
    fun updateWord(vararg words: Word?)

    @Delete
    fun deleteWord(vararg words: Word?)

    @Query("DELETE FROM WORD")
    fun deleteAllWords()

    @Query("SELECT *FROM WORD ORDER BY ID DESC ")
    fun getAllWords():List<Word>

}
  • Database
@Database(entities = [Word::class], version = 1, exportSchema = false)
abstract class WordDatabase : RoomDatabase() {
    abstract fun getWordDao(): WordDao
}

activity的使用

class MainActivity : AppCompatActivity() {
    lateinit var wordDatabase: WordDatabase
    lateinit var wordDao: WordDao
    lateinit var binding: ActivityMainBinding
    private lateinit var mainViewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        mainViewModel = ViewModelProvider(this, SavedStateViewModelFactory(this.application, this)).get(MainViewModel::class.java)
        binding.data = mainViewModel
        binding.lifecycleOwner = this
        wordDatabase = Room.databaseBuilder(this, WordDatabase::class.java, "word_database")
                .allowMainThreadQueries()
                .build()
        wordDao = wordDatabase.getWordDao()
        updateView()
    }
    //更新view
    fun updateView() {
        val list = wordDao.getAllWords()
        var test = ""
        for (word in list) {
            test = test + word.id + ":" + word.word + "=" + word.chineseMean + "\n"
        }
        value.text = test
    }
    //添加数据
    fun add(view: View) {
        val word = Word("peakmain", "皮克曼")
        val word1 = Word("body", "宝宝")
        wordDao.insertWord(word, word1)
        updateView()
    }
   //更新
    fun update(view: View) {
        val word = Word("Treasure", "喜")
        word.id = 1
        wordDao.updateWord(word)
        updateView()
    }
}

代码修改一
问题:1、会存在多个对象的实体类
2、mainviewmodel实际应该只是管理数据而不负责创建数据和删除数据等功能

  • WordDatabase单例
@Database(entities = [Word::class], version = 1, exportSchema = false)
abstract class WordDatabase : RoomDatabase() {
    companion object {
        private var INSTANCE: WordDatabase? = null

        fun getInstance(context: Context): WordDatabase {
            if (INSTANCE == null) {
                synchronized(this) {
                    if (INSTANCE == null) {
                        INSTANCE = Room.databaseBuilder(context.applicationContext
                                , WordDatabase::class.java, "word_database")
                                .build()

                    }
                }
            }
            return INSTANCE!!
        }
    }

    abstract fun getWordDao(): WordDao
}
  • 修改wordDao里面获取数据
    @Query("SELECT *FROM WORD ORDER BY ID DESC ")
    fun getAllWords():LiveData<List<Word>>
  • 建立仓库
    我们会发现一个问题:ViewModel的作用是和管理界面相关的数据,所以插入数据、更新数据和获取数据我们应该放到一个仓库里面
class WordRepository(context: Context) {
    val wordDatabase: WordDatabase = WordDatabase.getInstance(context.applicationContext)
    val wordDao = wordDatabase.getWordDao()
    val allWordsLive: LiveData<List<Word>> = wordDao.getAllWords()


    fun insertWords(vararg word: Word) {
        InsertAsyncTask(wordDao).execute(*word)
    }

    fun updateWord(vararg word: Word) {
        UpdateAsyncTask(wordDao).execute(*word)
    }

    class InsertAsyncTask(private val wordDao: WordDao) : AsyncTask<Word, Void, Void>() {

        override fun doInBackground(vararg p0: Word): Void? {
            wordDao.insertWord(*p0)
            return null
        }

    }

    class UpdateAsyncTask(private val wordDao: WordDao) : AsyncTask<Word, Void, Void>() {

        override fun doInBackground(vararg p0: Word): Void? {
            wordDao.updateWord(*p0)
            return null
        }

    }
}
  • MainViewModel代码修改
class MainViewModel(application: Application) : AndroidViewModel(application) {
    private val context: Application = application
    var wordRepository: WordRepository = WordRepository(application)
    fun getAllWordsLive(): LiveData<List<Word>> {
        return wordRepository.allWordsLive
    }

    fun insertWords(vararg word: Word) {
        wordRepository.insertWords(*word)
    }

    fun updateWord(vararg word: Word) {
        wordRepository.updateWord(*word)
    }


    fun startActivity() {
        val intent = Intent(context, MainActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        context.startActivity(intent)
    }
}
  • mainActivity代码修改
class MainActivity : AppCompatActivity() {
    lateinit var wordDao: WordDao
    lateinit var binding: ActivityMainBinding
    private lateinit var mainViewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        mainViewModel =
                ViewModelProvider(this, SavedStateViewModelFactory(this.application, this))
                        .get(MainViewModel::class.java)
        binding.data = mainViewModel
        binding.lifecycleOwner = this
        mainViewModel.getAllWordsLive().observe(this, Observer<List<Word>> { t ->
            var test = ""
            for (word in t) {
                test = test + word.id + ":" + word.word + "=" + word.chineseMean + "\n"
            }
            value.text = test
        })
    }

    //添加数据
    fun add(view: View) {
        val word1 = Word("peakmain", "皮克曼")
        val word2 = Word("body", "宝宝")
        mainViewModel.insertWords(word1,word2)
    }

    //更新
    fun update(view: View) {
        val word = Word("Treasure", "喜")
        word.id = 3
        mainViewModel.updateWord(word)
    }
}

迁移表结构

  • word修改
    @ColumnInfo(name = "showChinese")
    var showChinese: Boolean = false
  • WordDatabase
@Database(entities = [Word::class], version = 2, exportSchema = false)
abstract class WordDatabase : RoomDatabase() {
    companion object {
        private var INSTANCE: WordDatabase? = null

        fun getInstance(context: Context): WordDatabase {
            if (INSTANCE == null) {
                synchronized(this) {
                    if (INSTANCE == null) {
                        INSTANCE = Room.databaseBuilder(context.applicationContext
                                , WordDatabase::class.java, "word_database")
                                .addMigrations(migration1_2)
                                .build()

                    }
                }
            }
            return INSTANCE!!
        }
    }

    abstract fun getWordDao(): WordDao
}

object migration1_2 : Migration(1,2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE word ADD COLUMN showChinese INTEGER NOT NULL DEFAULT 1")
    }
}

删除数据库中某个字段
删除数据库中某个字段步骤

  • 1、创建新表
  • 2、从新表中插入数据
  • 3、删除旧表
  • 4、修改表名为旧表表名
object migration2_3 : Migration(2, 3) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.run {
            execSQL("CREATE TABLE word_tmp (id INTEGER PRIMARY KEY NOT NULL,english_word TEXT," +
                    "chinese_mean TEXT)")
            execSQL("INSERT INTO word_tmp (id,english_word,chinese_mean) " +
                    "SELECT id,english_word,chinese_mean from word")
            execSQL("DROP TABLE word")

            execSQL("ALTER TABLE word_tmp RENAME to word")
        }
    }
}

schema文件

  • Room在每次数据库升级过程中,都会导出一个Schema的文件,这是一个json文件,其中包含了数据库的基本信息,有了该文件,开发者能清除知道数据库历次变更情况。极大方便开发者排查问题
javaCompileOptions {
           annotationProcessorOptions {
             //指定schema文件的位置
               arguments = ["room.schemaLocation":"$projectDir/schemas".toString()]
           }
       }

WorkManager

  • WorkManager为 应用程序中那些不需要及时完成的任务提供了统一的解决方案,以便在设备电量和用户体验之间达到一个比较好的平衡,如:发送日志
  • workManager能保证任务一定被执行,即使应用程序当前不再运行过程中,甚至在设备重启之后,任务依然会在适当的时刻执行,这是因为WorkManager有自己的数据库,有关任务的所有信息都保存在该数据库中。
  • 兼容性广


    image.png
   def work_version = "2.3.4"
    // Kotlin + coroutines
    implementation "androidx.work:work-runtime-ktx:$work_version"
    // optional - GCMNetworkManager support
    implementation "androidx.work:work-gcm:$work_version"
    // optional - Test helpers
    androidTestImplementation "androidx.work:work-testing:$work_version"
class MyWork(@NonNull context: Context, @NonNull workerParams: WorkerParameters)
    : Worker(context, workerParams) {
    override fun doWork(): Result {
        val value = inputData.getString("in_key")
        Log.e("TAG", "value = $value")
        Log.e("TAG", "开始睡眠")
        Thread.sleep(3000)
        Log.e("TAG", "结束睡眠")
        return Result.success(workDataOf("out_key" to "$value result"))
    }
}
         val constraints = Constraints.Builder()
                 //NetworkType.NOT_REQUIRED:对网络没有要求
                 //NetworkType.CONNECTED:网络的时候执行
                 //NetworkType.UNMETERED:不计费的网络比如Wi-Fi
                 //NetworkType.NOT_ROAMING:非漫游网络
                 //NetworkType.METERED:计费网络比如3g,4g下执行
                 //注意:不代表恢复网络,就立马执行
                 .setRequiredNetworkType(NetworkType.METERED)//👈设置条件为网络连接情况
                 .build()
        val workReuest =
               //PeriodicWorkRequest:周期性任务
                OneTimeWorkRequestBuilder<MyWork>()//👈一次性任务
                        //.setConstraints(constraints)//👈设置条件
                        .setInputData(workDataOf("in_key" to "Peakmain"))//👈输入简单数据
                        .setInitialDelay(5L,TimeUnit.SECONDS)//设置5s延迟执行
                        .addTag("peakmain")
                        .build()
        workManager.enqueue(workReuest)
     //监听数据变化
        workManager.getWorkInfoByIdLiveData(workReuest.id)
                .observe(this, Observer {
                    Log.e("TAG", "onCreate:" + it.state)
                    if (it.state == WorkInfo.State.SUCCEEDED) {
                        Log.e("TAG", "onCreate" + it.outputData.getString("out_key"))
                    }
                })
 //取消任务
 workManager.cancelWorkById(workRequest.getId());
  • 任务链和任务组合
    • 责任链


      责任链.png
      WorkManager.getInstance(this)
              .beginWith(A)
              .then(B).enqueue()
  • AB,CD先执行最后执行E


    情况二.png
   var workManager1: WorkContinuation=WorkManager.getInstance(this)
              .beginWith(A)
              .then(B).enqueue()
   var workManager2: WorkContinuation=WorkManager.getInstance(this)
              .beginWith(A)
              .then(B).enqueue()
var takeList = ArrayList<WorkContinuation>()
takeList.add(workManager1)
takeList.add(workManager2)
var workManager: WorkContinuation = WorkContinuation.combine(takeList)
        .then(E)
        .enqeue()

paging

  • paging实际就是分页加载,它把几种常见的分页机制提供了统一的解决方案
  • paging支持的架构类型
    • 网络数据
      分页机制所设计的API接口不一样,但是总体可以分为3种,因此paging提供了3种不同的解决方案,分别是:PositionDataSource、PageKeyedDataSource、ItemKeyedDataSource
    • 数据库
      替换数据源
  • 工作原理


    image.png
  • 3个核心类
    • pagedListAdapter
      • 如果需要使用paging组件,适配器需要继承pagedListAdapter
    • pagedList
      • pagedList负责通知DataSource何时和如何获取数据,从DataSource获取的数据将存储在pagedList中
    • DataSource
      • 执行具体的数据载入工作,数据可以来自网络,也可以来自本地数据、数据库数据,根据分页机制不同,paging提供了3种DataSource
      • 数据的载入需要再工作线程中进行

PositionDataSource

  • 适用于可通过任意位置加载数据,且目标数据源数量固定的情况。
  • 比如:从数据库的第100条数据开始加载20条数据
  • DataSource创建
public class StudentDataSource extends PositionalDataSource<Student> {

    /**
     * 加载第一页数据的时候执行
     *
     * @param params
     * @param callback
     */
    @Override
    public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Student> callback) {
        /**
         * position:位置
         * totalCount:总的大小
         */
        callback.onResult(getStudents(0, 20), 0,1000);
    }

    /**
     * 有了初始化数据之后,滑动的时候如果需要加载数据的话,会调用此方法
     *
     * @param params
     * @param callback
     */
    @Override
    public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Student> callback) {
        callback.onResult(getStudents(params.startPosition, params.loadSize));
    }

    /**
     * 假的数据源
     */
    private List<Student> getStudents(int startPosition, int pageSize) {
        List<Student> students = new ArrayList<>();
        for (int i = startPosition; i < startPosition + pageSize; i++) {
            Student student = new Student("Id是:" + i, "名字:" + i);
            students.add(student);
        }
        return students;
    }
}
  • 数据工厂
/**
 * author :Peakmain
 * createTime:2021/11/5
 * mail:2726449200@qq.com
 * describe:PositionalDataSource对应的Key是Integer
 */
public class StudentDataSourceFactory extends DataSource.Factory<Integer,Student> {
    @NonNull
    @Override
    public DataSource<Integer, Student> create() {
        return new StudentDataSource();
    }
}
  • pageList的创建
public class StudentViewModel extends ViewModel {
    private final LiveData<PagedList<Student>> listLiveData;

    public StudentViewModel() {
        StudentDataSourceFactory factory = new StudentDataSourceFactory();
        this.listLiveData = new LivePagedListBuilder<>(factory, 20).build();
    }

    public LiveData<PagedList<Student>> getListLiveData() {
        return listLiveData;
    }
}
  • PagedListAdapter的创建
public class StudentAdapter extends PagedListAdapter<Student, StudentAdapter.CommonRecyclerViewHolder> {
    private static DiffUtil.ItemCallback<Student> DIFF_STUDENT = new DiffUtil.ItemCallback<Student>() {
        //一般比较的是唯一内容:id
        @Override
        public boolean areItemsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
            return oldItem.getId().equals(newItem.getId());
        }

        //对象本身的比较
        @Override
        public boolean areContentsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
            return oldItem.equals(newItem);
        }
    };

    public StudentAdapter() {
        //diffCallback比较的行为
        super(DIFF_STUDENT);
    }

    @NonNull
    @Override
    public CommonRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.bean_recycler_view, null);
        return new CommonRecyclerViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CommonRecyclerViewHolder holder, int position) {
        Student student = getItem(position);
        holder.mTvName.setText(student.getName());
        holder.mTvId.setText(student.getId());
    }

    public static class CommonRecyclerViewHolder extends RecyclerView.ViewHolder {
        private TextView mTvId, mTvName;

        public CommonRecyclerViewHolder(@NonNull View itemView) {
            super(itemView);
            mTvId = itemView.findViewById(R.id.tv_id);
            mTvName = itemView.findViewById(R.id.tv_name);
        }
    }
}

ItemKeyedDataSource<T>

  • 适用于目标数据的加载依赖特定的item的信息,即key字段包含的是item中的信息
  • 如:根据第N项的信息加载第n+1项的数据,传参中需要传入第N项的ID时
  • 场景:论坛应用评论信息的请求

PageKeyedDataSource

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