Android Kotlin Netzwerkprogrammierbeispiele

Android Kotlin Netzwerkprogrammierbeispiele einschließlich HTTP-Anforderungen, Datei-Download/Upload und WebSocket-Verbindungen

💻 HTTP-Anforderungen kotlin

🟢 simple ⭐⭐⭐

GET- und POST-Anforderungen senden mit HttpURLConnection und OkHttp-Bibliothek mit ordnungsgemäßer Fehlerbehandlung

⏱️ 30 min 🏷️ kotlin, android, networking, http
Prerequisites: Basic Kotlin knowledge, Internet permission in manifest
// Android Kotlin HTTP Request Examples
// Using HttpURLConnection and OkHttp

import android.content.Context
import kotlinx.coroutines.*
import java.io.*
import java.net.*
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject

// 1. HttpURLConnection Examples
class HttpURLConnectionExample(private val context: Context) {

    // Simple GET request
    suspend fun simpleGetRequest(url: String): String? = withContext(Dispatchers.IO) {
        var connection: HttpURLConnection? = null
        try {
            val urlObj = URL(url)
            connection = urlObj.openConnection() as HttpURLConnection
            connection.requestMethod = "GET"
            connection.connectTimeout = 10000
            connection.readTimeout = 10000

            val responseCode = connection.responseCode
            println("Response Code: $responseCode")

            if (responseCode == HttpURLConnection.HTTP_OK) {
                val response = connection.inputStream.bufferedReader().use { it.readText() }
                println("Response: $response")
                return@withContext response
            } else {
                println("Error response code: $responseCode")
                return@withContext null
            }
        } catch (e: Exception) {
            println("GET Request Error: ${e.message}")
            return@withContext null
        } finally {
            connection?.disconnect()
        }
    }

    // POST request with JSON body
    suspend fun postJsonRequest(url: String, jsonData: JSONObject): String? = withContext(Dispatchers.IO) {
        var connection: HttpURLConnection? = null
        try {
            val urlObj = URL(url)
            connection = urlObj.openConnection() as HttpURLConnection
            connection.requestMethod = "POST"
            connection.setRequestProperty("Content-Type", "application/json")
            connection.doOutput = true

            // Write JSON body
            val outputStream = connection.outputStream
            outputStream.write(jsonData.toString().toByteArray(Charsets.UTF_8))
            outputStream.flush()
            outputStream.close()

            val responseCode = connection.responseCode
            println("POST Response Code: $responseCode")

            if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED) {
                val response = connection.inputStream.bufferedReader().use { it.readText() }
                println("POST Response: $response")
                return@withContext response
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("POST Request Error: ${e.message}")
            return@withContext null
        } finally {
            connection?.disconnect()
        }
    }

