Android Jetpack套件之Paging分页组件

本文介绍了Google在2018 I/O大会推出的Android Jetpack,重点讲解Paging分页组件。先说明了引入库的选择,接着将实现分页加载功能分为准备数据和绑定UI两部分,详细阐述各部分步骤及流程,还给出适合初学者的Demo链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Google 2018 I/O大会上,Google正式推出了AndroidJetpack ——这是一套组件、工具和指导,可以帮助开发者构建出色的 Android 应用。
架构组件可帮助您设计稳健、可测试且易维护的应用。

  • 数据绑定

    以声明方式将可观察数据绑定到界面元素

  • Lifecycles

    管理您的 Activity 和 Fragment 生命周期

  • LiveData

    在底层数据库更改时通知视图

  • Navigation

    处理应用内导航所需的一切

  • Paging

    逐步从您的数据源按需加载信息

  • Room

    流畅地访问 SQLite 数据库

  • ViewModel

    以注重生命周期的方式管理界面相关的数据

而今天我们要将的是

 Paging分页组件

有英文阅读能力,并且已经掌握了Kotlin的可以通过下面链接浏览官方文档,毕竟官方的才是最接近真理的:https://developer.android.google.cn/topic/libraries/architecture/paging/

喜欢简单一点的,我只想先学会简单地访问网络加载数据,怎么办?那你找对地方了,功能没实现,你说得天花乱坠都是瞎扯淡。那我们一步一步来吧!

引入库

3个都可以实现paging,这里为了简单,就不去涉及rxjava了,我们引入runtime版本(如果不需要调试功能的,可以使用common版本,博主使用过common版本,总觉得少点什么东西,后来就换回runtime版本)


dependencies {
    //...
    implementation 'android.arch.paging:runtime:1.0.1'
    //...

}

实现分页加载功能

 

为了方便大家理解,我将它们分成两个部分:

  • 准备数据部分
  • 绑定UI部分

准备数据部分

1、定义DataSource,这里放关键的加载数据逻辑

/*
    //step8、定制自己的DataSource,继承哪个DataSource是根据后台接口来定的,这里的接口是pageCount和pageSize
    所以,我们需要继承PositionalDataSource<T>,并且实现他们的抽象函数
    */
    class MyDataSource extends PositionalDataSource<MusicBean>{
        List<MusicBean> listData = new ArrayList<>();
        /**
         * recyclerView第一次加载时自动调用
         *
         * @param params   包含当前加载的位置position、下一页加载的长度count
         * @param callback 将数据回调给UI界面使用callback.onResult
         */
        @Override
        public void loadInitial(@NonNull LoadInitialParams params, @NonNull final LoadInitialCallback<MusicBean> callback) {
            //step9、在这里请求网络加载数据

            LogUtils.i(this,"loadInitial "+pageCount);
            //计算显示到第几条
            final int position = computeInitialLoadPosition(params, PAGE_SIZE);
            //recyclerView第一次加载时我们调用OkHttp进行数据的加载
            Map<String, String> paramsGet = new HashMap<>();
            paramsGet.put("page", pageCount+"");
            paramsGet.put("count", PAGE_SIZE + "");
            paramsGet.put("type", "video");
            HttpUtils.getAllApiInCompany().getVideoBean(paramsGet).enqueue(new Callback<MusicResponeBean>() {
                @Override
                public void onResponse(Call<MusicResponeBean> call, Response<MusicResponeBean> response) {
                    List<MusicBean> tmpList = response.body().getResult();
                    listData.addAll(tmpList);

                    //step10、
                    //最重要的一步,paging是基于观察者模式,我们在这里调用callback.onResult();
                    //会直接将数据list返回到UI层,等下面接受到这个list数据的数据的时候我会提醒大家
                    //如果设置占位符需要调用三个参数的onResult()方法,最后一个参数为每页的总数据量
                    //我们没有设置占位符,因此调用两个参数的方法,注意position通过computeInitialLoadPosition(params, PAGE_SIZE)获取
                    callback.onResult(listData,position);
                }

                @Override
                public void onFailure(Call<MusicResponeBean> call, Throwable t) {

                }
            });

        }

        /**
         * 当用户滑动recyclerView到下一屏的时候自动调用,这里我们自动加载下一页的数据
         *
         * @param params   包含当前加载的位置position、下一页加载的长度count
         * @param callback 将数据回调给UI界面使用callback.onResult
         */
        @Override
        public void loadRange(@NonNull LoadRangeParams params, @NonNull final LoadRangeCallback<MusicBean> callback) {

            //step11、请求网络加载更多数据,执行在子线程
            pageCount++;
            LogUtils.i(this,"loadRange "+pageCount);

            Map<String, String> paramsGet = new HashMap<>();
            paramsGet.put("page", pageCount + "");
            paramsGet.put("count", PAGE_SIZE + "");
            paramsGet.put("type", "video");
            HttpUtils.getAllApiInCompany().getVideoBean(paramsGet)
                    .enqueue(new Callback<MusicResponeBean>() {
                @Override
                public void onResponse(Call<MusicResponeBean> call, Response<MusicResponeBean> response) {
                    List<MusicBean> tmpList = response.body().getResult();
                    listData.addAll(tmpList);

                    //step12、请求网络加载更多数据成功,调用callback的onResult()函数,传入总的list触发ui更新
                    callback.onResult(listData);
                }

                @Override
                public void onFailure(Call<MusicResponeBean> call, Throwable t) {

                }
            });
        }
    }

