文章目录

  • 1、简介
  • 2、Retrofit 配置与基本用法
    • 2.1 依赖引入与配置
    • 2.2 基本用法
  • 3、Retrofit 的注解
    • 3.1 请求方法注解
    • 3.2 请求头注解
    • 3.3 请求参数注解
    • 3.4 请求和响应格式(标记)注解
  • 4、Retrofit 注解的配合使用
    • 4.1 @GET 使用
    • 4.2 @GET、@Query 使用
    • 4.3 @GET、@QueryMap 使用
    • 4.4 @POST 使用
    • 4.5 @POST、@FormUrlEncoded、@File 使用
    • 4.6 @POST、@FormUrlEncoded、@FieldMap 使用
    • 4.7 @HTTP 使用
    • 4.8 @Path 使用
    • 4.9 @Url 使用
    • 4.10 @Header 使用
    • 4.11 @Headers 使用
    • 4.12 @Streaming 使用
    • 4.13 @Multipart、@part 使用
    • 4.14 @Multipart、@PartMap 使用
  • 5、Retrofit 源码解析
  • 6、Retrofit 的实现与原理
  • 6、总结

1、简介

  • Retrofit 是基于 OkHttp 进行的封装;
  • 从功能上说,Retrofit 完成了网络请求接口的封装,网络请求返回数据的解析和线程的自动切换,解决了 OkHttp 使用时存在的一些问题;
  • 从代码结构上说,Retrofit 提供了很多注解,在我们配置网络请求时,简化了代码,且解耦。

OKHttp 使用时存在的问题:

  1. 网络请求的接口配置繁琐,需要配置请求头、请求体和请求参数;
  2. 网络请求返回的 Response 数据需要手动解析,且不能够复用;
  3. 无法自动进行线程的切换。
  • Github

2、Retrofit 配置与基本用法

2.1 依赖引入与配置

// 模块的 build.gradle
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
// 基本属性数据转换器
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
//  Gson 数据转换器
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// Rxjava 适配器(可选)
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.2'
// OkHttp
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
// RxJava RxAndroid RxKotlin(可选)
implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0'
// Kotlin 协程(可选)
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'// 联网权限
<uses-permission android:name="android.permission.INTERNET" />// 由于安卓 9.0 禁止明文传输,所以需要网络安全配置
// 第一步:在【包名/app/src/main/res/xml/】目录下,新建 network_security_config.xml 文件,内容如下:
// 包名/app/src/main/res/xml/network_security_config.xml
// 更详细的可以查看:https://developer.android.com/training/articles/security-config?hl=zh-cn
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config cleartextTrafficPermitted="true"/><debug-overrides><trust-anchors><certificates src="system" overridePins="true"/><certificates src="user" overridePins="true"/></trust-anchors></debug-overrides>
</network-security-config>
// 第二步:在 AndroidManifest.xml 的 application 标签中添加:
android:networkSecurityConfig="@xml/network_security_config"//最后在模块的 build.gradle 中开启 java 1.8 支持,retrofit2 低版本可能不需要次支持,但是咱们用的是 2.9.0 版本,故需要支持该项
android {compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}
}

2.2 基本用法

