ChatGpt告诉你该用Flow了!

388 阅读8分钟

前言

在听完上篇《协程中线程切换原理》的分享文章后领导提出要求下节讲Flow的时候要从实际使用出发分析传统写法、Rxjava写法、以及Flow他们的用法区别然后再讲Flow的优势。这段时间又逢ChatGpt大火,所以我忽然有了个想法,让chatGpt来帮我完成这篇分享文章吧。

我与chatGpt的问答

传统写法

我: Android环境下用okhttp写一个网络请求的方法,要求在子线程做网络请求,并将结果返回给主线程。

chatGpt: 以下是一个在 Android 环境下使用 OkHttp 发送网络请求并返回结果的示例代码。这个示例会在子线程中执行网络请求,并使用回调将结果返回给主线程。

首先,需要在 Android 项目中添加 OkHttp 的依赖库。可以在项目的 build.gradle 文件中添加以下依赖项:

implementation 'com.squareup.okhttp3:okhttp:4.9.1'

然后,在代码中使用 OkHttp 发送网络请求,并将结果通过回调返回给主线程:

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class OkHttpUtils {
    
    private static final String TAG = "OkHttpUtils";
    
    private static final OkHttpClient client = new OkHttpClient();
    private static final Handler handler = new Handler(Looper.getMainLooper());
    
    public interface CallbackListener {
        void onSuccess(String response);
        void onFailure(IOException e);
    }
    
    public static void sendGetRequest(String url, CallbackListener listener) {
        Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "onFailure: " + e.getMessage());
                handler.post(() -> listener.onFailure(e));
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    Log.d(TAG, "onResponse: " + responseBody);
                    handler.post(() -> listener.onSuccess(responseBody));
                } else {
                    Log.e(TAG, "onResponse: " + response.message());
                    handler.post(() -> listener.onFailure(new IOException(response.message())));
                }
            }
        });
    }
}

在这个示例代码中,我们定义了一个 CallbackListener 接口,用来接收网络请求结果的回调。在 sendGetRequest 方法中,我们构建了一个 Request 对象,并使用 client.newCall(request) 方法创建了一个 Call 对象。然后,我们使用 call.enqueue 方法发送异步网络请求,并在回调方法中处理请求结果。在 onResponse 方法中,我们将响应体中的内容转换为字符串,然后使用 handler.post 方法将结果返回给主线程。如果出现异常,则调用 onFailure 方法,并将异常信息传递给回调接口。

在使用这个示例代码时,可以调用 sendGetRequest 方法来发送网络请求,并传递一个 CallbackListener 接口实例作为回调参数。网络请求的结果会通过回调返回给主线程,从而可以在 UI 线程中更新界面。例如:

OkHttpUtils.sendGetRequest("http://example.com", new OkHttpUtils.CallbackListener() {
    @Override
    public void onSuccess(String response) {
        // 在主线程中处理请求成功的结果
        textView.setText(response);
    }
    @Override
    public void onFailure(IOException e) {
        // 在主线程中处理请求失败的结果
        e.printStackTrace();
        Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT).show();
    }
});

Rxjava写法

我: 用rxjava在实现一遍

chatGpt:

以下是使用 RxJava 实现网络请求并返回结果的示例代码。这个示例会在子线程中执行网络请求,并使用 RxJava 的观察者模式将结果返回给主线程。

首先,需要在 Android 项目中添加 RxJava 的依赖库。可以在项目的 build.gradle 文件中添加以下依赖项:

implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'

然后,在代码中使用 RxJava 发送网络请求,并将结果通过观察者模式返回给主线程:

import android.util.Log;

import java.io.IOException;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.core.Scheduler;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class RxJavaUtils {
    
    private static final String TAG = "RxJavaUtils";
    
    private static final OkHttpClient client = new OkHttpClient();
    
    public static Observable<String> sendGetRequest(String url) {
        return Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                Request request = new Request.Builder()
                        .url(url)
                        .build();
                Call call = client.newCall(request);
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        emitter.onError(e);
                    }
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if (response.isSuccessful()) {
                            String responseBody = response.body().string();
                            emitter.onNext(responseBody);
                        } else {
                            emitter.onError(new IOException(response.message()));
                        }
                        emitter.onComplete();
                    }
                });
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }
}

在这个示例代码中,我们使用 RxJava 的 Observable 类来创建一个可观察对象,用来发送异步网络请求并返回结果。我们在 create 方法中实现了网络请求的具体逻辑,在 subscribe 方法中触发网络请求,并将结果通过 ObservableEmitter 发射出去。我们使用 subscribeOn(Schedulers.io()) 指定网络请求在 IO 线程中执行,使用 observeOn(AndroidSchedulers.mainThread()) 指定结果在主线程中处理。

在使用这个示例代码时,可以调用 sendGetRequest 方法来发送网络请求,并使用 subscribe 方法订阅观察者对象,从而接收网络请求的结果。例如:

