安卓Paging是一种分页加载数据的方法,它基于无限滚动模式而设计,可以帮助应用更高效地利用网络带宽和系统资源。Paging库可以加载和显示来自本地存储或网络中更大的数据集中的数据页面,适用于以列表的形式加载大量的数据。
Paging库具有以下功能:
1. 分页数据的内存中缓存,有助于确保应用在处理分页数据时高效使用系统资源。
2. 内置的请求去重功能,可确保应用高效利用网络带宽和系统资源。
3. 可配置的RecyclerView适配器,会在用户滚动到已加载数据的末尾时自动请求数据。
4. 对Kotlin协程和数据流以及LiveData和RxJava的一流支持。
5. 内置对错误处理功能的支持,包括刷新和重试功能。
总之,安卓Paging可以帮助开发者更有效地加载和管理大量数据,提高应用的性能和用户体验。
官方文档:Paging ?|? Jetpack ?|? Android Developers
dependencies {
//Room
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
//Paging
def paging_version = "3.2.1"
implementation "androidx.paging:paging-runtime:$paging_version"
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.9" />
<Button
android:id="@+id/btnPopulate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="生成数据"
app:layout_constraintBottom_toBottomOf="@+id/btnClean"
app:layout_constraintEnd_toStartOf="@+id/btnClean"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/btnClean" />
<Button
android:id="@+id/btnClean"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="清除数据"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/btnPopulate"
app:layout_constraintTop_toTopOf="@+id/guideline2" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="100dp">
<ImageView
android:id="@+id/item_img"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toStartOf="@+id/guideline5"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/baseline_img" />
<TextView
android:id="@+id/item_tv"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="TextView"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/item_img"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="1.0" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.mypaging;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
//实体类
@Entity
public class Student {
//主键
@PrimaryKey(autoGenerate = true)
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.example.mypaging;
import androidx.paging.DataSource;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
//接口 Dao 方法签名
@Dao
public interface StudentDao {
//插入数据
@Insert
void insertStudents(Student... students);
//删除数据
@Query("delete from student")
void deleteTBData();
//查询
@Query("select * from student order by id desc")
DataSource.Factory<Integer, Student> getAllStudents(); //paging
// LiveData<List<Student>> getAllStudents();
}
package com.example.mypaging;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
@Database(entities = {Student.class}, version = 1, exportSchema = false)
public abstract class StudentDataBase extends RoomDatabase {
private static StudentDataBase studentDataBase;
public static StudentDataBase getInstance(Context context) {
if (studentDataBase == null) {
studentDataBase = Room.databaseBuilder(context, StudentDataBase.class, "studentDB").build();
}
return studentDataBase;
}
public abstract StudentDao studentDao();
}
InsertAsyncTask.java
package com.example.mypaging;
import android.os.AsyncTask;
/**
* AsyncTask 在 API 级别 30 中此字段已弃用 , 请改用标准或 Kotlin 并发实用程序
* 插入数据 异步任务
* android.os.AsyncTask<Params、进度、结果>
*/
public class InsertAsyncTask extends AsyncTask<Student, Void, Void> {
private StudentDao studentDao;
public InsertAsyncTask(StudentDao studentDao) {
this.studentDao = studentDao;
}
/**
* 4个步骤
* 执行异步任务时,任务会经历 4 个步骤:
* <p>
* onPreExecute(),在任务之前的 UI 线程上调用 被执行。此步骤通常用于设置任务,例如 在用户界面中显示进度条。
* <p>
* doInBackground(Params),在后台线程上调用 执行完毕后立即执行。
* 此步骤用于 执行可能需要很长时间的后台计算。参数 的异步任务被传递到此步骤。
* 计算结果必须 由此步骤返回,并将传递回最后一步。这一步 还可用于发布一个或多个单元 的进步。
* 这些值在步骤中发布在 UI 线程上。onPreExecute()publishProgress(Progress)onProgressUpdate(Progress)
* <p>
* onProgressUpdate(Progress),在 UI 线程上调用 调用 。
* 执行的时间是 定义。此方法用于在用户中显示任何形式的进度 接口,而后台计算仍在执行。
* 例如 它可用于对进度条进行动画处理或在文本字段中显示日志。publishProgress(Progress)
* <p>
* onPostExecute(Result),在后台之后的 UI 线程上调用 计算完成。将后台计算的结果传递给主界面。
*/
@Override
protected Void doInBackground(Student... students) {
studentDao.insertStudents(students);
return null;
}
}
DeleteTBAsyncTask.java
package com.example.mypaging;
import android.os.AsyncTask;
public class DeleteTBAsyncTask extends AsyncTask<Void, Void, Void> {
private StudentDao studentDao;
public DeleteTBAsyncTask(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
protected Void doInBackground(Void... voids) {
studentDao.deleteTBData();
return null;
}
}
package com.example.mypaging;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
// 列表中的每一项数据
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ImageView imageView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.item_tv);
imageView = itemView.findViewById(R.id.item_img);
}
}
package com.example.mypaging;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.paging.PagedListAdapter;
import androidx.recyclerview.widget.DiffUtil;
public class MyPageAdapter extends PagedListAdapter<Student, MyViewHolder> {
public MyPageAdapter() {
super(new DiffUtil.ItemCallback<Student>() {
@Override
public boolean areItemsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
//比较id是否相等
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
//比较内容是否相等
return oldItem.getName().equals(oldItem.getName());
}
});
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//获取视图对象
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
//获取数据
Student student = getItem(position);
if (student == null) {
//容器中还没有数据时
holder.textView.setText("正在加载...");
} else {
holder.textView.setText(student.getName());
}
}
}
package com.example.mypaging;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class MainActivity extends AppCompatActivity {
private Button btnPopulate, btnClean;
private StudentDataBase studentDataBase;
private StudentDao studentDao;
//往视图中添加数据 需要用适配器
private RecyclerView recyclerView;
private MyPageAdapter myPageAdapter;
//数据获取
LiveData<PagedList<Student>> allStudentsLivePaged;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnPopulate = findViewById(R.id.btnPopulate);
btnClean = findViewById(R.id.btnClean);
recyclerView = findViewById(R.id.recyclerView);
myPageAdapter = new MyPageAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
//分隔符
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
//往视图中添加适配器
recyclerView.setAdapter(myPageAdapter);
studentDataBase = StudentDataBase.getInstance(this);
studentDao = studentDataBase.studentDao();
allStudentsLivePaged = new LivePagedListBuilder<>(studentDao.getAllStudents(), 3).build();
//观察
allStudentsLivePaged.observe(this, new Observer<PagedList<Student>>() {
@Override
public void onChanged(PagedList<Student> students) {
//内容发生改变
myPageAdapter.submitList(students);
students.addWeakCallback(null, new PagedList.Callback() {
@Override
public void onChanged(int i, int i1) {
Log.e("TAG", "----观察--" + students);
}
@Override
public void onInserted(int i, int i1) {
}
@Override
public void onRemoved(int i, int i1) {
}
});
}
});
//按钮事件
btnPopulate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Student[] students = new Student[100];
for (int i = 0; i < 100; i++) {
Student student = new Student();
student.setName("学生"+i);
students[i] = student;
}
new InsertAsyncTask(studentDao).execute(students);
}
});
//事件
btnClean.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new DeleteTBAsyncTask(studentDao).execute();
}
});
}
}