安卓接入通义千问AI的实现记录
官网:https://help.aliyun.com/zh/model-studio/use-qwen-by-calling-api#b1320a1664b9a
创建网络请求
- 创建一个BaseNetworkApi基类用于实现各种拦截器等。
abstract class BaseNetworkApi {fun <T> getApi(serviceClass: Class<T>, baseUrl: String): T {val retrofitBuilder = Retrofit.Builder().baseUrl(baseUrl).client(okHttpClient)return setRetrofitBuilder(retrofitBuilder).build().create(serviceClass)}/*** 实现重写父类的setHttpClientBuilder方法,* 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意操作*/abstract fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder/*** 实现重写父类的setRetrofitBuilder方法,* 在这里可以对Retrofit.Builder做任意操作,比如添加GSON解析器,Protocol*/abstract fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder/*** 配置http*/private val okHttpClient: OkHttpClientget() {var builder = RetrofitUrlManager.getInstance().with(OkHttpClient.Builder())builder = setHttpClientBuilder(builder)return builder.build()}
}
- 完成BaseNetworkApi的具体实现类。
val aiService : AiService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {AiApi.INSTANCE.getApi(AiService::class.java, AiService.getBaseUrl())
}class AiApi : BaseNetworkApi() {companion object {val INSTANCE: AiApi by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {AiApi()}}/*** 实现重写父类的setHttpClientBuilder方法,* 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意操作*/override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {builder.apply {//设置缓存配置 缓存最大10Mcache(Cache(File(appContext.cacheDir, "cxk_cache"), 10 * 1024 * 1024))//添加Cookies自动持久化cookieJar(cookieJar)addInterceptor(AiInterceptor())//超时时间 连接、读、写connectTimeout(10, TimeUnit.SECONDS)readTimeout(120, TimeUnit.SECONDS)writeTimeout(30, TimeUnit.SECONDS)}return builder}/*** 实现重写父类的setRetrofitBuilder方法,* 在这里可以对Retrofit.Builder做任意操作,比如添加GSON解析器,protobuf等*/override fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {return builder.apply {addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))}}val cookieJar: PersistentCookieJar by lazy {PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(appContext))}}
- 在AiInterceptor完成公共Header的添加。
class AiInterceptor : Interceptor {@Throws(IOException::class)override fun intercept(chain: Interceptor.Chain): Response {val builder = chain.request().newBuilder()builder.addHeader("Authorization", "Bearer sk-xxxxxxxxxxxxxxxxxxxx")builder.addHeader("Content-Type", "application/json").build()return chain.proceed(builder.build())}
}
此处的Key可在阿里云百炼自行申请。
- 通过第三方框架retrofit2完成网络请求注解。
interface AiService {companion object {fun getBaseUrl(): String {return "https://dashscope.aliyuncs.com"}}@POST("compatible-mode/v1/chat/completions")suspend fun askQuestion(@Body request: AiRequest): AiResponse<List<Choice>>
}@Keep
data class AiResponse<T>(val error: Error?, val choices: T) : BaseResponse<T>() {override fun isSucces() = error == nulloverride fun getResponseCode(): Int {return if (error == null) {200} else {400}}override fun getResponseData() = choicesoverride fun getResponseMsg(): String {return error?.message?: ""}
}data class Error(val message: String?, val type: String?, val param: String?, val code: String?)data class Message(var role: String?, var content: String?)data class Choice(var message: Message?, var finish_reason: String?, var index: Int?)data class AiRequest(var model: String?, var messages: List<Message>?, var stream :Boolean? = false)
@Keep
abstract class BaseResponse<T> {abstract fun isSucces(): Booleanabstract fun getResponseData(): Tabstract fun getResponseCode(): Intabstract fun getResponseMsg(): String}
- 在需要的地方直接调用即可,亦可编辑请求类对异常情况进行处理。
val message = Message("user", "你好")val messageList = ArrayList<Message>()messageList.add(message)val request1 = AiRequest("qwen-max", messageList)val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)scope.launch {val response = aiService.askQuestion(request1)Log.d("AI-test", response.toString())}
- 实现效果如下。
- 遇到的问题
● 在请求时,报java.net.SocketTimeoutException: timeout
遇到的 java.net.SocketTimeoutException: timeout 是网络请求超时问题,在调用阿里通义千问API时很常见。以下是建议方案:
① 增加超时时间配置
connectTimeout(10, TimeUnit.SECONDS)readTimeout(120, TimeUnit.SECONDS)writeTimeout(30, TimeUnit.SECONDS)
② 网络状态检查
③ 使用 qwen-turbo,响应速度最快