2、DataSource.Factory,构建PagedList所需

    /**
     * 自定义DataSource.Factory
     * //step7、定义一个DataSource.Facotry的子类,因为后台给的接口是那种有pageSize和pageCount的那种,
     * 所以这里选择返回的DataSource应该是PositionalDataSource<T>这种,别问我为什么,google就是这么说的。其他的情况可以参考
     * https://www.jianshu.com/p/ad040aab0e66
     */
    class PageDataSourceFacotry extends DataSource.Factory {

        PositionalDataSource<MusicBean> positionalDataSource;

        public PageDataSourceFacotry(PositionalDataSource<MusicBean> positionalDataSource) {
            this.positionalDataSource = positionalDataSource;
        }

        @Override
        public DataSource create() {
            return positionalDataSource;
        }
    }

3、构建LiveData<PagedList<T>>(使用Paging组件时,不用用List<T>在装载数据了,需要使用PagedList<T>),通过LivePagedListBuilder.builder方法生成,需要传入的参分别是前面的的DataSource.Factory和PagedList.Config。

 private void initPagedList() {
        //step13、生成自定义的DataSource对象
        PositionalDataSource<MusicBean> positionalDataSource = new MyDataSource();

        //step14、通过PagedList.Config.Builder()生成配置对象,传入的到LivePagedListBuilder中
        PagedList.Config config = new PagedList.Config.Builder()
                .setPageSize(PAGE_SIZE)//每次加载的数据数量15
                //距离本页数据几个时候开始加载下一页数据(例如现在加载10个数据,设置prefetchDistance为2,则滑到第八个数据时候开始加载下一页数据).
                .setPrefetchDistance(5)//15
                //这里设置是否设置PagedList中的占位符,如果设置为true,我们的数据数量必须固定,由于网络数据数量不固定,所以设置false.
                .setEnablePlaceholders(false)
                .setInitialLoadSizeHint(PAGE_SIZE)//15
                .build();


        /*
        //step6、新建LiveData<PagedList<T>>,需要通过LivePagedListBuilder来完成,里面需要两个参数。
        一个是:DataSource.Factor,作用就是返回一个DataSource,有几种类型,(PositionalDataSource适合接口是带pageCount,pageSize)
        一个是:PagedList.Config,配置主要分页加载参数,如每页加载多少条数据等
        */
        // 构建LiveData
        pagedListLiveData = new LivePagedListBuilder(new PageDataSourceFacotry(positionalDataSource)//自己定义
                , config).build();

    }

 

绑定UI部分

1、原来的adapter需要实现PagedListAdapter,注意获取itemBean的时候需要用getItem()

2、给LiveData注册observer的时候,通过adapter.submitList(PagedList)绑定