    // POST request with form data
    suspend fun postFormData(url: String, params: Map<String, String>): String? = withContext(Dispatchers.IO) {
        var connection: HttpURLConnection? = null
        try {
            val urlObj = URL(url)
            connection = urlObj.openConnection() as HttpURLConnection
            connection.requestMethod = "POST"
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
            connection.doOutput = true

            // Build form data
            val formData = params.map { "${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}" }
                .joinToString("&")

            val outputStream = connection.outputStream
            outputStream.write(formData.toByteArray(Charsets.UTF_8))
            outputStream.flush()
            outputStream.close()

            val responseCode = connection.responseCode
            if (responseCode == HttpURLConnection.HTTP_OK) {
                val response = connection.inputStream.bufferedReader().use { it.readText() }
                return@withContext response
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("Form POST Error: ${e.message}")
            return@withContext null
        } finally {
            connection?.disconnect()
        }
    }

    // GET request with headers
    suspend fun getRequestWithHeaders(url: String, headers: Map<String, String>): String? = withContext(Dispatchers.IO) {
        var connection: HttpURLConnection? = null
        try {
            val urlObj = URL(url)
            connection = urlObj.openConnection() as HttpURLConnection
            connection.requestMethod = "GET"

            // Add custom headers
            headers.forEach { (key, value) ->
                connection.setRequestProperty(key, value)
            }

            val responseCode = connection.responseCode
            if (responseCode == HttpURLConnection.HTTP_OK) {
                val response = connection.inputStream.bufferedReader().use { it.readText() }
                return@withContext response
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("GET with Headers Error: ${e.message}")
            return@withContext null
        } finally {
            connection?.disconnect()
        }
    }
}

// 2. OkHttp Examples
class OkHttpExample(private val context: Context) {

    private val client = OkHttpClient.Builder()
        .connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
        .readTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
        .build()

    // Simple GET with OkHttp
    suspend fun simpleGet(url: String): String? = withContext(Dispatchers.IO) {
        try {
            val request = Request.Builder()
                .url(url)
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("OkHttp GET Error: ${e.message}")
            return@withContext null
        }
    }

    // Async GET with callback
    fun asyncGet(url: String, callback: (String?) -> Unit) {
        val request = Request.Builder()
            .url(url)
            .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                println("Async GET failed: ${e.message}")
                callback(null)
            }

            override fun onResponse(call: Call, response: Response) {
                val body = response.body?.string()
                callback(body)
            }
        })
    }

    // POST with JSON body
    suspend fun postJson(url: String, json: JSONObject): String? = withContext(Dispatchers.IO) {
        try {
            val mediaType = "application/json".toMediaType()
            val body = json.toString().toRequestBody(mediaType)

            val request = Request.Builder()
                .url(url)
                .post(body)
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("OkHttp POST Error: ${e.message}")
            return@withContext null
        }
    }

    // POST with form data
    suspend fun postForm(url: String, params: Map<String, String>): String? = withContext(Dispatchers.IO) {
        try {
            val formBody = FormBody.Builder()
            params.forEach { (key, value) ->
                formBody.add(key, value)
            }

            val request = Request.Builder()
                .url(url)
                .post(formBody.build())
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("Form POST Error: ${e.message}")
            return@withContext null
        }
    }

    // PUT request
    suspend fun putRequest(url: String, json: JSONObject): String? = withContext(Dispatchers.IO) {
        try {
            val mediaType = "application/json".toMediaType()
            val body = json.toString().toRequestBody(mediaType)

            val request = Request.Builder()
                .url(url)
                .put(body)
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("PUT Error: ${e.message}")
            return@withContext null
        }
    }

    // DELETE request
    suspend fun deleteRequest(url: String): Boolean = withContext(Dispatchers.IO) {
        try {
            val request = Request.Builder()
                .url(url)
                .delete()
                .build()

            val response = client.newCall(request).execute()
            return@withContext response.isSuccessful
        } catch (e: Exception) {
            println("DELETE Error: ${e.message}")
            return@withContext false
        }
    }

    // Request with custom headers
    suspend fun requestWithHeaders(
        url: String,
        headers: Map<String, String>,
        method: String = "GET"
    ): String? = withContext(Dispatchers.IO) {
        try {
            val requestBuilder = Request.Builder()
                .url(url)

            // Add headers
            headers.forEach { (key, value) ->
                requestBuilder.addHeader(key, value)
            }

            // Set method
            when (method.uppercase()) {
                "POST" -> {
                    val mediaType = "application/json".toMediaType()
                    val body = "{}".toRequestBody(mediaType)
                    requestBuilder.post(body)
                }
                "PUT" -> {
                    val mediaType = "application/json".toMediaType()
                    val body = "{}".toRequestBody(mediaType)
                    requestBuilder.put(body)
                }
                "DELETE" -> requestBuilder.delete()
            }

            val response = client.newCall(requestBuilder.build()).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("Request with Headers Error: ${e.message}")
            return@withContext null
        }
    }
}

// 3. Advanced HTTP Operations
class AdvancedHttpExample(private val context: Context) {

    private val client = OkHttpClient()

    // Multipart upload
    suspend fun uploadMultipart(
        url: String,
        file: File,
        fileParamName: String = "file",
        params: Map<String, String> = emptyMap()
    ): String? = withContext(Dispatchers.IO) {
        try {
            val requestBody = MultipartBody.Builder()
                .setType(MultipartBody.FORM)

            // Add parameters
            params.forEach { (key, value) ->
                requestBody.addFormDataPart(key, value)
            }

            // Add file
            val mediaType = "multipart/form-data".toMediaType()
            requestBody.addFormDataPart(
                fileParamName,
                file.name,
                file.asRequestBody(mediaType)
            )

            val request = Request.Builder()
                .url(url)
                .post(requestBody.build())
                .build()

            val response = client.newCall(request).execute()
            return@withContext response.body?.string()
        } catch (e: Exception) {
            println("Multipart Upload Error: ${e.message}")
            return@withContext null
        }
    }

    // Download file with progress
    suspend fun downloadFileWithProgress(
        url: String,
        outputFile: File,
        onProgress: (Long, Long) -> Unit
    ): Boolean = withContext(Dispatchers.IO) {
        try {
            val request = Request.Builder().url(url).build()
            val response = client.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext false
            }

            val inputStream = response.body?.byteStream() ?: return@withContext false
            val contentLength = response.body?.contentLength() ?: 0

            outputFile.outputStream().use { output ->
                val buffer = ByteArray(8192)
                var bytesRead: Int
                var totalBytesRead = 0L

                while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                    output.write(buffer, 0, bytesRead)
                    totalBytesRead += bytesRead

                    if (contentLength > 0) {
                        onProgress(totalBytesRead, contentLength)
                    }
                }
            }