// 1.定义请求接口
interface ApiService {@GET("/article/list/{page}/json")fun getArticleList1(@Path("page") page: Int): Call<String>@GET("/article/list/{page}/json")fun getArticleList2(@Path("page") page: Int): Flowable<ApiResponse<ArticleList>>@GET("/article/list/{page}/json")suspend fun getArticleList3(@Path("page") page: Int): ApiResponse<ArticleList>
}
// 2.构建 OkHttpClient 对象
val baseUrl: String = "http://wanandroid.com/"
// 设置拦截器
val httpLoggingInterceptor = HttpLoggingInterceptor() {Log.e("okhttp.OkHttpClient", it)
}
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val okHttpClient = OkHttpClient().newBuilder()// 设置连接超时为 10 秒.connectTimeout(10L, TimeUnit.SECONDS)// 设置文件读取超时为 60 秒.readTimeout(60L, TimeUnit.SECONDS)// 设置用于读取和写入缓存响应的响应缓存为 10M.cache(Cache(cacheDir, 10240 * 1024))// 设置 http 日志拦截器// 使用 addInterceptor() 也可以,即为第一层自定义拦截器// 使用 addNetworkInterceptor() 也可,即为第六层非网页网络拦截拦截器.addInterceptor(httpLoggingInterceptor).build()
// 3.构建 Retrofit 对象
val retrofit: Retrofit = Retrofit.Builder().baseUrl(baseUrl).client(okHttpClient).addConverterFactory(ScalarsConverterFactory.create())// 基本属性转换器.addConverterFactory(GsonConverterFactory.create())// Gson 数据转换器.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())// RxJava 适配器.build()
// 4.通过动态代理获取到所定义的接口
val apiService = retrofit.create(ApiService::class.java)
// 5.同步请求
Thread(Runnable {try {val response: Response<String> = apiService.getAritrilList1(0).execute()val body = response.body()Log.d("response", "onResponse: $body")} catch (e: Exception) {e.printStackTrace()Log.d("response", "onFailure: ${e.message}")}
}).start()
// 5.异步请求(Retrofit 成功或者失败的回调都自动切换到 UI 线程)
apiService.getArticleList2(0).enqueue(object : Callback<ApiResponse<ArticleList>> {/*** 请求成功的回调方法** @param call Call<ApiResponse<ArticleList>>* @param response Response<ApiResponse<ArticleList>>*/override fun onResponse(call: Call<ApiResponse<ArticleList>>,response: Response<ApiResponse<ArticleList>>) {Log.d("response", "onResponse: ${response.body()?.requireData}")}/*** 请求失败的回调方法** @param call Call<ApiResponse<ArticleList>>* @param t Throwable*/override fun onFailure(call: Call<ApiResponse<ArticleList>>, t: Throwable) {Log.d("response", "onFailure: ${t.message}")}
})
// 5.OkHttp + Retrofit + RxJava 组合的异步 GET 请求
apiService.getArticleList3(0).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe({Log.d("response", "onResponse: ${it.requireData}")}, {it.printStackTrace()Log.d("response", "onFailure: ${it.message}")})
// 5.OkHttp + Retrofit + Coroutines
val job: Job = GlobalScope.launch {try {val apiResponse = apiService.getArticleList4(0)withContext(Dispatchers.Main) {Log.d("response", "onResponse: ${apiResponse.requireData}")}} catch (e: Exception) {e.printStackTrace()withContext(Dispatchers.Main) {Log.d("response", "onFailure: ${e.message}")}}
}

3、Retrofit 的注解

3.1 请求方法注解

请求方法注解 说明
@GET get 请求
@POST post 请求
@PUT put 请求
@DELETE delete 请求
@PATCH patch 请求,该请求是对 put 请求的补充,用于更新局部资源
@HEAD head 请求
@OPTIONS options 请求
@HTTP 通过注解,可以替换以上所有的注解,它拥有三个属性:method、path、hasBody

3.2 请求头注解

请求头注解 说明
@Headers 用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在
@Header 作为方法的参数传入,用于添加不固定的 header,它会更新已有请求头

3.3 请求参数注解

请求参数注解 说明
@Body 多用于 Post 请求发送非表达数据,根据转换方式将实例对象转化为对应字符串传递参数,比如使用 Post 发送 Json 数据,添加 GsonConverterFactory 则是将 body 转化为 json 字符串进行传递
@Filed 多用于 Post 方式传递参数,需要结合 @FromUrlEncoded 使用,即以表单的形式传递参数
@FiledMap 多用于 Post 请求中的表单字段,需要结合 @FromUrlEncoded 使用
@Part 用于表单字段,Part 和 PartMap 与 @multipart 注解结合使用,适合文件上传的情况
@PartMap 用于表单字段,默认接受类型是 Map<String,RequestBody>,可用于实现多文件上传
@Path 用于 Url 中的占位符
@Query 用于 Get 请求中的参数
@QueryMap 与 Query 类似,用于不确定表单参数,相当于多个 Query 参数
@Url 指定请求路径