RxJavaUtils.sendGetRequest("http://example.com")
        .subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                // 在这里可以做一些准备工作,比如显示进度条等
            }
            @Override
            public void onNext(String response) {
                // 在主线程中处理请求成功的结果
                textView.setText(response);
            }
            @Override
            public void onError(Throwable e) {
                // 在主线程中处理请求失败的结果
                e.printStackTrace();
                Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT

Flow写法

我: 用Flow实现一遍

chatGpt: 以下是使用 Kotlin Flow 实现网络请求并返回结果的示例代码。这个示例会在子线程中执行网络请求,并使用 Flow 的流模式将结果返回给主线程。

首先,需要在 Android 项目中添加 Kotlin Flow 的依赖库。可以在项目的 build.gradle 文件中添加以下依赖项:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"

然后,在代码中使用 Flow 发送网络请求,并将结果通过流模式返回给主线程

import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.IOException

object FlowUtils {

    private const val TAG = "FlowUtils"

    private val client = OkHttpClient()

    fun sendGetRequest(url: String): Flow<String> = flow {
        val request = Request.Builder()
            .url(url)
            .build()
        val call = client.newCall(request)
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.e(TAG, "Request failed: ${e.message}")
                // 使用 Flow 的 emit 方法将异常抛出
                emit(e.message ?: "Unknown error")
            }

            override fun onResponse(call: Call, response: Response) {
                val responseBody = response.body()?.string()
                if (response.isSuccessful && responseBody != null) {
                    // 使用 Flow 的 emit 方法将请求成功的结果抛出
                    emit(responseBody)
                } else {
                    val errorMessage = response.message()
                    Log.e(TAG, "Request failed: $errorMessage")
                    // 使用 Flow 的 emit 方法将异常抛出
                    emit(errorMessage)
                }
            }
        })
    }.flowOn(Dispatchers.IO)
}

在这个示例代码中,我们使用 Kotlin 的 Flow 类来创建一个流对象,用来发送异步网络请求并返回结果。我们在 flow 方法中实现了网络请求的具体逻辑,并使用 emit 方法将结果抛出。我们使用 flowOn(Dispatchers.IO) 指定网络请求在 IO 线程中执行。

在使用这个示例代码时,可以调用 sendGetRequest 方法来发送网络请求,并使用 collect 方法订阅流对象,从而接收网络请求的结果。例如:

lifecycleScope.launch {
    FlowUtils.sendGetRequest("http://example.com")
        .collect { response ->
            // 在主线程中处理请求结果
            textView.text = response
        }
}

在这个示例代码中,我们使用 Kotlin 的协程来调用 Flow 对象。我们在 collect 方法中实现了处理网络请求结果的逻辑,这个方法会在主线程中执行。注意,我们使用 lifecycleScope.launch 方法来启动协程,这样可以确保协程在当前 Activity 或 Fragment 被销毁时自动取消。

三种写法的区别

我: 这三种写法有什么区别,有什么优缺点呢?

chatGpt: 三种写法都可以实现在 Android 环境下使用 OkHttp 发送网络请求,并在子线程中执行请求并返回结果给主线程。

使用 Callback 的写法优点是简单易懂,适用于对异步请求响应时间不敏感的场景,但缺点是需要手动管理线程,容易造成线程泄漏等问题。

使用 RxJava 的写法优点是可以很方便地进行线程切换和请求结果的转换,代码可读性也较好,但缺点是需要引入额外的依赖库,增加项目的复杂度。

使用 Flow 的写法优点是可以使用 Kotlin 语言特性来实现异步流处理,避免了回调地狱,同时也可以很方便地进行线程切换和请求结果的转换,但缺点是需要使用 Kotlin 协程,需要理解协程的概念和使用方法。另外,Flow 的稳定性还没有 RxJava 那么成熟,可能存在一些潜在的问题。

总体来说,选择哪种写法主要取决于项目的具体需求和团队的技术栈,以及对项目复杂度和代码可读性的要求。如果需要快速实现简单的网络请求,可以选择 Callback 的写法;如果项目已经使用了 RxJava,可以选择 RxJava 的写法;如果项目使用了 Kotlin,可以选择 Flow 的写法。

Flow对比RxJava的优势

我: 那Flow对比RxJava的写法有些优势呢?

chatGpt: 相比 RxJava,Flow 在以下方面具有优势:

  1. 更加轻量级:Flow 是 Kotlin 标准库的一部分,无需引入额外的依赖库,而 RxJava 需要引入 RxJava 核心库和相关的操作符库,增加了项目的复杂度和体积。
  2. 更加简单:Flow 基于 Kotlin 协程实现,使用协程的语法糖可以实现代码更加简单和易读,比 RxJava 更加简单。
  3. 更加灵活:Flow 支持背压处理和多个订阅者同时订阅同一个数据流,而 RxJava 对于多个订阅者需要使用 share()replay() 等操作符进行处理。
  4. 更加安全:Flow 可以在协程的上下文中运行,因此可以避免 RxJava 中常见的线程安全问题和内存泄漏问题。
  5. 更加可预测:Flow 使用 Kotlin 的类型安全特性,可以更加容易地避免类型不匹配和空指针异常等问题。

总体来说,Flow 是 Kotlin 标准库中推出的异步流处理库,相比 RxJava 更加轻量级、简单、灵活、安全和可预测。对于新项目或者已经使用 Kotlin 的项目,Flow 可以作为第一选择。

总结

第一次使用chatgpt的方式来完成分享文章,不得不赞叹AI科技的伟大。原本需要自己总结研究的知识在很短时间内就可以完成,效率提升杠杠的。

但是相对的,工具的提升带来的就是人的懒惰。不再深入源码深入知识点,很多细节问题很难掌握,得到的知识点最后也不深刻,容易遗忘。

当然,问题都是两面性的,看你如何取舍了