【安卓基础重点知识9】常用组件库(Glide、EventBus、SmartRefreshLayout)

0 阅读12分钟

1. Glide

1.1 简介

  • Glide 是一个快速高效的 Android 开源媒体管理和图像加载框架,它将媒体解码、内存和磁盘缓存以及资源池封装到一个简单易用的界面中

  • 支持拉取,解码和展示视频快照,图片,和GIF动画

  • Glide的Api十分灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库

  • Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求

1.2 权限

1.2.1 网络加载

通过网络连接加载图像,将INTERNET和ACCESS_NETWORK_STATE权限添加到你的AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

1.2.2 本地存储

要从DCIM或图片等本地文件夹加载图像,需要添加READ_EXTERNAL_STORAGE权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

要将 Glide 的缓存存储在公共 sdcard 上,需要使用 WRITE_EXTERNAL_STORAGE权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

1.3 性能

Glide 充分考虑了Android图片加载性能的两个关键方面:1.图片解码速度;2.解码图片带来的资源压力

为了让用户拥有良好的App使用体验,图片不仅要快速加载,而且还不能因为过多的主线程I/O或频繁的垃圾回收,导致页面的闪烁和抖动现象。

Glide使用了多个步骤来确保在Android上加载图片尽可能的快速和平滑:

  • 自动、智能地下采样(downsampling)和缓存(caching),以最小化存储开销和解码次数

  • 积极的资源重用,例如字节数组和Bitmap,以最小化昂贵的垃圾回收和堆碎片影响

  • 深度的生命周期集成,以确保仅优先处理活跃的Fragment和Activity的请求,并有利于应用在必要时释放资源以避免在后台时被杀掉

1.4 简单用法

1.4.1 引入依赖

implementation("com.github.bumptech.glide:glide:4.12.0")
//注解处理器(Glide's annotation processor)是用来在编译时生成一些额外的代码
//以帮助Glide优化图片加载性能
implementation("com.github.bumptech.glide:compiler:4.12.0")

注意:加载网络图片,一定要在AndroidMainfest.xml文件中设置网络权限

1.4.2 相关属性

  • 编写布局文件,在Activity中使用
Glide.with(context)
     .asGif()//设置本次加载为 GIF 动态图
     .load(url)//图片地址
     .priority(Priority.HIGH)//设置加载的优先级(Priority.LOW、Priority.NORMAL、
                                             //Priority.HIGH、Priority.IMMEDIATE)
     .placeholder(R.drawable.placeholder)//占位符
     .error(R.drawable.error)//加载错误显示的图片
     .fallback(R.drawable.ic_launcher_background)//设置备用图片,参数可以是Id,也可以是Drawable对象
     .thumbnail(0.1f)//缩略图,设置图片的缩略图大小,其中0.1f表示原始图像的10%大小
     .transform(new CircleCrop())//设置图片形式,CircleCrop()将图片设置为圆形,
                                //RoundedCorners(50)将图片设置为圆角
     .transition(DrawableTransitionOptions.withCrossFade())//设置淡入淡出的动画效果
     .into(imageView)//显示图片对应ImageView控件的id

补充:

  • 其中load()可以加载以下几种类型的图片资源

    .load(String string)string可以为一个文件路径、uri或者url
    .load(Uri uri)uri类型
    .load(File file)文件
    .load(Integer resourceId)资源Id,R.drawable.xxx或者R.mipmap.xxx
    .load(byte[] model)byte[]类型
    .load(T model)自定义类型
  • fallback()和error()的区别:

    1. fallback() 方法:

      • 用于设置备用图片资源,在主请求(load(imageUrl))失败时显示
    2. .error() 方法:

      • 设置在加载出错时显示的图片资源。与 fallback() 不同的是,error() 主要用于处理加载失败的情况(例如网络错误或者无效的 URL),而 fallback() 则是在主请求无法加载时使用备用图片。

1.4.3 图片缓存

Glide使用双缓存策略来管理图片缓存。它在内存中缓存未经压缩的原始图片,提升加载速度,同时还会将压缩后的图片存储在磁盘中,节省内存和流量。

根据图片的URL或资源ID生成唯一的缓存键,以确保不同的图片不会混淆。此外,Glide支持自定义缓存的大小和有效期,以适应不同需求。

  • 内存缓存

Glide使用内存缓存来存储最近使用的图像数据,以便快速访问。内存缓存基于LRU(Least Recently Used,最近最少使用)算法,保留最近加载的图像数据。当应用需要再次访问这些图像时,可以直接提供,从而避免频繁的网络请求和磁盘读取。

  • 磁盘缓存

    • 活动资源缓存(Active Resources Cache): 这是一个小型、可写的磁盘缓存,存储当前正在使用的图像数据。它有助于减少频繁加载的图片的重复磁盘读取。

    • 未活动资源缓存(Inactive Resources Cache): 这是一个更大、只读的磁盘缓存,用于长期存储已加载的图像数据。当活动资源缓存已满时,Glide会将不再活跃的图片从活动缓存中移至未活动资源缓存,以腾出空间供新图片使用。

  • 缓存策略

    • DiskCacheStrategy.ALL: 在内存和磁盘上都缓存。

    • DiskCacheStrategy.NONE: 不使用磁盘缓存。

    • DiskCacheStrategy.DATA: 只缓存原始数据。

    • DiskCacheStrategy.RESOURCE: 只缓存转换后的资源。

    • DiskCacheStrategy.AUTOMATIC: 根据图片数据源自动选择缓存策略。

       RequestOptions options = new RequestOptions()
                                .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC);

