这里写目录标题
- [Harmony os Next------Retrofit网络库的使用和封装](#Harmony os Next——Retrofit网络库的使用和封装)
- 描述
- Retrofit的使用
Harmony os Next------Retrofit网络库的使用和封装
描述
Retrofit作为Android平台最为常用的一个第三方网络库,搭配Kotlin的协程,使用极为便捷。通过Harmony的工作人员的努力,制作出的Retrofit For Harmony Library依旧十分强大。
通过在oh-package.json5
文件中添加如下依赖即可完成装配,具体版本可以查看retrofit api
"@ohos/retrofit": "2.0.1-rc.0"
同时也可以通过添加httpclient
为其添加拦截器等
"@ohos/httpclient": "2.0.0-rc.9",
Retrofit的使用
BaseService
需要创建一个类继承BaseService
。同时可以在类名上方加一个@BasePath("/")
注解。
意味在BaseUrl
后面补全一个/
。因为在后面通过ServiceBuilder
创建AppService
实例时,需要指定BaseUrl
,例如BaseUrl
为https://api.xxxx.com
,在类上面添加注解@BasePath("/")
,便会自动在BaseUrl
后面加一个/
,后面的具体接口就不需要加/
,例如有一个获取天气的子接口api/weather
,最终拼凑的就是https://api.xxxx.com/api/weather
c
@BasePath("/")
class AppService extends BaseService{
}
部分功能使用
GET
其中NetworkConstants.GET_WEATHER
就是上述举例的api/weather
,在其前面使用GET
注解进行包裹,然后可以通过@Query
传递参数。最后返回的数据是通过异步的方式返回。Promise<Response<...>>
为固定返回参数,BaseModel<WeatherModel>
为api接口返回的JSON数据格式,可根据项目自行定义
c
@BasePath("/")
class AppService extends BaseService{
@GET(NetworkConstants.GET_WEATHER)
async getWeather(
@Query("cityId") cityId: string
): Promise<Response<BaseModel<WeatherModel>>> { return {} as Response<BaseModel<WeatherModel>> }
}
POST
跟GET
使用一致,通过POST
注解进行包裹,同样可以使用@Field
进行参数传递,其中 @FormUrlEncoded
注解是给此方法添加如下参数头
'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
c
@BasePath("/")
class AppService extends BaseService{
@FormUrlEncoded
@POST(NetworkConstants.TEST_API)
async postTestApi(
@Field("testId")testId : string
): Promise<Response<BaseModel<TestModel>>> { return {} as Response<BaseModel<TestModel>> }
}
创建拦截器
以打印日子拦截器为例,可以将每一个API请求的方法、参数、URL、返回结果等进行打印输出,便于测试。具体如下
c
class LoggingInterceptor implements Interceptor{
async intercept(chain: Chain): Promise<Response> {
try {
let request = chain.requestI()
let requestBody: RequestBody = request.body
let url = request.url as HttpUrl
const connectResponse = await chain.proceedI(chain.requestI())
let startMessage = `-->${request.method} ${url.url} ${connectResponse.protocol ?? ''}`
let contentType: string = requestBody.content
let endMessage = `--> END ${request.method}`
Logger.info(`Headers:${JSON.stringify(request.headers)}`)
Logger.info(startMessage)
Logger.info(contentType)
Logger.info(endMessage)
return connectResponse
} catch (error) {
return new Promise<Response>((resolve, reject) => {
let request = chain.requestI()
let response = chain.proceedI(request)
response.then((data) => {
resolve(data)
}).catch((err: Error) => {
reject(err)
});
})
}
}
}
创建实例
通过addInterceptor
便可以添加上述我们创建的日志打印拦截器
通过setEndpoint
可以设置BASEURL
通过在build
函数里面指定需要创建的BaseService
子类
c
let client: HttpClient = new HttpClient.Builder()
.setConnectTimeout(5, TimeUnit.SECONDS)
.setReadTimeout(5, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build()
export const appService = new ServiceBuilder()
.setEndpoint(NetworkConstants.BASEURL)
.setClient(client)
.build(AppService)
全局封装
通过创建一个全局范型方法baseApiRequest
,将api请求的异常进行集中处理,只对其成功返回的结果和错误进行返回,可以避免重复异常处理
c
export function baseApiRequest<T>(
apiCall: Promise<Response<T>>,
onSuccess: (result: T) => void,
onFailed: (error: ResourceStr) => void
) {
apiCall.then((result: Response<T>) => {
if (result.isSuccessful() && result.code() == HttpStatusCode.SUCCESS) {
onSuccess(result.result)
} else {
onFailed(result.message())
}
}).catch((error: Error) => {
if (error as IOException) {
//网络错误
onFailed($r('app.string.HH_Public_DisconnectNetwork'))
}else {
onFailed(error.message)
}
})
}
具体使用如下所示,就此我们就可以将一个网络请求接口简化到下面方法一样
c
baseApiRequest(
appService.getWeather(citeId),
(result) => {
if (result.code === HttpStatusCode.SUCCESS) {
//successful
} else {
//failed-error code
promptAction.showToast({ message: result.message })
}
},
(error) => {
//failed
Logger.info(`${error}`)
promptAction.showToast({ message: error })
}
)