            return@withContext true
        } catch (e: Exception) {
            println("Download Error: ${e.message}")
            return@withContext false
        }
    }

    // Retry mechanism
    suspend fun requestWithRetry(
        url: String,
        maxRetries: Int = 3,
        delayMillis: Long = 1000
    ): String? = withContext(Dispatchers.IO) {
        var lastException: Exception? = null

        repeat(maxRetries) { attempt ->
            try {
                val request = Request.Builder().url(url).build()
                val response = client.newCall(request).execute()

                if (response.isSuccessful) {
                    return@withContext response.body?.string()
                }
            } catch (e: Exception) {
                lastException = e
                println("Attempt $attempt failed: ${e.message}")

                if (attempt < maxRetries - 1) {
                    delay(delayMillis)
                }
            }
        }

        println("All $maxRetries attempts failed")
        return@withContext null
    }

    // Caching configuration
    fun createCachedClient(cacheDir: File, cacheSize: Long = 10 * 1024 * 1024): OkHttpClient {
        val cache = Cache(cacheDir, cacheSize)

        return OkHttpClient.Builder()
            .cache(cache)
            .build()
    }

    // Request with timeout
    suspend fun requestWithTimeout(
        url: String,
        timeoutSeconds: Int = 30
    ): String? = withContext(Dispatchers.IO) {
        try {
            val client = OkHttpClient.Builder()
                .connectTimeout(timeoutSeconds.toLong(), java.util.concurrent.TimeUnit.SECONDS)
                .readTimeout(timeoutSeconds.toLong(), java.util.concurrent.TimeUnit.SECONDS)
                .build()

            val request = Request.Builder().url(url).build()
            val response = client.newCall(request).execute()

            return@withContext response.body?.string()
        } catch (e: Exception) {
            println("Timeout Error: ${e.message}")
            return@withContext null
        }
    }
}

// 4. REST API Client
class RestClient(private val baseUrl: String, private val context: Context) {

    private val client = OkHttpClient()

    suspend fun get(endpoint: String): String? = withContext(Dispatchers.IO) {
        try {
            val url = "$baseUrl$endpoint"
            val request = Request.Builder()
                .url(url)
                .get()
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("GET $endpoint Error: ${e.message}")
            return@withContext null
        }
    }

    suspend fun post(endpoint: String, data: JSONObject): String? = withContext(Dispatchers.IO) {
        try {
            val url = "$baseUrl$endpoint"
            val mediaType = "application/json".toMediaType()
            val body = data.toString().toRequestBody(mediaType)

            val request = Request.Builder()
                .url(url)
                .post(body)
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("POST $endpoint Error: ${e.message}")
            return@withContext null
        }
    }

    suspend fun put(endpoint: String, data: JSONObject): String? = withContext(Dispatchers.IO) {
        try {
            val url = "$baseUrl$endpoint"
            val mediaType = "application/json".toMediaType()
            val body = data.toString().toRequestBody(mediaType)

            val request = Request.Builder()
                .url(url)
                .put(body)
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("PUT $endpoint Error: ${e.message}")
            return@withContext null
        }
    }

    suspend fun delete(endpoint: String): Boolean = withContext(Dispatchers.IO) {
        try {
            val url = "$baseUrl$endpoint"
            val request = Request.Builder()
                .url(url)
                .delete()
                .build()

            val response = client.newCall(request).execute()
            return@withContext response.isSuccessful
        } catch (e: Exception) {
            println("DELETE $endpoint Error: ${e.message}")
            return@withContext false
        }
    }
}

// Main demonstration
suspend fun demonstrateHTTPRequests(context: Context) {
    println("=== Android Kotlin HTTP Request Examples ===\n")

    // 1. HttpURLConnection examples
    println("--- 1. HttpURLConnection Examples ---")
    val httpExample = HttpURLConnectionExample(context)

    // Note: Replace with actual API endpoints for testing
    // val getResult = httpExample.simpleGetRequest("https://api.example.com/data")
    // println("GET Result: $getResult")

    // 2. OkHttp examples
    println("\n--- 2. OkHttp Examples ---")
    val okHttpExample = OkHttpExample(context)

    // val okHttpGet = okHttpExample.simpleGet("https://api.example.com/data")
    // println("OkHttp GET: $okHttpGet")

    // 3. Advanced operations
    println("\n--- 3. Advanced HTTP Operations ---")
    val advancedExample = AdvancedHttpExample(context)

    // 4. REST client
    println("\n--- 4. REST API Client ---")
    val restClient = RestClient("https://api.example.com", context)

    println("\n=== All HTTP Request Examples Completed ===")
}

💻 Datei-Download/Upload kotlin

🟡 intermediate ⭐⭐⭐⭐

Dateien herunterladen und hochladen mit Fortschrittsanzeige, Pause/Wiederaufnahme-Funktionalität und Hintergrundtask-Management

⏱️ 35 min 🏷️ kotlin, android, networking, download, upload
Prerequisites: Intermediate Kotlin, Storage permissions
// Android Kotlin File Download/Upload Examples
// Using DownloadManager, WorkManager, and OkHttp

import android.app.DownloadManager
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.webkit.MimeTypeMap
import androidx.work.*
import kotlinx.coroutines.*
import okhttp3.*
import java.io.*
import java.net.HttpURLConnection
import java.net.URL

// 1. DownloadManager (System-managed downloads)
class SystemDownloadExample(private val context: Context) {

    private val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

    // Simple download using DownloadManager
    fun downloadFile(url: String, title: String, description: String): Long {
        val request = DownloadManager.Request(Uri.parse(url)).apply {
            setTitle(title)
            setDescription(description)
            setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
            setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)

            // Set destination
            val fileName = URL(url).file.substringAfterLast("/")
            val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
                fileName.substringAfterLast('.')
            )
            setMimeType(mimeType)