1.4.4 清除缓存

若图片发生变化或需要释放存储空间,可手动清除Glide缓存。

  • 清除磁盘缓存:
Glide.get(this).clearDiskCache();
  • 清除内存缓存可使用:
Glide.get(this).clearMemory();     
  • 清除所有图片加载请求
Glide.clear()

2. EventBus

2.1 简介

EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递。传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦

  • **事件(Event):**又可称为消息。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属的 Class。事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件

  • **订阅者(Subscriber):**订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 onEvent 函数,这个函数叫事件响应函数。订阅者通过 register 接口订阅某个事件类型,unregister 接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为 0

  • **发布者(Publisher):**发布某事件的对象,通过 post 接口发布事件

2.2 工作流程图解

Publisher使用post发出一个Event事件,Subscriber在onEvent()函数中接收事件。

EventBus 负责存储订阅者、事件相关信息,订阅者和发布者都只和 EventBus 关联

订阅者首先调用 EventBus 的 register 接口订阅某种类型的事件,当发布者通过 post 接口发布该类型的事件时,EventBus 执行调用者的事件响应函数。

2.3 优势

  • 简化组件之间的通讯方式

  • 对通信双方进行解藕

  • 使用ThreadMode灵活切换工作线程

  • 速度快、性能好

  • 库比较小,不占内存

2.4 缺点

  • 使用的时候有定义很多event类

  • event在注册的时候会调用反射去遍历注册对象的方法在其中找出带有@subscriber标签的方法,性能不高。

  • 需要自己注册和反注册,如果忘了反注册就会导致内存泄漏

2.5 环境配置

dependencies {
    implementation("org.greenrobot:eventbus:3.2.0")
}

2.6 使用

需求:我们在同一个Activity中,实现消息的发送和接收

  • 在onStart中注册EventBus
@Override
protected void onStart() {
    super.onStart();
    //注册EventBus
    EventBus.getDefault().register(this);
}

在onDestory中将EventBus销毁

@Override
protected void onDestroy() {
    super.onDestroy();
    //注销EventBus
    EventBus.getDefault().unregister(this);
}

在onclick中发送消息

@Override
    public void onClick(View v) {
        EventMessage eventMessage = new EventMessage("这是一条消息");
        EventBus.getDefault().post(eventMessage);
        //将事件设置为sticky类型
//        EventBus.getDefault().postSticky(eventMessage);
    }

写一个方法使用@Subscribe注解,用于进行信息的接收和处理

//message接收处理
@Subscribe(threadMode = ThreadMode.POSTING, sticky = false, priority = 1)
public void onReceiveMessage(EventMessage eventMessage){
    TextView tv_eventbus = findViewById(R.id.tv_eventbus);
    tv_eventbus.setText(eventMessage.getMessage());
}

2.7 SubScribe注解

2.7.1 ThreadMode 模式

类型
POSTING: 默认模式 表示发送事件 post() 发生在哪个线程,接收事件 onReceiveMessage就发生在哪个线程环境中 适合更新 UI 的操作
MAIN / MAIN_ODERED: 主线程接收事件
表示无论事件在什么线程环境发布 post(),事件的接收总是在主线程环境执行。
MAIN:事件处理方法在主线程上执行,但不保证事件的顺序性,即事件处理方法的执行顺序可能不同于事件发布的顺序
MAIN_ODERED:事件处理方法在主线程上执行,并且保证事件的顺序性,即事件处理方法按照事件被发布的顺序依次执行
  • 使用 ThreadMode.MAIN 可以在 UI 线程中更新 UI,处理非严格顺序要求的事件
  • 使用 ThreadMode.MAIN_ORDERED 可以确保在需要处理严格按照事件顺序执行的场景下,例如处理连续的数据更新或状态变化
BACKGROUND
事件处理方法在后台线程中执行,避免阻塞主线程,但不保证是单一的后台线程
  • 当事件处理方法需要执行一些耗时的操作时,比如网络请求、数据库操作、文件读写等
  • 如果事件处理方法的结果不需要立即更新 UI
ASYNC 事件处理方法在一个单独后台线程中执行
  • 需要保证每个事件处理方法在独立的线程中执行,避免不同事件之间的影响
  • 需要并行处理多个事件的情况,每个事件处理方法可以并行执行而互不干扰

2.7.2 sticky黏性

sticky的作用就是:订阅者可以先不进行注册,如果post事件已经发出,再注册订阅者,同样可以接收到事件,并进行处理

2.7.3 priority

