草庐IT

RXJava3+OKHTTP3+Retrofit2(观察者设计模式)讲解+实战

爱编程的小费同学 2024-03-20 原文

废话不多说,全部代码请拉到最后!!!!

retrofit是载体(基于okhttp),rxjava 就是一个实现异步操作的库。 它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。

就比如我们开了一家披萨店:

你总不能直接把披萨胚直接卖给客人

RX思维分析(完成披萨订单)

被观察者(起点)-------需求(1个或者多个)---------观察者(终点)

就是和卖批萨一样,一条流水线,你开始是个被观察者(披萨胚),观察者(客人)点餐提出需求,通过中间做这些需求(放芝士啥的)最后一个需求结束以后把参数(披萨成品)传给观察者(客人)

RX=反应式 reactive x==反应式==响应式编程思维RX == 响应式编程思维 什么是响应式编程? 答:根据上一层的响应来影响下一层的变化 (起点〉点击按钮触发方法---->请求服务器---->服务器的响应.---->解析响应的json数据界面展示出来(终点)

(1)项目实战环节:RXJava3+OKHTTP3

1.编写样式,并在按钮加一个自定义命名onclick事件

 

2.因为是加载网络图片,所以要manifest中开启网络权限

3.添加RXJava 3X依赖才能正常在mainActivity调用方法

4.编写MainActivity.java代码

 

效果图:

因为是异步,所以肯定不止一个需求,那么接下来我们再添加一个

就只需要在map后面加需求就好了

先去自定义了一个方法,然后再调用它

效果图:

(2)项目实战环节:Retrofit2+RXJava3+OKHTTP3结合( Retrofit本身并不具备网络请求的能力 ):

Retrofit是一个网络请求框架的封装,底层网络请求默认使用OkHttp,也就是说Retrofit本身并不具备网络请求的能力,只是简化了网络请求的相关参数配置或者说Retrofit只负责上层接口的封装。

使用Retrofit,实际上是使用Retrofit接口层封装请求参数、Header、Url 等信息,之后由OkHttp完成后续的请求操作,当服务端返回数据之后,OkHttp再将原始的结果交给Retrofit,然后Retrofit根据用户的需求,对结果进行解析。

1.导入retrofit依赖

2.创建接口,这里只是定义rxjava的observable方法,@Url注解后面传入图片链接,ResponseBody是抽象类,它实例化的那部分它封装进去了在源码里后续我们只需要直接在mainActivity直接调用,通过string()字符流的方式读取内容,bytes()字节流的方式读取内容我们还可以获得字节流,实现自己的定制化读取。会在mainActivity写。

在原有的基础上改一下,首先在mainActivity创建接口对象,进行实例化

下面我们可以看见map里面okhttp在结合rxjava的时候不写接口,进行请求图片的时候需要写很多行代码,各种调用方法赋值。很繁琐。。。

3.Retrofit实例化

4.创建网络请求接口对象实例

5.编写map需求(关于下载下来的文件流格式转换这一块)这个需求完成(加工完成)以后会将bitmap图片传给观察者(客人)里面的onNext()方法。如果要是有别的需求,会把bitmap图片传到下一个需求,只有把需求全部完成才会传给onNext()方法。

在onnext我们也可以除了展示做其他操作,这里就只写了一个展示功能,onnext执行完成以后如果成功跑完,就会执行complete方法,弹窗隐藏,如果期间我们点击了别的地方啥的导致无法正常跑完就会执行onError报错。

周期梳理:

 

效果图:

学习期间疑惑点解答:

参考文档:

1.Retrofit2的使用(接口数据请求关于注解用法)

Retrofit2 详解和使用(一)_锐湃的博客-CSDN博客_retrofit2使用详解^v63^js_top,201^v3^control_1,213^v2^t3_esquery_v1&utm_term=Streaming%E6%B3%A8%E8%A7%A3&spm=1018.2226.3001.4187

2.Retrofit2+RXJava3框架搭建

java 图片下载案例_Retrofit Rxjava实现图片下载、保存并展示实例_杨勃 (阿北)的博客-CSDN博客^v63^js_top,201^v3^control_1,213^v2^t3_esquery_v1&utm_term=retrofit2%E5%9B%BE%E7%89%87%E4%B8%8B%E8%BD%BD&spm=1018.2226.3001.4187

3.RXJava思想教学视频--OKHTTP3+RXJava3框架搭建(建议加依赖使用RXJava3)

1.RX思维_哔哩哔哩_bilibili

4.Observable使用

Angular7知识点5----Observable(可观察对象)_暖一杯茶的博客-CSDN博客

5.Retrofit讲解

网络请求(三)— Retrofit_橙子19911016的博客-CSDN博客_retrofit网络请求^v63^js_top,201^v3^control_1,213^v2^t3_esquery_v1&spm=1018.2226.3001.4187

6.RXJava执行顺序

RxJava在Android中的使用详解_AndyYuan317的博客-CSDN博客_rxjava android^v63^js_top,201^v3^control_1,213^v2^t3_esquery_v1&spm=1018.2226.3001.4187

Retrofit的基本使用流程:

1) 通过建造者模式构建一个Retrofit实例;

