Android上發(fā)送HTTP請(qǐng)求,一般有兩個(gè)方式:HttpURLConnection和HttpClient。HttpClient由于存在API數(shù)量多,擴(kuò)展困難缺點(diǎn),Android6.0開始HttpClient的功能完全移除,被廢棄。
Android 9.0開始,應(yīng)用程序默認(rèn)值允許使用HttpS類型的網(wǎng)絡(luò)請(qǐng)求。如果測(cè)試想請(qǐng)求Http的請(qǐng)求,需要配置network-security-config
進(jìn)行網(wǎng)絡(luò)請(qǐng)求,請(qǐng)先配置權(quán)限:<uses-permission android:name="android.permission.INTERNET" />
1.純HttpURLConnection進(jìn)行數(shù)據(jù)請(qǐng)求
private fun doRequestWithHttpURLConnection(){
thread {
var connection:HttpURLConnection?=null
val response = StringBuilder()
val url = URL("https://www.baidu.com/")
connection = url.openConnection() as HttpURLConnection
connection.connectTimeout = 8000
connection.readTimeout = 8000
val inputStream = connection.inputStream
val reader = BufferedReader(InputStreamReader(inputStream))
reader.use {
reader.forEachLine {
response.append(it)
}
}
showResponse( response.toString())
}
}
private fun showResponse(content: String) {
runOnUiThread {
responseText.text = content
}
}
注意:網(wǎng)絡(luò)請(qǐng)求需要放置在子線程,刷新UI需要在UI線程
2.使用OKHttp
在開源盛行的今天,網(wǎng)絡(luò)方面有很多開源的框架,其中OkHttp是比較優(yōu)秀的。
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
使用OkHttp需要添加這個(gè)依賴,添加之后會(huì)自動(dòng)加載兩個(gè)庫一個(gè)是OkHttp庫,一個(gè)是Okio庫,后者是前者通信的基礎(chǔ)。
fun sendOkHttpRequest(address:String,callback:Callback){
val client=OkHttpClient()
val request=Request.Builder()
.url(address)
.build()
client.newCall(request).enqueue(callback)
}
//在activity調(diào)用
sendOkHttpRequest("https://www.baidu.com/", object :Callback {
override fun onFailure(call: Call, e: IOException) {
TODO("Not yet implemented")
}
override fun onResponse(call: Call, response: Response) {
val string = response.body?.string()
showResponse(string)
}
})
3.Retrofit 庫的使用
retrofit庫,是在okHttp的基礎(chǔ)上繼續(xù)封裝的庫,是目前最好的網(wǎng)絡(luò)框架。OkHttp側(cè)重在底層通信的實(shí)現(xiàn),Retrofit側(cè)重是上層接口的封裝。
常用的請(qǐng)求注解:
@GET :請(qǐng)求服務(wù)器啥的數(shù)據(jù)
@POST:用于向服務(wù)器提交數(shù)據(jù)
@PUT:修復(fù)服務(wù)器上的數(shù)據(jù)
@DELETE:刪除服務(wù)器上的數(shù)據(jù)
object RetrofitCreator {
private const val BASE_URL="https://vsapi.meishesdk.com"
private val retrofit=Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build()
fun <T> create(retrofitClass:Class<T>):T= retrofit.create(retrofitClass)
inline fun <reified T> create(): T = create(T::class.java)
}
定義一個(gè)單例的Retrofit的創(chuàng)建類
//get 請(qǐng)求傳參數(shù)
@GET("/api/authorization/ST/current")
fun getSTLicFileInfo(@Query("appId") appId:String): Call<LicenseInfo>
定義一個(gè)接口,通過@Query來傳遞參數(shù),返回一個(gè)對(duì)象。
val appService = RetrofitCreator.create<AppService>()
appService.getSTLicFileInfo(App.getAppId()).enqueue(object : Callback<LicenseInfo>{
override fun onResponse(call: Call<LicenseInfo>, response: Response<LicenseInfo>) {
Log.d("test","onResponse")
}
override fun onFailure(call: Call<LicenseInfo>, t: Throwable) {
Log.d("test","onFailure")
}
})
使用的時(shí)候通過上面的方式就行調(diào)用
//get請(qǐng)求,通過path 來實(shí)現(xiàn)分頁接口
@GET("{page}/data.json")
fun getData(@Path("page") page:Int):Call<List<LicenseInfo>>
通過使用Path來定義分頁請(qǐng)求的接口
@DELETE("data/{id}")
fun deleteLic(@Path("id") id:String):Call<ResponseBody>
刪除一條數(shù)據(jù),返回值是ResponseBody,表示客戶端對(duì)于服務(wù)器響應(yīng)的數(shù)據(jù)不關(guān)心,也不解析。
@POST("data/create")
fun createData(@Body data:Data):Call<ResponseBody>
post 請(qǐng)求通過@Body來提交Data類型的數(shù)據(jù)
//靜態(tài)傳Header
@Headers("User-Agent:okhttp","Cache-Control:max-age=0")
@GET("data.json")
fun getData():Call<Data>
//動(dòng)態(tài)傳Header
@GET("data.json")
fun getData( @Header("Cache-Control") cacheControl:String):Call<Data>
帶著Header來請(qǐng)求,這個(gè)形式是靜態(tài)的傳遞Header,如果希望動(dòng)態(tài)傳遞Header需要用到
4.Retrofit實(shí)現(xiàn)下載文件
//動(dòng)態(tài)url實(shí)現(xiàn)下載 用于小文件下載
@GET
fun downloadFile(@Url url:String):Call<ResponseBody>
通過@Url來實(shí)現(xiàn)動(dòng)態(tài)url來下載文件,由于全部寫入內(nèi)內(nèi)存,所以不能下載大文件,否則會(huì)OOM
val create = RetrofitCreator.create<AppService>()
val downloadFile = create.downloadFile("http://196.168.1.200/text.txt")
downloadFile.enqueue(object : retrofit2.Callback<ResponseBody>{
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful){
val writtenToDisk: Boolean = writeResponseBodyToDisk(response.body())
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
TODO("Not yet implemented")
}
})
//將數(shù)據(jù)寫入文件
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
// todo change the file location/name according to your needs
File futureStudioIconFile = new File(getExternalFilesDir(null) + File.separator + "Icon.png");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
如果是大文件下載
@Streaming
@GET
fun downloadBigFile(@Url url:String):Call<ResponseBody>
下載大文件只需要添加@Streaming的方式來實(shí)現(xiàn)大文件下載