retrofit是载体(基于okhttp),rxjava 就是一个实现异步操作的库。 它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。
就比如我们开了一家披萨店:
你总不能直接把披萨胚直接卖给客人

RX思维分析(完成披萨订单)
被观察者(起点)-------需求(1个或者多个)---------观察者(终点)
就是和卖批萨一样,一条流水线,你开始是个被观察者(披萨胚),观察者(客人)点餐提出需求,通过中间做这些需求(放芝士啥的)最后一个需求结束以后把参数(披萨成品)传给观察者(客人)
RX=反应式 reactive x==反应式==响应式编程思维RX == 响应式编程思维 什么是响应式编程? 答:根据上一层的响应来影响下一层的变化 (起点〉点击按钮触发方法---->请求服务器---->服务器的响应.---->解析响应的json数据界面展示出来(终点)

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


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

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

4.编写MainActivity.java代码







效果图:

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

就只需要在map后面加需求就好了
先去自定义了一个方法,然后再调用它


效果图:

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)
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) 调用回调执行器,切换线程,处理返回结果;
添加依赖(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学习中,参考了很多大佬的讲解,如果有描述不对的地方请各位同学多多指正,希望我们可以在不断努力专注学习安卓中技术水平越来越厉害~
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用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
鉴于我有以下迁移: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
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我经常迷上ruby的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情
这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo
我想开始使用“Sinatra”框架进行编码,但我找不到该框架的“MVC”模式。是“MVC-Sinatra”模式或框架吗? 最佳答案 您可能想查看Padrino这是一个围绕Sinatra构建的框架,可为您的项目提供更“类似Rails”的感觉,但没有那么多隐藏的魔法。这是使用Sinatra可以做什么的一个很好的例子。虽然如果您需要开始使用这很好,但我个人建议您将它用作学习工具,以对您来说最有意义的方式使用Sinatra构建您自己的应用程序。写一些测试/期望,写一些代码,通过测试-重复:)至于ORM,你还应该结帐Sequel其中(imho
有没有一种方法可以自动生成种子数据文件并创建种子数据,就像您在下面链接中的Laravel中看到的那样?LaravelDatabaseMigrations&Seed我在另一个应用程序上看到在Rails的db文件夹下创建了一些带有时间戳的文件,其中包含种子数据。创建它的好方法是什么? 最佳答案 我建议你使用Fabrication的组合gem和Faker.Fabrication允许您编写一个模式来构建您的对象,而Faker为您提供虚假数据,如姓名、电子邮件、电话号码等。这是制造商的样子:Fabricator(:user)dousernam
我有一个交互式RubyonRails应用程序,我想在特定时间将其置于“只读模式”。这将允许用户读取他们需要的数据,但阻止他们执行写入数据库的操作。执行此操作的一种方法是在数据库中放置一个true/false变量,该变量在进行任何写入之前进行检查。我的问题。有没有更优雅的解决方案来解决这个问题? 最佳答案 如果你真的想阻止任何数据库写入,我能想到的最简单的方法是覆盖readonly?始终返回true的模型方法,无论是在选定模型中还是对于所有ActiveRecord模型。如果模型设置为只读(通常通过调用#readonly!来完成),任何