2) 通过Retrofit对象的create方法创建接口实例(动态代理);

3) 调用接口的方法解析注解,调用具体的网络请求方法;

4) 通过数据解析器解析数据;

5) 调用回调执行器,切换线程,处理返回结果;

完整代码(okhttp3+rxjava3+retrofit2结合)

添加依赖(Build.gradle)

    //RxJava的依赖包
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    //RxAndroid的依赖包
    implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
    /*retrofit2依赖包*/
    implementation 'com.squareup.retrofit2:retrofit:2.7.0'
    //配合Rxjava 使用的适配器
    implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
    //依赖和retrofit对应的gson解析类库,配置了就拥有了json解析的功能
    implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
    /*okhttp3依赖*/
    implementation 'com.squareup.okhttp3:okhttp:3.1.2'

添加网络请求权限(AndroidManifest.xml)

<!--开启网络权限-->
    <uses-permission android:name="android.permission.INTERNET"/>

创建接口DownloadService.java

package com.example.rxjavastudy1;
import io.reactivex.rxjava3.core.Observable;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
public interface DownloadService {
    @GET
    //GET下载文件必须结合@Streaming使用
    @Streaming
    /*ResponseBody是抽象类,它实例化的那部分它封装进去了在源码里
    后续我们只需要直接在mainActivity直接调用,通过string()字符流的方式读取内容,
    bytes()字节流的方式读取内容
    我们还可以获得字节流,实现自己的定制化读取。*/
    Observable<ResponseBody> download(@Url String fileUrl);
    //Observable属于RxJS库里面的一个对象,可以用来处理异步事件
}