            // Save to external storage
            setDestinationInExternalFilesDir(
                context,
                Environment.DIRECTORY_DOWNLOADS,
                fileName
            )
        }

        return downloadManager.enqueue(request)
    }

    // Query download status
    fun getDownloadStatus(downloadId: Long): Int {
        val query = DownloadManager.Query().setFilterById(downloadId)
        val cursor: Cursor = downloadManager.query(query)

        var status = -1
        if (cursor.moveToFirst()) {
            status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
        }
        cursor.close()

        return status
    }

    // Get download progress
    fun getDownloadProgress(downloadId: Long): Int {
        val query = DownloadManager.Query().setFilterById(downloadId)
        val cursor: Cursor = downloadManager.query(query)

        var progress = 0
        if (cursor.moveToFirst()) {
            val bytesDownloaded = cursor.getInt(
                cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
            )
            val bytesTotal = cursor.getInt(
                cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
            )

            if (bytesTotal > 0) {
                progress = (bytesDownloaded * 100 / bytesTotal)
            }
        }
        cursor.close()

        return progress
    }

    // Cancel download
    fun cancelDownload(downloadId: Long) {
        downloadManager.remove(downloadId)
    }
}

// 2. Manual Download with Progress
class ManualDownloadExample(private val context: Context) {

    // Download file with progress callback
    suspend fun downloadFile(
        url: String,
        outputFile: File,
        onProgress: (bytesRead: Long, contentLength: Long) -> Unit
    ): Boolean = withContext(Dispatchers.IO) {
        var connection: HttpURLConnection? = null
        try {
            val urlObj = URL(url)
            connection = urlObj.openConnection() as HttpURLConnection
            connection.connectTimeout = 10000
            connection.readTimeout = 10000
            connection.requestMethod = "GET"

            val responseCode = connection.responseCode
            if (responseCode != HttpURLConnection.HTTP_OK) {
                return@withContext false
            }

            val contentLength = connection.contentLengthLong
            val inputStream = connection.inputStream
            val outputStream = FileOutputStream(outputFile)

            val buffer = ByteArray(8192)
            var bytesRead: Int
            var totalBytesRead = 0L

            while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                outputStream.write(buffer, 0, bytesRead)
                totalBytesRead += bytesRead

                onProgress(totalBytesRead, contentLength)
            }

            outputStream.flush()
            outputStream.close()
            inputStream.close()

            return@withContext true
        } catch (e: Exception) {
            println("Download Error: ${e.message}")
            return@withContext false
        } finally {
            connection?.disconnect()
        }
    }

    // Download with OkHttp
    private val okHttpClient = OkHttpClient()

    suspend fun downloadWithOkHttp(
        url: String,
        outputFile: File,
        onProgress: (bytesRead: Long, contentLength: Long) -> Unit
    ): Boolean = withContext(Dispatchers.IO) {
        try {
            val request = Request.Builder().url(url).build()
            val response = okHttpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext false
            }

            val inputStream = response.body?.byteStream() ?: return@withContext false
            val contentLength = response.body?.contentLength() ?: 0

            outputFile.outputStream().use { output ->
                val buffer = ByteArray(8192)
                var bytesRead: Int
                var totalBytesRead = 0L

                while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                    output.write(buffer, 0, bytesRead)
                    totalBytesRead += bytesRead

                    onProgress(totalBytesRead, contentLength)
                }
            }

            return@withContext true
        } catch (e: Exception) {
            println("OkHttp Download Error: ${e.message}")
            return@withContext false
        }
    }

    // Resumeable download
    data class DownloadState(
        val url: String,
        val outputFile: File,
        val downloadedBytes: Long = 0
    )

    suspend fun resumeableDownload(
        state: DownloadState,
        onProgress: (bytesRead: Long, contentLength: Long) -> Unit
    ): Boolean = withContext(Dispatchers.IO) {
        var connection: HttpURLConnection? = null
        try {
            val urlObj = URL(state.url)
            connection = urlObj.openConnection() as HttpURLConnection

            // Set range header for resume
            if (state.downloadedBytes > 0 && state.outputFile.exists()) {
                connection.setRequestProperty(
                    "Range",
                    "bytes=${state.downloadedBytes}-"
                )
            }

            connection.connectTimeout = 10000
            connection.readTimeout = 10000

            val responseCode = connection.responseCode
            if (responseCode !in listOf(HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_PARTIAL)) {
                return@withContext false
            }

            val contentLength = if (responseCode == HttpURLConnection.HTTP_PARTIAL) {
                connection.contentLength + state.downloadedBytes
            } else {
                state.downloadedBytes
            }

            val inputStream = connection.inputStream
            val outputStream = if (state.downloadedBytes > 0) {
                FileOutputStream(state.outputFile, true)
            } else {
                FileOutputStream(state.outputFile)
            }

            val buffer = ByteArray(8192)
            var bytesRead: Int
            var totalBytesRead = state.downloadedBytes

            while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                outputStream.write(buffer, 0, bytesRead)
                totalBytesRead += bytesRead

                onProgress(totalBytesRead, contentLength)
            }

            outputStream.flush()
            outputStream.close()
            inputStream.close()

            return@withContext true
        } catch (e: Exception) {
            println("Resumeable Download Error: ${e.message}")
            return@withContext false
        } finally {
            connection?.disconnect()
        }
    }
}