3.4 请求和响应格式(标记)注解

请求和响应格式(标记) 说明
@FromUrlCoded 表示请求发送编码表单数据,每个键值对需要使用 @Filed 注解
@Multipart 表示请求发送 form_encoded 数据(使用于有文件上传的场景),每个键值对需要用 @Part 来注解键名,随后的对象需要提供值
@Streaming 表示响应用字节流的形式返回,如果没有使用注解,默认会把数据全部载入到内存中,该注解在下载大文件时特别有用

4、Retrofit 注解的配合使用

4.1 @GET 使用

  • @GET:请求方法注解,get 请求,括号内的是请求地址,Url 的一部分。
interface ApiService {// https://api.github.com/user@GET("/user")fun getData1(): Call<ResponseBody>
}

4.2 @GET、@Query 使用

  • @Query:请求参数注解,用于 Get 请求中的参数。
interface ApiService {// https://api.github.com/user?id=10006&name=刘亦菲@GET("/user")fun getData2(@Query("id") id: Long, @Query("name") name: String): Call<ResponseBody>
}

4.3 @GET、@QueryMap 使用

  • @QueryMap:请求参数注解,与 @Query 类似,用于不确定表单参数,通过 Map 将不确定的参数传入,相当于多个 Query 参数。
interface ApiService {// https://api.github.com/user?id=10006&name=刘亦菲@GET("/user")fun getData3(@QueryMap map: Map<String, Any>): Call<ResponseBody>
}val map = mutableMapOf<String, Any>()
map["id"] = 10006
map["name"] = "刘亦菲"
val call: Call<ResponseBody> = retrofit.create(ApiService.class).getData3(map)

4.4 @POST 使用

  • @POST:请求方法注解,post 请求,括号内的是请求地址,Url 的一部分。
interface ApiService {// https://api.github.com/user@POST("/user/emails")fun getData4(): Call<ResponseBody>
}

4.5 @POST、@FormUrlEncoded、@File 使用

  • @FormUrlEncoded:请求格式注解,请求实体是一个 From 表单,每个键值对需要使用 @Field 注解。
  • @File:请求参数注解,提交请求的表单字段,必须要添加,而且需要配合 @FormUrlEncoded 使用。
interface ApiService{// https://api.github.com/user/emails@FormUrlEncoded@POST("/user/emails")fun getData5(@Field("name") name: String, @Field("sex") sex: String): Call<ResponseBody>
}

4.6 @POST、@FormUrlEncoded、@FieldMap 使用

  • @FieldMap:请求参数注解,与 @Field 作用一致,用于不确定表单参数,通过 Map 将不确定的参数传入,相当于多个 Field 参数。
interface ApiService{// https://api.github.com/user/emails@FormUrlEncoded@POST("/user/emails")fun getData6(@FieldMap map: Map<String, Any>): Call<ResponseBody>
}val map = mutableMapOf<String, Any>()
map["id"] = 10006
map["name"] = "刘亦菲"
val call: Call<ResponseBody> = retrofit.create(ApiService.class).getData6(map)

4.7 @HTTP 使用

  • @HTTP:替换 @GET、@POST、@PUT、@DELETE、@HEAD 以及更多拓展功能。
  • method:表示请求的方法,区分大小写,这里的值 retrofit 不会再做任何处理,必须要保证正确。
  • path:网络请求地址路径。
  • hasBody:是否有请求体,boolean 类型。
interface ApiService{// https://api.github.com/user/keys@HTTP(method = "GET", path = "/user/keys", hasBody = false)fun getData7(): Call<ResponseBody>
}

4.8 @Path 使用

  • @Path:请求参数注解,用于 Url 中的占位符 {},所有在网址中的参数。
interface ApiService{// 输入 id=111,输出 https://api.github.com/orgs/111@GET("/orgs/{id}")fun getData8(@Query("name") name: String, @Path("id") id: Long): Call<ResponseBody>
}