public class SamplePagingActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    private PagingViewModel pagingViewModel;
    private MyPagingAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample_paging);


        pagingViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(PagingViewModel.class);

        //step3、获取ViewModel中的LiveData(LiveData<T> ,T必须要是一个PagedList<>类型),注册观察者
        pagingViewModel.getPagedListLiveData().observe(this, new Observer<PagedList<MusicBean>>() {
            @Override
            public void onChanged(@Nullable PagedList<MusicBean> musicBeans) {
                //step4、通过submitList()函数触发adapter数据更新
                adapter.submitList(musicBeans);
            }
        });

        recyclerView = findViewById(R.id.rv);

        //step1、adapter换成PagingAdapter
         adapter = new MyPagingAdapter( new DiffUtil.ItemCallback<MusicBean>() {
            @Override
            public boolean areItemsTheSame(@NonNull MusicBean musicBean, @NonNull MusicBean t1) {
                return musicBean.text.equals(t1.text);//比对唯一标识码
//                return false;
            }

            @SuppressLint("DiffUtilEquals")
            @Override
            public boolean areContentsTheSame(@NonNull MusicBean musicBean, @NonNull MusicBean t1) {
                return musicBean.equals(t1);//比对对象
//                return false;
            }
        });
        recyclerView.setAdapter(adapter);

    }

    class MyPagingAdapter extends PagedListAdapter<MusicBean, RecyclerView.ViewHolder> {

        protected MyPagingAdapter(@NonNull DiffUtil.ItemCallback<MusicBean> diffCallback) {
            super(diffCallback);
        }

        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {

            View view = LayoutInflater.from(SamplePagingActivity.this).inflate(android.R.layout.simple_list_item_1, null);
            RecyclerView.ViewHolder viewholder = new RecyclerView.ViewHolder(view) {
            };
            return viewholder;
        }

        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
            //step2、获取list的方法必须换成PagingAdapter的getItem()函数
            String str = getItem(i).text;
            ((TextView) viewHolder.itemView.findViewById(android.R.id.text1)).setText(str);
        }
    }


}

 

这里先简述一下流程:

  1. 首先我们需要一个DataSource:根据后台借口,应用不同的DataSource,Android api封装了3种
  2. 然后创建一个DataSource.Factory:实现抽象方法,返回一个DataSource
  3. 创建一个PagedList.Config:设置加载配置,如每页加载多少条数据
  4. 在ViewModel中创建LiveData<PagedList<T>>,通过LivePagedListBuilder创建,需要用到上面3个数据
  5. adapter换成PagingAdapter,bindView中获取list的方法必须换成PagingAdapter的getItem()函数
  6. 获取ViewModel中的LiveData(LiveData<T> ,T必须要是一个PagedList<>类型),注册观察者,在onChanged()内通过submitList()函数触发adapter数据更新

 

 

对于Android Jetpack的套件,我正在整理一个比较简单,适合初学者的Demo,特别适合快速入门,有兴趣的童鞋们可以去下载看看:https://github.com/KubyWong/JetpackSample

 