// 3. File Upload
class FileUploadExample(private val context: Context) {

    private val client = OkHttpClient()

    // Upload single file
    suspend fun uploadFile(
        url: String,
        file: File,
        paramName: String = "file",
        additionalParams: Map<String, String> = emptyMap()
    ): String? = withContext(Dispatchers.IO) {
        try {
            val requestBody = MultipartBody.Builder()
                .setType(MultipartBody.FORM)

            // Add additional parameters
            additionalParams.forEach { (key, value) ->
                requestBody.addFormDataPart(key, value)
            }

            // Add file
            val mimeType = getMimeType(file)
            requestBody.addFormDataPart(
                paramName,
                file.name,
                file.asRequestBody(mimeType.toMediaTypeOrNull())
            )

            val request = Request.Builder()
                .url(url)
                .post(requestBody.build())
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("Upload Error: ${e.message}")
            return@withContext null
        }
    }

    // Upload multiple files
    suspend fun uploadMultipleFiles(
        url: String,
        files: List<File>,
        paramName: String = "files"
    ): String? = withContext(Dispatchers.IO) {
        try {
            val requestBody = MultipartBody.Builder()
                .setType(MultipartBody.FORM)

            files.forEach { file ->
                val mimeType = getMimeType(file)
                requestBody.addFormDataPart(
                    paramName,
                    file.name,
                    file.asRequestBody(mimeType.toMediaTypeOrNull())
                )
            }

            val request = Request.Builder()
                .url(url)
                .post(requestBody.build())
                .build()

            val response = client.newCall(request).execute()
            if (response.isSuccessful) {
                return@withContext response.body?.string()
            } else {
                return@withContext null
            }
        } catch (e: Exception) {
            println("Multiple Upload Error: ${e.message}")
            return@withContext null
        }
    }

    // Upload with progress
    suspend fun uploadFileWithProgress(
        url: String,
        file: File,
        paramName: String = "file",
        onProgress: (bytesWritten: Long, contentLength: Long) -> Unit
    ): Boolean = withContext(Dispatchers.IO) {
        try {
            // Create counting request body
            val countingRequestBody = object : RequestBody() {
                override fun contentType(): okhttp3.MediaType? {
                    return getMimeType(file).toMediaTypeOrNull()
                }

                override fun writeTo(sink: BufferedSink) {
                    val source = file.source().buffer()
                    val contentLength = file.length()
                    var bytesWritten = 0L

                    val buffer = ByteArray(8192)
                    var bytesRead: Int

                    while (source.read(buffer).also { bytesRead = it } != -1) {
                        sink.write(buffer, 0, bytesRead)
                        bytesWritten += bytesRead
                        onProgress(bytesWritten, contentLength)
                    }
                }

                override fun contentLength(): Long = file.length()
            }

            val requestBody = MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart(paramName, file.name, countingRequestBody)
                .build()

            val request = Request.Builder()
                .url(url)
                .post(requestBody)
                .build()

            val response = client.newCall(request).execute()
            return@withContext response.isSuccessful
        } catch (e: Exception) {
            println("Upload with Progress Error: ${e.message}")
            return@withContext false
        }
    }

    private fun getMimeType(file: File): String {
        val extension = file.extension.lowercase()
        return when (extension) {
            "jpg", "jpeg" -> "image/jpeg"
            "png" -> "image/png"
            "gif" -> "image/gif"
            "pdf" -> "application/pdf"
            "txt" -> "text/plain"
            "json" -> "application/json"
            "mp4" -> "video/mp4"
            "mp3" -> "audio/mpeg"
            else -> "application/octet-stream"
        }
    }
}

// 4. Background Upload with WorkManager
class UploadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val fileUrl = inputData.getString("fileUrl") ?: return Result.failure()
        val uploadUrl = inputData.getString("uploadUrl") ?: return Result.failure()

        val file = File(fileUrl)
        val uploader = FileUploadExample(applicationContext)

        val result = uploader.uploadFile(uploadUrl, file)

        return if (result != null) {
            Result.success(workDataOf("response" to result))
        } else {
            Result.failure()
        }
    }

    companion object {
        fun enqueueUpload(context: Context, file: File, uploadUrl: String): UUID {
            val data = workDataOf(
                "fileUrl" to file.absolutePath,
                "uploadUrl" to uploadUrl
            )

            val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresBatteryNotLow(true)
                .build()

            val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
                .setConstraints(constraints)
                .setInputData(data)
                .build()

            WorkManager.getInstance(context).enqueue(uploadRequest)
            return uploadRequest.id
        }
    }
}