4.9 @Url 使用

  • @Url:表示指定请求路径,可以当做参数传入,如果有 @Url 注解时,GET 传入的 Url 可以省略。
interface ApiService{// 输入 url="/orgs/{id}",id=111,输出 https://api.github.com/orgs/111@GET("/user/emails")fun getData9(@Url url: String, @Query("id") id: Long): Call<ResponseBody>
}

4.10 @Header 使用

  • @Header:用于添加不固定的请求头,作用于方法的参数,作为方法的参数传入,该注解会更新已有的请求头。
interface ApiService{// 输出 https://api.github.com/user/emails@GET("/user/emails")fun getData10(@Header("token") token: String): Call<ResponseBody>
}

4.11 @Headers 使用

  • @Header:请求头注解,用于添加不固定请求头。
interface ApiService{// 输出 https://api.github.com/user/emails@Headers({"phone-type:android", "version:1.1.1"})@GET("/user/emails")fun getData11(): Call<ResponseBody>
}

4.12 @Streaming 使用

  • @Streaming:表示响应体的数据用流的方式返回,使用于返回数据比较大,该注解在下载大文件时特别有用。
interface ApiService{// 输出 https://api.github.com/gists/public@Streaming@POST("/gists/public")fun getData12(): Call<ResponseBody>
}

4.13 @Multipart、@part 使用

  • @Multipart:表示请求实体是一个支持文件上传的表单,需要配合 @Part 和 @PartMap 使用,适用于文件上传。
  • @part:用于表单字段,适用于文件上传的情况,@Part 支持三种类型:RequestBody、MultipartBody.Part、任意类型。
interface ApiService{// 输出 https://api.github.com/user/followers@Multipart@POST("/user/followers")fun getData13(@Part("name") name: RequestBody, @Part file: MultipartBody.Part): Call<ResponseBody>
}// 声明类型,这里是文字类型
val textType: MediaType? = "text/plain".toMediaTypeOrNull()
// 根据声明的类型创建 RequestBody,就是转化为 RequestBody 对象
val name = RequestBody.create(textType, "这里是你需要写入的文本:刘亦菲")
// 创建文件,这里演示图片上传
val file = File("文件路径")
if (!file.exists()) {file.mkdir()
}
// 将文件转化为 RequestBody 对象
// 需要在表单中进行文件上传时,就需要使用该格式:multipart/form-data
val imgBody: RequestBody = RequestBody.create("image/png".toMediaTypeOrNull(), file)
// 将文件转化为 MultipartBody.Part
// 第一个参数:上传文件的 key;第二个参数:文件名;第三个参数:RequestBody 对象
val filePart = createFormData("file", file.getName(), imgBody)
val partDataCall: Call<ResponseBody> =retrofit.create(ApiService::class.java).getData13(name, filePart)

4.14 @Multipart、@PartMap 使用

  • @PartMap:用于多文件上传, 与 @FieldMap 和 @QueryMap 的使用类似。
interface ApiService{// 输出 https://api.github.com/user/followers@Multipart@POST("/user/followers")fun getData14(@PartMap map: Map<String, MultipartBody.Part>): Call<ResponseBody>
}val file1 = File("文件路径")
val file2 = File("文件路径")
if (!file1.exists()) {file1.mkdir()
}
if (!file2.exists()) {file2.mkdir()
}
val requestBody1 = RequestBody.create("image/png".toMediaTypeOrNull(), file1);
val requestBody2 = RequestBody.create("image/png".toMediaTypeOrNull(), file2);
val filePart1 = MultipartBody.Part.createFormData("file1", file1.name, requestBody1);
val filePart2 = MultipartBody.Part.createFormData("file2", file2.name, requestBody2);
val mapPart = mutableMapOf<String, MultipartBody.Part>()
mapPart["file1"] = filePart1
mapPart["file2"] = filePart2
val partMapDataCall: Call<ResponseBody> =retrofit.create(ApiService.class).getData14(mapPart)

5、Retrofit 源码解析