<think>好的,我现在需要帮用户设计一个基于安卓的健身社交App的技术路线。用户之前已经询问过个性化推荐的技术,现在他们可能希望扩展到一个具体的应用场景,也就是健身社交。首先,我需要明确用户的需求是什么。健身社交App通常包括用户注册、健身数据记录、社交互动(如分享、点赞、评论)、可能还有个性化推荐等功能。 用户提到要“技术路线简约”,所以应该避免过于复杂的架构,但又要保证核心功能的实现。我需要考虑安卓开发的常用技术栈,以及后端服务的选择。可能还需要考虑数据存储、实时通信、第三方API集成(如健康数据获取)、用户隐私和安全等方面。 首先,前端部分,安卓原生开发可以使用Kotlin,因为现在Google推荐使用Kotlin替代Java。界面设计方面,Jetpack Compose可以提高开发效率,适合简约路线。然后,后端可能需要一个轻量级的框架,比如Node.js或者Spring Boot,但考虑到简约,可能使用Firebase这样的BaaS(后端即服务)可以快速搭建,减少服务器管理的麻烦。 数据存储方面,Firebase提供了实时数据库和Firestore,适合社交功能中的实时互动。用户认证也可以用Firebase Auth,支持多种登录方式。健身数据的处理可能需要集成安卓的Health Connect API来获取步数、心率等数据,或者使用Google Fit API。 社交功能需要实现帖子发布、动态流、点赞评论等,可能用到RecyclerView来展示动态列表,图片上传可以使用Glide或Picasso加载图片,存储到Firebase Storage。实时聊天可能需要WebSocket或者Firebase的实时数据库。 个性化推荐部分,之前用户已经了解过相关技术,这里可能需要简化,比如基于用户的活动类型和社交关系做简单的协同过滤,或者使用Firebase的机器学习套件进行本地模型推理。 注意事项方面,用户隐私和数据安全是关键,特别是健康数据属于敏感信息,需要符合GDPR或其他相关法规。此外,App性能优化也很重要,比如减少APK大小,使用ProGuard混淆代码,优化网络请求等。 测试方面,需要单元测试和UI测试,可以使用JUnit和Espresso。持续集成/持续部署(CI/CD)可以用GitHub Actions或者Jenkins自动化构建流程。 可能还需要第三方服务,比如地图API显示跑步路线,支付接口如果涉及会员系统,但用户没有提到,暂时可能不需要。错误监控和日志收集可以用Firebase Crashlytics。 总结一下,技术路线应该分为客户端、服务端、数据存储、第三方集成、安全与性能几个部分,每个部分选择最简单高效的工具和框架,确保快速开发和部署,同时保持可扩展性,方便后续迭代。</think>以下为基于安卓的健身社交App的技术路线简约版,采用模块化分层设计,兼顾开发效率与扩展性: --- ### 一、技术架构图 ``` [客户端]              [服务端]             [第三方服务] ├─ Android(Kotlin)    ├─ Spring Boot      ├─ 高德地图API ├─ Jetpack Compose    ├─ MySQL/MongoDB    ├─ 支付宝SDK └─ Health Connect     └─ Redis            └─ 极光推送 ``` --- ### 二、核心模块技术选型 #### 1. **运动数据采集** - **硬件层** - 手机传感器:通过`SensorManager`获取加速度/陀螺仪数据 - 智能设备:蓝牙BLE协议连接手环/手表(`BluetoothGatt`) - 健康数据同步:集成Android Health Connect API(步数/心率) - **数据处理** ```kotlin // 计步算法示例(峰值检测) fun detectStep(accValues: List<Float>): Int {     val threshold = 9.8 * 1.5  // 1.5g加速度阈值     return accValues.count { it > threshold } } ``` #### 2. **社交功能实现** - **动态发布** - 图片处理:Glide + GPUImage(滤镜/压缩) - 视频处理:ExoPlayer + FFmpeg(关键帧提取) - LBS定位:高德地图SDK的`AMapLocationClient` - **即时互动** - 消息推送:极光推送WebSocket长连接 - 评论分级加载:SQLite本地缓存 + 分页查询`Paging3` #### 3. **推荐系统(轻量化)** - 用户特征:`UserVector = [运动类型, 活跃时段, 社交网络]` - 物品表征:`ItemEmbedding = Word2Vec(运动标签)` - 匹配算法:改进的协同过滤 $$ \text{Similarity} = \frac{\sum (R_{u,i} - \bar{R_u})(R_{v,i} - \bar{R_v})}{\sigma_u \sigma_v} $$ --- ### 三、关键实现路径 1. **开发环境** - Android Studio Arctic Fox + AGP 7.0 - 最低兼容API 24(Android 7.0) 2. **通信协议** - RESTful API:Retrofit2 + Moshi(协议加密采用HTTPS+SSL Pinning) - 数据传输格式:ProtoBuf(比JSON节省30%流量) 3. **状态管理** - 单一数据源:采用MVI架构模式 - 全局状态:ViewModel + Hilt依赖注入 --- ### 四、性能优化重点 | 模块               | 优化手段                         | 目标指标         | |--------------------|----------------------------------|------------------| | 启动速度           | App Startup库 + 延迟加载         | 冷启动<800ms   | | 内存管理           | LeakCanary检测 + 图片弱引用     | OOM率<0.01%     | | 网络请求           | OkHttp缓存策略 + Stetho监控     | 重复请求减少40% | | 运动算法           | NDK移植计步核心逻辑             | 功耗降低15%     | --- ### 五、隐私与安全 1. 敏感权限动态申请(位置/相机) ```xml <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" /> <uses-feature android:name="android.hardware.sensor.accelerometer" /> ``` 2. 数据加密:AES-256加密本地运动记录 3. GDPR合规:提供「数据导出与删除」入口 --- ### 六、部署方案 - **服务端**:阿里云ECS(2核4G)Docker容器化部署 - **数据库**:云数据库MySQL版(读写分离)+ Redis缓存热点数据 - **持续交付**:GitLab CI/CD自动化构建 -> 华为应用市场灰度发布 --- 该路线可实现MVP版本3个月内上线,建议优先开发核心功能(运动记录+动态广场),后续迭代引入直播训练课等增值服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值