// Main demonstration
suspend fun demonstrateFileDownloadUpload(context: Context) {
    println("=== Android Kotlin File Download/Upload Examples ===\n")

    // 1. System download
    println("--- 1. System Download (DownloadManager) ---")
    val systemDownload = SystemDownloadExample(context)

    // Note: Replace with actual URL
    // val downloadId = systemDownload.downloadFile(
    //     url = "https://example.com/file.pdf",
    //     title = "File Download",
    //     description = "Downloading file..."
    // )

    // 2. Manual download with progress
    println("\n--- 2. Manual Download with Progress ---")
    val manualDownload = ManualDownloadExample(context)

    // val outputFile = File(context.getExternalFilesDir(null), "downloaded_file.pdf")
    // manualDownload.downloadFile(
    //     url = "https://example.com/file.pdf",
    //     outputFile = outputFile,
    //     onProgress = { bytesRead, totalBytes ->
    //         val progress = (bytesRead * 100 / totalBytes).toInt()
    //         println("Download progress: $progress%")
    //     }
    // )

    // 3. File upload
    println("\n--- 3. File Upload ---")
    val uploadExample = FileUploadExample(context)

    // val fileToUpload = File(context.filesDir, "upload.txt")
    // uploadExample.uploadFile(
    //     url = "https://example.com/upload",
    //     file = fileToUpload,
    //     additionalParams = mapOf("userId" to "123")
    // )

    println("\n=== All File Download/Upload Examples Completed ===")
}

💻 WebSocket-Verbindung kotlin

🟡 intermediate ⭐⭐⭐⭐

WebSocket-Verbindungen herstellen, Nachrichten senden/empfangen und Verbindungslebenszyklus verwalten

⏱️ 30 min 🏷️ kotlin, android, networking, websocket
Prerequisites: Intermediate Kotlin, OkHttp library
// Android Kotlin WebSocket Examples
// Using OkHttp WebSocket and Java-WebSocket

import android.content.Context
import kotlinx.coroutines.*
import okhttp3.*
import okio.ByteString
import org.json.JSONObject
import java.util.concurrent.TimeUnit

// 1. Basic WebSocket with OkHttp
class BasicWebSocketExample(private val context: Context) {

    private val client = OkHttpClient.Builder()
        .readTimeout(0, TimeUnit.MILLISECONDS)
        .build()
    private var webSocket: WebSocket? = null

    // Connect to WebSocket
    fun connect(url: String) {
        val request = Request.Builder()
            .url(url)
            .build()

        val listener = object : WebSocketListener() {
            override fun onOpen(ws: WebSocket, response: Response) {
                println("WebSocket Connected")
                println("Server response: ${response.message}")

                // Send welcome message
                ws.send("Hello from Android!")
            }

            override fun onMessage(ws: WebSocket, text: String) {
                println("Received text: $text")
            }

            override fun onMessage(ws: WebSocket, bytes: ByteString) {
                println("Received bytes: ${bytes.hex()}")
            }

            override fun onClosing(ws: WebSocket, code: Int, reason: String) {
                println("Closing: $code $reason")
                ws.close(code, reason)
            }

            override fun onClosed(ws: WebSocket, code: Int, reason: String) {
                println("WebSocket Closed: $code $reason")
            }

            override fun onFailure(ws: WebSocket, t: Throwable, response: Response?) {
                println("WebSocket Error: ${t.message}")
            }
        }

        webSocket = client.newWebSocket(request, listener)
    }

    // Send text message
    fun sendMessage(message: String) {
        webSocket?.send(message)
    }

    // Send JSON message
    fun sendJson(json: JSONObject) {
        webSocket?.send(json.toString())
    }

    // Send binary data
    fun sendBinary(data: ByteArray) {
        webSocket?.send(ByteString.of(*data))
    }

    // Close connection
    fun close() {
        webSocket?.close(1000, "Goodbye!")
        webSocket = null
    }
}