样式ui编写(activityMain.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <!--按钮触发-->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="showImageAction"
        android:text="图片显示加载功能"
        tools:ignore="HardcodedText,UsingOnClickInXml" />
    <!--RXJava来显示我们的图片-->
    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

MainActivity.java编写

package com.example.rxjavastudy1;

import androidx.appcompat.app.AppCompatActivity;

import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import java.io.PrintWriter;
import java.io.StringWriter;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {


    /*ImageView控件,用来显示结果图像*/
    private ImageView image;

    /*创建接口对象*/
    DownloadService downloadService;

    /*创建弹窗对象*/
    ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        image = findViewById(R.id.image);

    }

    /*图片显示加载功能*/
    public void showImageAction(View view) {

        //1.创建Retrofit对象
        Retrofit retrofit = new Retrofit.Builder()
                // 设置 网络请求 Url
                .baseUrl("http://baidu.com/")
                /*添加rxjava适配*/
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                //设置使用Gson解析(记得加入依赖),定义转换器
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        /*2.接口实例化*/
        downloadService = retrofit.create(DownloadService.class);
        /*3.调用接口方法下载一张图片*/
        downloadService.download("https://c-ssl.duitang.com/uploads/blog/202208/11/20220811225937_06f91.jpg")
                /*给上面分配异步线程,实现图片下载操作*/
                .subscribeOn(Schedulers.io())
                /*2.进行需求转换*/
                /*需求1:进行图片格式转换*/
                /*尖括号第一个是传进来的参数格式,第二个是传出去的文件格式*/
                .map(new Function<ResponseBody, Bitmap>() {
                    @Override
                    public Bitmap apply(ResponseBody path) throws Exception {
                        /*Byte[]类型转为Inputstream流*/
                        Bitmap bitmap;
                        //解流
                        bitmap = BitmapFactory.decodeStream(path.byteStream());
                        //传给下个流或者onnext方法
                        return bitmap;
                    }
                })
                /*需求2:加水印*/
                /*尖括号第一个是传进来的参数格式,第二个是传出去的文件格式*/
                .map(new Function<Bitmap, Bitmap>() {
                    @Override
                    public Bitmap apply(Bitmap bitmap) throws Throwable {
                        //创建方法对象
                        Paint paint = new Paint();
                        //设置水印颜色
                        paint.setColor(Color.YELLOW);
                        //设置水印字体大小
                        paint.setTextSize(100);
                        //设置水印文本,位置信息
                        Bitmap shuiyinBitmap = drawTextToBitmap(bitmap, "你瞅啥", paint, 88, 88);
                        //传给下个流或者onnext方法
                        return shuiyinBitmap;

                    }
                })
                //在android主线程中展示
                .observeOn(AndroidSchedulers.mainThread())
                /*观察者*/
                .subscribe(new Observer<Bitmap>() {
                    /*1.订阅*/
                    /*订阅事件disposable*/
                    /*OnSubscribe 会被存储在返回的 Observable 对象中,它的作用相当于一个计划表,
                    当 Observable 被订阅的时候,OnSubscribe 的 call() 方法会自动被调用,事件序列就会依照设定依次触发*/
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        /*创建弹窗对象*/
                        progressDialog = new ProgressDialog(MainActivity.this);
                        /*设置弹窗信息*/
                        progressDialog.setTitle("RXJava 玩儿命加载中。。。");
                        progressDialog.show();
                    }

                    /*3.*/
                    @Override
                    public void onNext(@NonNull Bitmap pic) {
                        /*展示图片*/
                        image.setImageBitmap(pic);
                    }

                    /*如果出现报错就会执行这一条*/
                    @Override
                    public void onError(@NonNull Throwable e) {
                        StringWriter stringWriter = new StringWriter();
                        e.printStackTrace(new PrintWriter(stringWriter));
                        /*打印错误信息*/
                        Log.d("tag", stringWriter.toString());
                    }

                    /*4.整个链条全部结束*/
                    @Override
                    public void onComplete() {
                        /*隐藏加载框*/
                        progressDialog.dismiss();
                    }
                });

    }

    /*加水印后的bitmap方法*/
    private final Bitmap drawTextToBitmap(Bitmap bitmap, String text, Paint paint, int paddingLeft, int paddingTop) {
        Bitmap.Config bitmapConfig = bitmap.getConfig();
        paint.setDither(true);
        paint.setFilterBitmap(true);
        if (bitmapConfig == null) {
            bitmapConfig = Bitmap.Config.ARGB_8888;
        }
        bitmap = bitmap.copy(bitmapConfig, true);
        Canvas canvas = new Canvas(bitmap);

        canvas.drawText(text, paddingLeft, paddingTop, paint);
        return bitmap;
    }
}

小费同学也是Android学习中,参考了很多大佬的讲解,如果有描述不对的地方请各位同学多多指正,希望我们可以在不断努力专注学习安卓中技术水平越来越厉害~

有关RXJava3+OKHTTP3+Retrofit2(观察者设计模式)讲解+实战的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  5. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  6. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

  7. ruby - 在 Ruby 中查找多个正则表达式匹配的模式和位置 - 2

    这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo

  8. ruby - sinatra 框架的 MVC 模式 - 2

    我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho

  9. ruby-on-rails - Rails 如何创建数据模式种子数据 - 2

    有没有一种方法可以自动生成种子数据文件并创建种子数据,就像您在下面链接中的Laravel中看到的那样?LaravelDatabaseMigrations&Seed我在另一个应用程序上看到在Rails的db文件夹下创建了一些带有时间戳的文件,其中包含种子数据。创建它的好方法是什么? 最佳答案 我建议你使用Fabrication的组合gem和Faker.Fabrication允许您编写一个模式来构建您的对象,而Faker为您提供虚假数据,如姓名、电子邮件、电话号码等。这是制造商的样子:Fabricator(:user)dousernam

  10. ruby-on-rails - Ruby on Rails 应用程序的只读模式 - 2

    我有一个交互式RubyonRails应用程序,我想在特定时间将其置于“只读模式”。这将允许用户读取他们需要的数据,但阻止他们执行写入数据库的操作。执行此操作的一种方法是在数据库中放置一个true/false变量,该变量在进行任何写入之前进行检查。我的问题。有没有更优雅的解决方案来解决这个问题? 最佳答案 如果你真的想阻止任何数据库写入,我能想到的最简单的方法是覆盖readonly?始终返回true的模型方法,无论是在选定模型中还是对于所有ActiveRecord模型。如果模型设置为只读(通常通过调用#readonly!来完成),任何

随机推荐