priority是优先级,是一个int类型,默认值为0。值越大,优先级越高,越优先接收到事件。值得注意的是,只有在post事件和事件接收处理,处于同一个线程环境的时候,才有意义

3. SmartRefreshLayout

SmartRefreshLayout是一个“聪明”或者“智能”的下拉刷新布局,由于它的“智能”,它不只是支持所有的View,还支持多层嵌套的视图结构。它继承自ViewGroup 而不是FrameLayout或LinearLayout,提高了性能。 也吸取了现在流行的各种刷新布局的优点,包括谷歌官方的 SwipeRefreshLayout,其他第三方的 Ultra-Pull-To-Refresh、TwinklingRefreshLayout 。还集成了各种炫酷的 Header 和 Footer

3.1 特点

  • 支持多种样式

    • SmartRefreshLayout 支持多种下拉刷新和上拉加载的样式,如经典、球脉冲、水波纹、贝塞尔曲线回弹等,可以根据项目需求选择合适的样式
  • 支持多种控件

    • 支持 RecyclerView、ListView、GridView、ScrollView、WebView 等 Android 原生控件,以及其它第三方库的集成
  • 自定义配置

    • 提供丰富的自定义配置选项,开发者可以通过配置参数调整刷新和加载的行为,如设置是否启用刷新、加载更多、是否支持自动刷新等
  • 越界回弹

    • 支持越界回弹效果,当下拉或上拉超出边界时,可以展示类似弹性效果的动画
  • 自动刷新和加载

    • 支持自动刷新和加载更多功能,可以通过代码自动触发刷新或加载操作,方便在特定场景下的使用
  • 高度定制化

    • 提供了丰富的接口和回调,开发者可以根据自己的需求实现更复杂的刷新和加载逻辑,例如自定义 Header 和 Footer
  • 性能优化

    • 优化了滑动性能,保证在滑动过程中不会因为刷新或加载操作而影响用户体验

3.2 用法

  • 引入依赖
implementation("com.github.bumptech.glide:glide:4.12.0")
implementation("io.github.scwang90:refresh-layout-kernel:2.0.5")      //核心必须依赖
implementation("io.github.scwang90:refresh-header-classics:2.0.5")    //经典刷新头
implementation("io.github.scwang90:refresh-footer-classics:2.0.5")    //经典加载
  • 在gradle.properties文件中添加
android.useAndroidX=true//这一行本身可能就有
android.enableJetifier=true

**android.useAndroidX=true:**这个配置项告诉 Gradle 使用 AndroidX 库而不是支持库(Support Library)。AndroidX 是一个重新设计的 Android 库,旨在提供更清晰、一致和可扩展的 API,并且它是未来 Android 应用开发的方向。许多现代的第三方库和组件(包括 SmartRefreshLayout)已经迁移到了 AndroidX,因此你需要确保你的项目也使用 AndroidX 来兼容这些库。

**android.enableJetifier=true:**Jetifier 是一个工具,它可以帮助你的项目在使用 AndroidX 之后,仍然能够与使用旧版支持库的依赖项兼容。它会自动修改你的依赖项,确保它们使用的是 AndroidX 版本的库而不是旧版支持库。

  • 在布局文件中添加SmartRefreshLayout
<com.scwang.smart.refresh.layout.SmartRefreshLayout
    android:id="@+id/smart_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.scwang.smart.refresh.header.ClassicsHeader
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:srlTextPulling="刷新数据"/>
    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <com.scwang.smart.refresh.footer.ClassicsFooter
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:srlTextPulling="加载数据"/>
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
  • 编写Activity文件
public class MainActivity extends AppCompatActivity implements OnRefreshListener, OnLoadMoreListener {
    private SmartRefreshLayout refreshLayout;
    private ListView listView;
    private ArrayAdapter<String> adapter;
    private List<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        init();
        refreshLayout = findViewById(R.id.smart_refresh_layout);
        
        listView = findViewById(R.id.listview);
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
        listView.setAdapter(adapter);
        
        refreshLayout.setOnRefreshListener(this);
        refreshLayout.setOnLoadMoreListener(this);
    }

    //初始化数据
    private void init(){
        list = new ArrayList<>();
        for (int i = 0; i < 20; i++){
            list.add("初始化数据" + i);
        }
    }
    
    //刷新数据
    @Override
    public void onRefresh(@NonNull RefreshLayout refreshLayout) {
        refreshData();
    }

    //加载更多数据
    @Override
    public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
        loadMoreData();
    }
    
    private void refreshData(){
        list.clear();
        for (int i = 0; i < 20; i++){
            list.add("重新加载的数据" + i);
        }
        adapter.notifyDataSetChanged();
        refreshLayout.finishRefresh();
    }

    private void loadMoreData(){
        int size = list.size();
        for (int i = size; i <size + 10; i++){
            list.add("新加载的数据" + i);
        }
        adapter.notifyDataSetChanged();
        refreshLayout.finishLoadMore();
    }
}

本系列【安卓基础重点知识】是刚开始学习android的时候记录的,其中部分内容来自网页,忘记记录来源了,如需添加引用,联系我即可