// 2. Advanced WebSocket with Reconnection
class AdvancedWebSocketExample(
    private val context: Context,
    private val url: String
) {
    private val client = OkHttpClient.Builder()
        .readTimeout(0, TimeUnit.MILLISECONDS)
        .build()

    private var webSocket: WebSocket? = null
    private var isConnected = false
    private var reconnectAttempts = 0
    private val maxReconnectAttempts = 5
    private val reconnectDelay = 3000L // 3 seconds

    private val messageQueue = mutableListOf<String>()

    // Message callback interface
    interface WebSocketCallback {
        fun onConnected()
        fun onMessage(text: String)
        fun onMessage(bytes: ByteString)
        fun onDisconnected()
        fun onError(error: String)
    }

    private var callback: WebSocketCallback? = null

    fun setCallback(cb: WebSocketCallback) {
        this.callback = cb
    }

    // Connect with auto-reconnect
    fun connect() {
        val request = Request.Builder()
            .url(url)
            .addHeader("Origin", context.packageName)
            .build()

        val listener = object : WebSocketListener() {
            override fun onOpen(ws: WebSocket, response: Response) {
                println("WebSocket Connected")
                isConnected = true
                reconnectAttempts = 0

                callback?.onConnected()

                // Send queued messages
                synchronized(messageQueue) {
                    messageQueue.forEach { message ->
                        ws.send(message)
                    }
                    messageQueue.clear()
                }
            }

            override fun onMessage(ws: WebSocket, text: String) {
                println("Received: $text")
                callback?.onMessage(text)
            }

            override fun onMessage(ws: WebSocket, bytes: ByteString) {
                println("Received bytes: ${bytes.hex()}")
                callback?.onMessage(bytes)
            }

            override fun onClosing(ws: WebSocket, code: Int, reason: String) {
                println("Closing: $code $reason")
            }

            override fun onClosed(ws: WebSocket, code: Int, reason: String) {
                println("WebSocket Closed: $code $reason")
                isConnected = false
                callback?.onDisconnected()

                // Attempt reconnection
                attemptReconnect()
            }

            override fun onFailure(ws: WebSocket, t: Throwable, response: Response?) {
                println("WebSocket Error: ${t.message}")
                isConnected = false
                callback?.onError(t.message ?: "Unknown error")

                // Attempt reconnection
                attemptReconnect()
            }
        }

        webSocket = client.newWebSocket(request, listener)
    }

    // Attempt reconnection
    private fun attemptReconnect() {
        if (reconnectAttempts < maxReconnectAttempts) {
            reconnectAttempts++

            CoroutineScope(Dispatchers.IO).launch {
                delay(reconnectDelay)
                println("Reconnecting... Attempt $reconnectAttempts")
                connect()
            }
        } else {
            println("Max reconnection attempts reached")
        }
    }

    // Send message with queueing
    fun sendMessage(message: String) {
        if (isConnected) {
            webSocket?.send(message)
        } else {
            // Queue message for later
            synchronized(messageQueue) {
                messageQueue.add(message)
            }
            println("Message queued: $message")
        }
    }

    // Close connection
    fun close() {
        reconnectAttempts = maxReconnectAttempts // Stop reconnection
        webSocket?.close(1000, "User closing")
        webSocket = null
        isConnected = false
    }
}

// 3. WebSocket Chat Example
class ChatWebSocket(private val context: Context) {

    private val client = OkHttpClient.Builder()
        .readTimeout(0, TimeUnit.MILLISECONDS)
        .build()

    private var webSocket: WebSocket? = null

    data class ChatMessage(
        val type: String, // "join", "message", "leave"
        val username: String,
        val content: String,
        val timestamp: Long = System.currentTimeMillis()
    )

    interface ChatCallback {
        fun onMessage(message: ChatMessage)
        fun onUserJoined(username: String)
        fun onUserLeft(username: String)
        fun onConnectionStatus(connected: Boolean)
    }

    private var callback: ChatCallback? = null

    fun setCallback(cb: ChatCallback) {
        this.callback = cb
    }

    // Connect to chat server
    fun connect(serverUrl: String, username: String) {
        val request = Request.Builder()
            .url(serverUrl)
            .build()

        val listener = object : WebSocketListener() {
            override fun onOpen(ws: WebSocket, response: Response) {
                println("Chat Connected as: $username")
                callback?.onConnectionStatus(true)

                // Send join message
                val joinMessage = ChatMessage(
                    type = "join",
                    username = username,
                    content = ""
                )
                ws.send(toJson(joinMessage))
            }

            override fun onMessage(ws: WebSocket, text: String) {
                val message = fromJson(text)
                message?.let {
                    when (it.type) {
                        "join" -> callback?.onUserJoined(it.username)
                        "leave" -> callback?.onUserLeft(it.username)
                        "message" -> callback?.onMessage(it)
                    }
                }
            }

            override fun onClosed(ws: WebSocket, code: Int, reason: String) {
                println("Chat Closed")
                callback?.onConnectionStatus(false)
            }

            override fun onFailure(ws: WebSocket, t: Throwable, response: Response?) {
                println("Chat Error: ${t.message}")
                callback?.onConnectionStatus(false)
            }
        }

        webSocket = client.newWebSocket(request, listener)
    }

    // Send chat message
    fun sendMessage(username: String, content: String) {
        val message = ChatMessage(
            type = "message",
            username = username,
            content = content
        )
        webSocket?.send(toJson(message))
    }

    // Leave chat
    fun leave(username: String) {
        val leaveMessage = ChatMessage(
            type = "leave",
            username = username,
            content = ""
        )
        webSocket?.send(toJson(leaveMessage))

        // Close connection
        webSocket?.close(1000, "User leaving")
        webSocket = null
    }

    private fun toJson(message: ChatMessage): String {
        val json = JSONObject()
        json.put("type", message.type)
        json.put("username", message.username)
        json.put("content", message.content)
        json.put("timestamp", message.timestamp)
        return json.toString()
    }

    private fun fromJson(json: String): ChatMessage? {
        return try {
            val obj = JSONObject(json)
            ChatMessage(
                type = obj.optString("type"),
                username = obj.optString("username"),
                content = obj.optString("content"),
                timestamp = obj.optLong("timestamp", System.currentTimeMillis())
            )
        } catch (e: Exception) {
            null
        }
    }
}

