kotlin Jetpack Compose 框架
首先,确保在你的build.gradle
文件中添加OkHttp和Jetpack Compose的依赖:
dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'androidx.compose.runtime:runtime:1.0.1' implementation 'androidx.compose.ui:ui:1.0.1' implementation 'androidx.compose.foundation:foundation:1.0.1' implementation 'androidx.compose.material:material:1.0.1' implementation 'androidx.activity:activity-compose:1.3.1' implementation 'androidx.core:core-ktx:1.6.0' }
接下来,编写下载和播放音频的逻辑:
import android.content.res.Configuration import android.media.MediaPlayer import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.squareup.okhttp3.OkHttpClient import com.squareup.okhttp3.Request import java.io.File import java.io.FileOutputStream import java.io.IOException class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { DownloadAndPlayAudioApp() } } } fun DownloadAndPlayAudioApp() { val audioUrl = "https://example.com/audio.mp3" val downloadDir = "${applicationContext.externalCacheDir?.absolutePath}/audio/" val audioFilePath = "$downloadDir/audio.mp3" var isDownloading by remember { mutableStateOf(false) } var isPlaying by remember { mutableStateOf(false) } val mediaPlayer = remember { MediaPlayer() } Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Button( onClick = { if (!isDownloading) { isDownloading = true downloadAudio(audioUrl, audioFilePath) } }, enabled = !isDownloading && !isPlaying ) { Text("Download Audio") } Spacer(modifier = Modifier.height(16.dp)) Button( onClick = { if (!isPlaying) { mediaPlayer.reset() mediaPlayer.setDataSource(audioFilePath) mediaPlayer.prepare() mediaPlayer.start() isPlaying = true } else { mediaPlayer.pause() isPlaying = false } }, enabled = !isDownloading ) { Text(if (isPlaying) "Stop Playing" else "Play Audio") } } DisposableEffect(Unit) { onDispose { mediaPlayer.release() } } } suspend fun downloadAudio(url: String, filePath: String) { val client = OkHttpClient() val request = Request.Builder().url(url).build() try { val response = client.newCall(request).execute() if (!response.isSuccessful) throw IOException("Unexpected code $response") val file = File(filePath) val fos = FileOutputStream(file) fos.write(response.body()?.bytes()) fos.close() Log.d("AudioDownload", "Download complete.") } catch (e: Exception) { Log.e("AudioDownload", "Error downloading audio", e) } } showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) ( fun DefaultPreview() { DownloadAndPlayAudioApp() }
在这段代码中,我们首先定义了一个MainActivity
,在其中设置了包含DownloadAndPlayAudioApp
的界面。DownloadAndPlayAudioApp
是一个Composable函数,它管理着下载状态和播放状态,并且包含了两个按钮,一个用于下载音频,另一个用于播放/停止音频。
下载音频的逻辑封装在suspend
函数downloadAudio
中,它使用OkHttp发起网络请求,将音频数据写入本地文件。播放音频的逻辑则通过MediaPlayer
实现,当点击播放按钮时,MediaPlayer
会加载指定的音频文件并开始播放。
请注意,为了使下载音频的异步操作生效,你需要将相关代码放在LaunchedEffect
或async
/await
结构中,本示例中简化了这一过程,直接使用了suspend
函数。在实际项目中,你可能需要根据你的协程架构进行适当的调整。
此外,记得在AndroidManifest.xml中申请必要的权限,例如网络访问和外部存储权限。
要在Jetpack Compose应用中使用OkHttp下载远程音频文件到本地,并使用MediaPlayer播放该音频文件,你需要执行几个步骤。以下是一个简化的示例,展示了如何完成这些步骤:
- 添加依赖:首先,确保你的
build.gradle
文件中包含了OkHttp和Compose的依赖。 - 创建下载逻辑:使用OkHttp异步下载音频文件。
- 管理MediaPlayer:一旦音频文件被下载到本地,使用MediaPlayer播放它。
- Compose UI:在Compose UI中创建按钮来控制下载和播放。
1. 添加依赖
在build.gradle
文件中添加OkHttp的依赖:
implementation 'com.squareup.okhttp3:okhttp:4.9.3' // 使用最新版本
2. 创建下载逻辑和MediaPlayer管理
你可以创建一个ViewModel来处理下载和播放逻辑。
ViewModel (AudioViewModel.kt
):
import android.content.Context import android.media.MediaPlayer import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import okhttp3.* import java.io.* class AudioViewModel : ViewModel() { // 假设你有一个存储下载音频文件路径的变量 private var audioFilePath: String? = null // 播放状态 val isPlaying = MutableLiveData<Boolean>(false) // 下载音频文件 fun downloadAudio(context: Context, url: String) { // 这里是简化的下载逻辑,你可能需要处理错误和状态更新 val request = Request.Builder() .url(url) .build() val client = OkHttpClient() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { // 处理错误 } override fun onResponse(call: Call, response: Response) { if (!response.isSuccessful) { // 处理错误 return } val file = File(context.filesDir, "downloaded_audio.mp3") // 假设你有一个固定的文件名 audioFilePath = file.absolutePath // 将响应体写入文件 try (val sink: Sink = Okio.sink(file)) { val source = response.body()?.source() source?.readAll(sink.buffer(), sink.bufferSize()) sink.flush() } catch (e: IOException) { // 处理错误 } // 下载完成后,可以开始播放(如果需要) // playAudio(context) } }) } // 播放音频(如果文件已下载) fun playAudio(context: Context) { if (audioFilePath == null || audioFilePath?.isEmpty() == true) { // 处理文件未找到的情况 return } val mediaPlayer = MediaPlayer() mediaPlayer.setDataSource(audioFilePath) mediaPlayer.prepareAsync() // 使用异步准备,以避免阻塞UI线程 mediaPlayer.setOnPreparedListener { mediaPlayer.start() isPlaying.value = true } mediaPlayer.setOnCompletionListener { isPlaying.value = false mediaPlayer.release() // 释放资源 } } // 停止播放(如果需要) fun stopAudio() { // 你可以在这里添加逻辑来停止正在播放的音频 } }
3. Compose UI
在Compose UI中,你可以使用按钮来触发下载和播放。
Compose UI (AudioScreen.kt
):
import android.content.Context import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.insets.navigationBarsPadding import com.google.accompanist.insets.statusBarsPadding fun AudioScreen(context: Context) { val viewModel: AudioViewModel = viewModel() BoxWithConstraints( modifier = Modifier .fillMaxSize() .statusBarsPadding() .navigationBarsPadding() ) { Column( modifier = Modifier .fillMaxSize()