6、Retrofit 的实现与原理

  • Retrofit 采用动态代理创建实现了 Service 接口的代理对象。当我们调用 Service 的方法时候会执行 InvocationHandler.invoke() 方法。在 invoke() 方法中,会调用 loadServiceMethod() 方法;
  • 在 loadServiceMethod() 方法内部,如果缓存中已有 method,直接返回对应的 ServiceMethod 对象,否则会通过大量的反射对方法注解进行解析,生成对应的 ServiceMethod 对象并返回;
  • 这里返回的 ServiceMethod 实际上是 SuspendForBody 对象,SuspendForBody 继承于 HttpServiceMethod,HttpServiceMethod 继承于 ServiceMethod,接着调用 ServiceMethod.invoke() 方法,会走到 HttpServiceMethod.invoke() 方法,在 invoke() 方法中,创建 OkHttpCall 对象,并进一步封装成 ExecutorCallbackCall 对象(默认),实际发起网络请求的就是 OkHttpCall 对象;
  • OkHttpCall 发起网络请求,通过 ConverterFactory 将网络请求返回的 Response 数据解析成 Java 对象,并通过 MainThreadExecutor 将回调转发至主线程。

6、总结

  • Retrofit 采用动态代理创建实现了 Service 接口的代理对象,当我们调用 Api 接口实例中的方法时,这些方法的处理逻辑都会通过动态代理 Proxy.newProxyInstance 转发给 invoke() 方法,在 invoke() 方法中会通过 loadServiceMethod() 方法注解的所有参数解析进 ServiceMethod 中,然后将 ServiceMethod 传入 OkhttpCall 中去,接着将 OkhttpCall 传给 ServiceMethod 中的 CallAdapter 的 adapt() 方法,把 OkhttpCall 适配成不同平台的网络请求执行器,就可以得到不同平台的网络请求执行器。在 Android 平台中,CallAdapter 实例的 adapt() 方法会把 OkhttpCall 适配成 ExecutorCallbackCall,当我们发起网络请求时,ExecutorCallbackCall 就会委托 OkhttpCall 发起网络请求,OkhttpCall 内部就是用得 Okhttp 的同步或者异步网络请求,当网络请求数据返回时,ExecutorCallbackCall 就会通过 MainThreadExecutor 把线程切换主线程执行回调,这里的回调其实就是将原本的 Okhttp 的回调对接到 Retrofit 本身的成功或者失败会调用。
  • 以异步为例,Retrofit 通过 ExecutorCallbackCall 的 enqueue() 方法发起网络请求,最终会通过 OkhttpCall 的 enqueue() 方法来发起网络请求,OkhttpCall 的 enqueue() 方法中,首先会调用创建一个来自 Okhttp的Call 实例,然后通过这个 Okhttp 的 Call 实例的 enqueue() 方法来发起异步请求,当网络结果 Okhttp 的 Response 返回时,调用 parseResponse 方法解析 Response,parseResponse 方法里面还会调用 ServiceMethod 的 toResponse() 方法通过 Converter 实例的 convert() 方法把 ResponseBody 转化成我们想要的数据,不同的数据转化有不同的实现,在 Retrofit 的默认实现中,它就是直接返回 Okhttp 的 ResponseBody,最后把这个转化后的 body 和原始的 Okhttp 的 Response 一并封装成 Retrofit 的 Response 返回,最后把 parseResponse() 方法返回的 Response 通过 callback 回调出去,这时 ExecutorCallbackCall 收到回调,通过线程切换执行器 callbackExecutor,切换到主线程执行 callback 回调,一次异步请求就完成了,同步请求也是大同小异,只是少了个回调。