// 4. WebSocket Heartbeat/Ping
class HeartbeatWebSocket(
    private val context: Context,
    private val url: String
) {
    private val client = OkHttpClient.Builder()
        .readTimeout(0, TimeUnit.MILLISECONDS)
        .pingInterval(30, TimeUnit.SECONDS) // Send ping every 30 seconds
        .build()

    private var webSocket: WebSocket? = null
    private var heartbeatJob: Job? = null

    // Connect with heartbeat
    fun connect() {
        val request = Request.Builder()
            .url(url)
            .build()

        val listener = object : WebSocketListener() {
            override fun onOpen(ws: WebSocket, response: Response) {
                println("Heartbeat WebSocket Connected")
                startHeartbeat(ws)
            }

            override fun onMessage(ws: WebSocket, text: String) {
                println("Received: $text")
            }

            override fun onClosing(ws: WebSocket, code: Int, reason: String) {
                stopHeartbeat()
            }

            override fun onClosed(ws: WebSocket, code: Int, reason: String) {
                stopHeartbeat()
            }

            override fun onFailure(ws: WebSocket, t: Throwable, response: Response?) {
                stopHeartbeat()
            }
        }

        webSocket = client.newWebSocket(request, listener)
    }

    // Start heartbeat
    private fun startHeartbeat(ws: WebSocket) {
        heartbeatJob = CoroutineScope(Dispatchers.IO).launch {
            while (isActive) {
                delay(15000) // Send custom heartbeat every 15 seconds
                val heartbeat = JSONObject().apply {
                    put("type", "heartbeat")
                    put("timestamp", System.currentTimeMillis())
                }
                ws.send(heartbeat.toString())
                println("Heartbeat sent")
            }
        }
    }

    // Stop heartbeat
    private fun stopHeartbeat() {
        heartbeatJob?.cancel()
        heartbeatJob = null
    }

    // Close connection
    fun close() {
        stopHeartbeat()
        webSocket?.close(1000, "Closing")
        webSocket = null
    }
}

// 5. WebSocket Manager (Singleton)
class WebSocketManager private constructor(private val context: Context) {

    companion object {
        @Volatile
        private var instance: WebSocketManager? = null

        fun getInstance(context: Context): WebSocketManager {
            return instance ?: synchronized(this) {
                instance ?: WebSocketManager(context.applicationContext).also { instance = it }
            }
        }
    }

    private val connections = mutableMapOf<String, WebSocket>()

    data class WebSocketConfig(
        val url: String,
        val key: String = url,
        val autoReconnect: Boolean = true,
        val reconnectDelay: Long = 3000L
    )

    // Connect with config
    fun connect(config: WebSocketConfig, listener: WebSocketListener) {
        val client = OkHttpClient.Builder()
            .readTimeout(0, TimeUnit.MILLISECONDS)
            .build()

        val request = Request.Builder()
            .url(config.url)
            .build()

        val ws = client.newWebSocket(request, listener)
        connections[config.key] = ws
    }

    // Send message
    fun send(key: String, message: String) {
        connections[key]?.send(message)
    }

    // Close specific connection
    fun close(key: String) {
        connections[key]?.close(1000, "Closing")
        connections.remove(key)
    }

    // Close all connections
    fun closeAll() {
        connections.values.forEach { it.close(1000, "Closing all") }
        connections.clear()
    }
}

// Main demonstration
fun demonstrateWebSocket(context: Context) {
    println("=== Android Kotlin WebSocket Examples ===\n")

    // 1. Basic WebSocket
    println("--- 1. Basic WebSocket ---")
    val basicWs = BasicWebSocketExample(context)
    // basicWs.connect("wss://echo.websocket.org")
    // basicWs.sendMessage("Hello WebSocket!")

    // 2. Advanced WebSocket with reconnection
    println("\n--- 2. Advanced WebSocket with Reconnection ---")
    val advancedWs = AdvancedWebSocketExample(context, "wss://echo.websocket.org")
    // advancedWs.setCallback(object : AdvancedWebSocketExample.WebSocketCallback {
    //     override fun onConnected() { println("Connected!") }
    //     override fun onMessage(text: String) { println("Message: $text") }
    //     override fun onMessage(bytes: ByteString) { println("Bytes received") }
    //     override fun onDisconnected() { println("Disconnected") }
    //     override fun onError(error: String) { println("Error: $error") }
    // })
    // advancedWs.connect()

    // 3. Chat WebSocket
    println("\n--- 3. Chat WebSocket ---")
    val chatWs = ChatWebSocket(context)
    // chatWs.connect("wss://chat.example.com", "User123")
    // chatWs.sendMessage("User123", "Hello everyone!")

    // 4. WebSocket Manager
    println("\n--- 4. WebSocket Manager ---")
    val wsManager = WebSocketManager.getInstance(context)
    // wsManager.connect(
    //     WebSocketManager.WebSocketConfig("wss://echo.websocket.org"),
    //     listener
    // )

    println("\n=== All WebSocket Examples Completed ===")
}