I'm using PRDownloader. I want to download a file from my Google Drive, hence I need to supply Authorization: Bearer <ACCESS_TOKEN> .. how can I do that with PRDownloader? I don't see any way to do this from the website, but perhaps there's some unofficial way?
I stumbled upon the same problem and ended up using Fetch instead. A working example looks then like this. The important line can be found in the download method:
request.addHeader("Authorization", "Bearer $accessToken")
Here is the complete DummyActivity:
class MainActivity : AppCompatActivity() {
private lateinit var fetch: Fetch
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Timber.plant(Timber.DebugTree())
val okHttpClient = OkHttpClient.Builder().build()
val fetchConfiguration =
FetchConfiguration.Builder(this)
.setDownloadConcurrentLimit(10)
.setProgressReportingInterval(100)
.setHttpDownloader(OkHttpDownloader(okHttpClient))
.build()
fetch = getInstance(fetchConfiguration)
button.setOnClickListener { download() }
}
private fun download() {
val url = "https://yourdomain.com/download/path.zip"
val dirPath = getExternalFilesDir(null)?.path
val fileName = "ResultFileName.zip"
val accessToken = "your_access_token"
val request = Request(url, Uri.fromFile(File(dirPath, fileName)))
request.priority = Priority.HIGH
request.networkType = NetworkType.ALL
request.addHeader("Authorization", "Bearer $accessToken")
fetch.enqueue(
request,
Func { updatedRequest -> Timber.d("request updated: ${updatedRequest.id})") },
Func { error: Error? -> Timber.d("Error: ${error?.name}")}
)
}
}
For anyone else stumbling into this. You can try:
PRDownloader.download(url, dir.absolutePath, filename)
.setHeader("accept-language", headers.getString("accept-language"))
.setHeader("Authorization", headers.getString("Authorization"))
.build()
Related
I'm trying to understand Kotlin couroutine. So here's my code (based on this tutorial). To keep the code relatively simple, I deliberately avoid MVVM, LiveData, etc. Just Kotlin couroutine and Retrofit.
Consider this login process.
ApiInterface.kt
interface ApiInterface {
// Login
#POST("/user/validate")
suspend fun login(#Body requestBody: RequestBody): Response<ResponseBody>
}
ApiUtil.kt
class ApiUtil {
companion object {
var API_BASE_URL = "https://localhost:8100/testApi"
fun getInterceptor() : OkHttpClient {
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
return okHttpClient
}
fun createService() : ApiInterface {
val retrofit = Retrofit.Builder()
.client(getInterceptor())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(OJIRE_BASE_URL)
.build()
return retrofit.create(ApiInterface::class.java)
}
}
fun login(userParam: UserParam): String {
val gson = Gson()
val json = gson.toJson(userParam)
var resp = ""
val requestBody = json.toString().toRequestBody("application/json".toMediaTypeOrNull())
CoroutineScope(Dispatchers.IO).launch {
val response = createService().login(requestBody)
withContext(Dispatchers.Main){
if (response.isSuccessful){
val gson = GsonBuilder().setPrettyPrinting().create()
val prettyJson = gson.toJson(
JsonParser.parseString(
response.body()
?.string()
)
)
resp = prettyJson
Log.d("Pretty Printed JSON :", prettyJson)
}
else {
Log.e("RETROFIT_ERROR", response.code().toString())
}
}
}
return resp
}
}
LoginActivity.kt
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
edtUsername = findViewById(R.id.edtUsername)
edtPassword = findViewById(R.id.edtPassword)
btnLogin = findViewById(R.id.btnLogin)
btnLogin.setOnClickListener {
val api = ApiUtil()
val userParam = UserParam(edtMobileNo.text.toString(), edtPassword.text.toString())
val response = JSONObject(api.login(userParam))
var msg = ""
if (response.getString("message").equals("OK")){
msg = "Login OK"
}
else {
msg = "Login failed"
}
Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
}
}
}
When debugging the login activity, the API response is captured properly on prettyJson
The problem is resp is still empty. Guess that's how async process work. What I want is to wait until the API call is completed, then the result can be nicely passed to resp as the return value of login(). How to do that?
Well, you got several things wrong here. We'll try to fix them all.
First, the main problem you described is that you need to acquire resp in login() synchronously. You got this problem only because you first launched an asynchronous operation there. Solution? Don't do that, get the response synchronously by removing launch(). I guess withContext() is also not required as we don't do anything that requires the main thread. After removing them the code becomes much simpler and fully synchronous.
Last thing that we need to do with login() is to make it suspendable. It needs to wait for the request to finish, so it is a suspend function. The resulting login() should be similar to:
suspend fun login(userParam: UserParam): String {
val gson = Gson()
val json = gson.toJson(userParam)
val requestBody = json.toString().toRequestBody("application/json".toMediaTypeOrNull())
val response = createService().login(requestBody)
return if (response.isSuccessful){
val gson = GsonBuilder().setPrettyPrinting().create()
gson.toJson(
JsonParser.parseString(
response.body()
?.string()
)
)
}
else {
Log.e("RETROFIT_ERROR", response.code().toString())
// We need to do something here
}
}
Now, as we converted login() to suspendable, we can't invoke it from the listener directly. Here we really need to launch asynchronous operation, but we won't use CoroutineScope() as you did in your example, because it leaked background tasks and memory. We will use lifecycleScope like this:
btnLogin.setOnClickListener {
val api = ApiUtil()
val userParam = UserParam(edtMobileNo.text.toString(), edtPassword.text.toString())
lifecycleScope.launch {
val response = JSONObject(api.login(userParam))
var msg = ""
if (response.getString("message").equals("OK")){
msg = "Login OK"
}
else {
msg = "Login failed"
}
withContext(Dispatchers.Main) {
Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
}
}
}
Above code may not be fully functional. It is hard to provide working examples without all required data structures, etc. But I hope you get the point.
Also, there are several other things in your code that could be improved, but I didn't touch them to not confuse you.
Has anyone been able to accomplish sending a multipart/form-data POST in Android with CRONET yet? I have had no success trying to upload an image/png using a POST request to our server and am curious if anyone has.
notes : i need CRONET based solution only
Post Method Example:
val myBuilder = CronetEngine.Builder(context)
// Enable caching of HTTP data and
// other information like QUIC server information, HTTP/2 protocol and QUIC protocol.
val cronetEngine: CronetEngine = myBuilder
.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024.toLong())
.enableHttp2(true)
.enableQuic(true)
.build()
val executor: Executor = Executors.newSingleThreadExecutor()
val requestBuilder = cronetEngine.newUrlRequestBuilder(
"FULL-URL",
MyUrlRequestCallback(),
executor
)
// Content-Type is required, removing it will cause Exception
requestBuilder.addHeader("Content-Type","application/json; charset=UTF-8")
requestBuilder.setHttpMethod("POST")
val myUploadDataProvider = MyUploadDataProvider()
requestBuilder.setUploadDataProvider(myUploadDataProvider,executor)
val request: UrlRequest = requestBuilder.build()
request.start()
MyUploadDataProvider Class:
import android.util.Log
import org.chromium.net.UploadDataProvider
import org.chromium.net.UploadDataSink
import java.lang.Exception
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
private const val TAG = "MyUploadDataProvider"
//TODO replace username and passowrd "_user & _pass"
var string: String ="{\"username\":\"_user\",\"password\":\"_pass\"}"
val charset = StandardCharsets.UTF_8
class MyUploadDataProvider() : UploadDataProvider() {
override fun getLength(): Long {
val size:Long = string.length.toLong()
Log.e(TAG,"Length = "+size)
return size
}
override fun rewind(uploadDataSink: UploadDataSink?) {
Log.e(TAG,"REWIND IS CALLED")
uploadDataSink!!.onRewindSucceeded()
}
override fun read(uploadDataSink: UploadDataSink?, byteBuffer: ByteBuffer?) {
Log.e(TAG,"READ IS CALLED")
byteBuffer!!.put(string.toByteArray(charset))
//byteBuffer.rewind()
//For chunked uploads, true if this is the final read. It must be false for non-chunked uploads.
uploadDataSink!!.onReadSucceeded(false)
}
}
To upload a file, I would rather use create() methods from UploadDataProviders. With help of these methods, you can create an UploadDataProvider for uploading a File, FileDescriptor or even a byte array. For instance, to upload an empty array of bytes, I could do something as:
...
val myUploadDataProvider = UploadDataProviders.create(byteArrayOf())
...
Or, If I have a File object, I can also pass it directly into the create method:
...
val myUploadDataProvider = UploadDataProviders.create(someFile)
...
So, the whole code can look as follows:
...
val myBuilder = CronetEngine.Builder(this)
val cronetEngine: CronetEngine = myBuilder
.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024.toLong())
.enableHttp2(true)
.enableQuic(true)
.build()
val executor: Executor = Executors.newSingleThreadExecutor()
val requestBuilder = cronetEngine.newUrlRequestBuilder(
"http://ptsv2.com/",
object : UrlRequest.Callback() {
override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) {
}
override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
}
override fun onFailed(request: UrlRequest?, info: UrlResponseInfo?, error: CronetException?) {
}
override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
}
override fun onRedirectReceived(request: UrlRequest?, info: UrlResponseInfo?, newLocationUrl: String?) {
}
},
executor
)
requestBuilder.addHeader("Content-Type","application/json; charset=UTF-8")
requestBuilder.setHttpMethod("POST")
val myUploadDataProvider = UploadDataProviders.create(byteArrayOf())
requestBuilder.setUploadDataProvider(myUploadDataProvider,executor)
val request: UrlRequest = requestBuilder.build()
request.start()
...
yes, cronet and volley is google officially supported library but please also consider the other side of google...google also official support retrofit...because its fast and less boilerplate code...please read some blogs and also you can visit udacity nano degree course where say it retrofit is best for networking...
Can I use this signer https://github.com/babbel/okhttp-aws-signer for upload file to s3?
I use this https://bytes.babbel.com/en/articles/2019-01-03-okhttp-aws-signer.html reference when build my sample app:
class AwsSigingInterceptor(private val signer: OkHttpAwsV4Signer) : Interceptor {
private val dateFormat: ThreadLocal<SimpleDateFormat>
init {
dateFormat = object : ThreadLocal<SimpleDateFormat>() {
override fun initialValue(): SimpleDateFormat {
val localFormat = SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US)
localFormat.timeZone = TimeZone.getTimeZone("UTC")
return localFormat
}
}
}
override fun intercept(chain: Chain): Response =
chain.run {
val request = request()
val newRequest = request.newBuilder()
.addHeader("x-amz-date", dateFormat.get().format(clock.now()))
.addHeader("host", request.url().host())
.build()
val signed = signer.sign(newRequest, "<accessKeyId>", "<secretAccessKey>")
proceed(signed)
}
}
but I didn't see any x-amz-content-sha256 there.. only additional Authorization header added. When I try the debug result header in Advanced Rest Client or Postman, it says "Missing required header for this request: x-amz-content-sha256"
I am getting an SSL exception when I try to upload a file to Amazon S3's pre-signed URL with OkHttp 3.9.1: SSLException: Write error: ssl=0xa0b73280: I/O error during system call, Connection reset by peer
It is the same problem as in another SO question but in my case it fails always. I upload just files over 1MiB in size, I have not tried small files.
As I mentioned in my answer in that question, switching to Java's HttpURLConnection fixed the problem and the upload works perfectly.
Here is my RequestBody implementation (in Kotlin) to upload a file from Android's Uri and I do use OkHttp's .put() method:
class UriRequestBody(private val file: Uri, private val contentResolver: ContentResolver, private val mediaType: MediaType = MediaType.parse("application/octet-stream")!!): RequestBody() {
override fun contentLength(): Long = -1L
override fun contentType(): MediaType? = mediaType
override fun writeTo(sink: BufferedSink) {
Okio.source((contentResolver.openInputStream(file))).use {
sink.writeAll(it)
}
}
}
and here is my HttpURLConnection implementation:
private fun uploadFileRaw(file: Uri, uploadUrl: String, contentResolver: ContentResolver) : Int {
val url = URL(uploadUrl)
val connection = url.openConnection() as HttpURLConnection
connection.doOutput = true
connection.requestMethod = "PUT"
val out = connection.outputStream
contentResolver.openInputStream(file).use {
it.copyTo(out)
}
out.close()
return connection.responseCode
}
What is OkHttp doing differently so it can lead to this SSL exception?
EDIT:
Here is the OkHttp code to upload the file (using the default application/octet-stream mime type):
val s3UploadClient = OkHttpClient().newBuilder()
.connectTimeout(30_000L, TimeUnit.MILLISECONDS)
.readTimeout(30_000L, TimeUnit.MILLISECONDS)
.writeTimeout(60_000L, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true)
.build()
val body: RequestBody = UriRequestBody(file, contentResolver)
val request = Request.Builder()
.url(uploadUrl)
.put(body)
.build()
s3UploadClient.newCall(request).execute()
And this is the JavaScript server code that generates the pre-signed upload URL:
const s3 = new aws.S3({
region: 'us-west-2',
signatureVersion: 'v4'
});
const signedUrlExpireSeconds = 60 * 5;
const signedUrl = s3.getSignedUrl('putObject', {
Bucket: config.bucket.name,
Key: `${fileName}`,
Expires: signedUrlExpireSeconds
});
This seems to work with retrofit library:
fun uploadImage(imagePath: String, directUrl: String): Boolean {
Log.d(TAG, "Image: ${imagePath}, Url: $directUrl")
val request = Request.Builder()
.url(directUrl)
.put(RequestBody.create(null, File(imagePath)))
.build()
val response = WebClient.getOkHttpClient().newCall(request).execute()
return response.isSuccessful
}
I want to do a login validation using POST method and to get some information using GET method.
I've URL, server Username and Password already of my previous project.
For Android, Volley is a good place to get started. For all platforms, you might also want to check out ktor client or http4k which are both good libraries.
However, you can also use standard Java libraries like java.net.HttpURLConnection
which is part of the Java SDK:
fun sendGet() {
val url = URL("http://www.google.com/")
with(url.openConnection() as HttpURLConnection) {
requestMethod = "GET" // optional default is GET
println("\nSent 'GET' request to URL : $url; Response Code : $responseCode")
inputStream.bufferedReader().use {
it.lines().forEach { line ->
println(line)
}
}
}
}
Or simpler:
URL("https://google.com").readText()
Send HTTP POST/GET request with parameters using HttpURLConnection :
POST with Parameters:
fun sendPostRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Your API Link>")
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "POST"
val wr = OutputStreamWriter(getOutputStream());
wr.write(reqParam);
wr.flush();
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
println("Response : $response")
}
}
}
GET with Parameters:
fun sendGetRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Yout API Link>?"+reqParam)
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "GET"
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
println("Response : $response")
}
}
}
Using only the standard library with minimal code!
thread {
val json = try {
URL(url).readText()
} catch (e: Exception) {
return#thread
}
runOnUiThread { displayOrWhatever(json) }
}
This starts a GET request on a new thread, leaving the UI thread to respond to user input. However, we can only modify UI elements from the main/UI thread, so we actually need a runOnUiThread block to show the result to our user. This enqueues our display code to be run on the UI thread soon.
The try/catch is there so your app won't crash if you make a request with your phone's internet off. Add your own error handling (e.g. showing a Toast) as you please.
.readText() is not part of the java.net.URL class but a Kotlin extension method, Kotlin "glues" this method onto URL. This is enough for plain GET requests, but for more control and POST requests you need something like the Fuel library.
Have a look at Fuel library, a sample GET request
"https://httpbin.org/get"
.httpGet()
.responseString { request, response, result ->
when (result) {
is Result.Failure -> {
val ex = result.getException()
}
is Result.Success -> {
val data = result.get()
}
}
}
// You can also use Fuel.get("https://httpbin.org/get").responseString { ... }
// You can also use FuelManager.instance.get("...").responseString { ... }
A sample POST request
Fuel.post("https://httpbin.org/post")
.jsonBody("{ \"foo\" : \"bar\" }")
.also { println(it) }
.response { result -> }
Their documentation can be found here
I think using okhttp is the easiest solution. Here you can see an example for POST method, sending a json, and with auth.
val url = "https://example.com/endpoint"
val client = OkHttpClient()
val JSON = MediaType.get("application/json; charset=utf-8")
val body = RequestBody.create(JSON, "{\"data\":\"$data\"}")
val request = Request.Builder()
.addHeader("Authorization", "Bearer $token")
.url(url)
.post(body)
.build()
val response = client . newCall (request).execute()
println(response.request())
println(response.body()!!.string())
Remember to add this dependency to your project https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
UPDATE: July 7th, 2019
I'm gonna give two examples using latest Kotlin (1.3.41), OkHttp (4.0.0) and Jackson (2.9.9).
UPDATE: January 25th, 2021
Everything is okay with the most updated versions.
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.12.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
Get Method
fun get() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users?page=2")
val request = Request.Builder()
.url(url)
.get()
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val mapperAll = ObjectMapper()
val objData = mapperAll.readTree(responseBody)
objData.get("data").forEachIndexed { index, jsonNode ->
println("$index $jsonNode")
}
}
POST Method
fun post() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users")
//just a string
var jsonString = "{\"name\": \"Rolando\", \"job\": \"Fakeador\"}"
//or using jackson
val mapperAll = ObjectMapper()
val jacksonObj = mapperAll.createObjectNode()
jacksonObj.put("name", "Rolando")
jacksonObj.put("job", "Fakeador")
val jacksonString = jacksonObj.toString()
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = jacksonString.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(body)
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val objData = mapperAll.readTree(responseBody)
println("My name is " + objData.get("name").textValue() + ", and I'm a " + objData.get("job").textValue() + ".")
}
Maybe the simplest GET
For everybody stuck with NetworkOnMainThreadException for the other solutions: use AsyncTask or, even shorter, (yet still experimental) Coroutines:
launch {
val jsonStr = URL("url").readText()
}
If you need to test with plain http don't forget to add to your manifest:
android:usesCleartextTraffic="true"
For the experimental Coroutines you have to add to build.gradle as of 10/10/2018:
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.24.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.24.0"
...
If you are using Kotlin, you might as well keep your code as succinct as possible. The run method turns the receiver into this and returns the value of the block.
this as HttpURLConnection creates a smart cast. bufferedReader().readText() avoids a bunch of boilerplate code.
return URL(url).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
You can also wrap this into an extension function.
fun URL.getText(): String {
return openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
And call it like this
return URL(url).getText()
Finally, if you are super lazy, you can extend the String class instead.
fun String.getUrlText(): String {
return URL(this).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
}
And call it like this
return "http://somewhere.com".getUrlText()
You can use kohttp library. It is a Kotlin DSL HTTP client. It supports the features of square.okhttp and provides a clear DSL for them. KoHttp async calls are powered by coroutines.
httpGet extension function
val response: Response = "https://google.com/search?q=iphone".httpGet()
you can also use async call with coroutines
val response: Deferred<Response> = "https://google.com/search?q=iphone".asyncHttpGet()
or DSL function for more complex requests
val response: Response = httpGet {
host = "google.com"
path = "/search"
param {
"q" to "iphone"
"safe" to "off"
}
}
You can find more details in docs
To get it with gradle use
implementation 'io.github.rybalkinsd:kohttp:0.12.0'
Without adding additional dependencies, this works. You don't need Volley for this. This works using the current version of Kotlin as of Dec 2018: Kotlin 1.3.10
If using Android Studio, you'll need to add this declaration in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
You should manually declare imports here. The auto-import tool caused me many conflicts.:
import android.os.AsyncTask
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection
You can't perform network requests on a background thread. You must subclass AsyncTask.
To call the method:
NetworkTask().execute(requestURL, queryString)
Declaration:
private class NetworkTask : AsyncTask<String, Int, Long>() {
override fun doInBackground(vararg parts: String): Long? {
val requestURL = parts.first()
val queryString = parts.last()
// Set up request
val connection: HttpsURLConnection = URL(requestURL).openConnection() as HttpsURLConnection
// Default is GET so you must override this for post
connection.requestMethod = "POST"
// To send a post body, output must be true
connection.doOutput = true
// Create the stream
val outputStream: OutputStream = connection.outputStream
// Create a writer container to pass the output over the stream
val outputWriter = OutputStreamWriter(outputStream)
// Add the string to the writer container
outputWriter.write(queryString)
// Send the data
outputWriter.flush()
// Create an input stream to read the response
val inputStream = BufferedReader(InputStreamReader(connection.inputStream)).use {
// Container for input stream data
val response = StringBuffer()
var inputLine = it.readLine()
// Add each line to the response container
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
// TODO: Add main thread callback to parse response
println(">>>> Response: $response")
}
connection.disconnect()
return 0
}
protected fun onProgressUpdate(vararg progress: Int) {
}
override fun onPostExecute(result: Long?) {
}
}
GET and POST using OkHttp
private const val CONNECT_TIMEOUT = 15L
private const val READ_TIMEOUT = 15L
private const val WRITE_TIMEOUT = 15L
private fun performPostOperation(urlString: String, jsonString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val body = jsonString.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.post(body)
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
private fun performGetOperation(urlString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.get()
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
Object serialization and deserialization
#Throws(JsonProcessingException::class)
fun objectToJson(obj: Any): String {
return ObjectMapper().writeValueAsString(obj)
}
#Throws(IOException::class)
fun jsonToAgentObject(json: String?): MyObject? {
return if (json == null) { null } else {
ObjectMapper().readValue<MyObject>(json, MyObject::class.java)
}
}
Dependencies
Put the following lines in your gradle (app) file. Jackson is optional. You can use it for object serialization and deserialization.
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
You can use this library Fuel Library as well, which makes it further easier.
val map = mutableMapOf<String, String>()
map.put("id","629eeb9da9d8f50016e1af96")
val httpAsync = url
.httpPost()
.jsonBody(
Gson().toJson(map) // for json string
)
.responseString { request, response, result -> //do something with the response }