Retrofit 源码解析(2.9.0 版本)相关推荐

  1. Retrofit源码解析

    本文分析的源码基于retrofit:2.9.0. 前言 Retrofit基于OkHttp,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装,它们的关系示意图 ...

  2. android网络框架retrofit源码解析二

    注:源码解析文章参考了该博客:http://www.2cto.com/kf/201405/305248.html 前一篇文章讲解了retrofit的annotation,既然定义了,那么就应该有解析的 ...

  3. Android 网络框架之Retrofit源码解析,flutter边框特效

    Retrofit的构建使用了建造者模式,这个模式的优点就是可以构造复杂的对象,方便扩展,并且看起来代码比较简洁,美观: 在开始之前,我们先来看一下Retrofit的成员变量: 这里的变量并不是很多,我 ...

  4. android 日历源码解析,Android 4.0日历(calendar)源码分析之概览

    Calendar 从4.0开始,谷歌android系统有了脱碳换骨的改变,相应的日历应用的代码架构也跟2.*完全不同.代码更规范,当然也更复杂,且涉及到了android开发的方方面面. 如果你熟悉了i ...

  5. TreeMap源码解析

    1.TreeMap介绍 TreeMap是一个通过红黑树实现有序的key-value集合. TreeMap继承AbstractMap,也即实现了Map,它是一个Map集合 TreeMap实现了Navig ...

  6. Spark ALS recommendForAll源码解析实战之Spark1.x vs Spark2.x

    文章目录 Spark ALS recommendForAll源码解析实战 1. 软件版本: 2. 本文要解决的问题 3. 源码分析实战 3.1 Spark2.2.2 ALS recommendForA ...

  7. Spring 注解面面通 之 @CrossOrigin 注册处理方法源码解析

      参照<Spring 注解面面通 之 @RequestMapping 注册处理方法源码解析>,其讲解了@RequestMapping注释的处理方法注册过程,而@CrossOrigin是基 ...

  8. Spring 注解面面通 之 @CrossOrigin 处理请求源码解析

      @CrossOrigin源码解析主要分为两个阶段:   ① @CrossOrigin注释的方法扫描注册.   ② 请求匹配@CrossOrigin注释的方法.   本文针对第②阶段从源码角度进行解 ...

  9. Apache IoTDB源码解析(0.11.2版本):Session的源码解析

    1. 声明 当前内容主要为解析Apache IoTDB 0.11.2版本的Session的源码解析 通过前面的Apache Thrift的Demo,可以发现iotdb中的server是使用了thrif ...

最新文章

  1. MOS管安全工作区SOA
  2. 在ABAP中将负号提前的方法
  3. python爬虫什么书好_初学python爬虫看什么书
  4. (Head First 设计模式)学习笔记(1)
  5. 写给新入行程序员的10条建议
  6. PHP将Excel另存为html页面
  7. 音乐播放器App界面优秀案例,通过案例看大咖如何设计?
  8. sleep() wait() yield() join()
  9. 持续集成并不能消除 Bug,而是让它们非常容易发现和改正(转)
  10. Mac安装Gradle eclipse安装buildship插件
  11. JavaScript函数的返回值
  12. gentoo 修改键盘映射
  13. python提取文件指定列_python 提取文件指定列的方法示例
  14. 智课雅思词汇---六、fer是什么意思
  15. [受限玻尔兹曼机] 原理、求解过程推导、深度信念网络
  16. stack smashing detected(c++报错)
  17. Windows Server 2003 AGP纹理加速无法打开问题可行的解决方案
  18. Cents7 查看当前版本
  19. 加州大学欧文分校计算机排名,美国加州大学伯克利分校计算机专业排名
  20. 谈谈企业信息化 一种比较简单、灵活的产品物料多单位实现方案

热门文章

  1. 2020研电赛开始,Xilinx万元大奖等你来!看AI+MPsoc平台如何选?
  2. Kubernetes学习总结(3)——一年时间打造全球最大规模之一的Kubernetes集群,蚂蚁金服怎么做到的?
  3. 演示类中:类属性和实例属性的定义使用和区别
  4. 编译opencv出现Could NOT find PythonLibs: Found unsuitable version “3.6.9“, but required is exact version
  5. java/php/net/pythonMES大学生综合测评系统的设计与实现设计
  6. LS1021A 开发环境搭建
  7. 光影魔术手-批量-修改图片大小-小于1000k
  8. GitHub Desktop + 码云,基友搭配体验翻倍!
  9. 置信度和置信区间的算法实现
  10. 让您自信出版论文:iThenticate抄袭检测完全指南