の回答で書いたやつなんですが。
メディアファイル「a.mp3」「b.mp3」「c.mp3」があり、 a の再生が終わったら b を再生…とする方法です(MediaPlayer
使用)。
//using Android.App;
//using Android.Widget;
//using Android.OS;
//using Android.Media;
//using System.Threading.Tasks;
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
var button = FindViewById<Button>(Resource.Id.myButton);
var sounds = new int[]
{
Resource.Raw.trumpet1,
Resource.Raw.trumpet2
};
button.Click += async (sender, e) =>
{
foreach (var id in sounds)
{
await PlayAsync(id);
}
};
}
// 再生が終了したら true を、エラーだったら false を返す
private Task<bool> PlayAsync(int rscId)
{
var compSource = new TaskCompletionSource<bool>();
var mp = MediaPlayer.Create(this, rscId);
mp.Completion += (_, __) =>
{
compSource.SetResult(true);
};
mp.Error += (_, __) =>
{
compSource.SetResult(false);
};
mp.Start();
return compSource.Task;
}
}
MediaPlayer
は、再生が完了すると onCompletion
を通知するので、それを受信して次の曲を再生開始すればよいのですが、普通に書くとコールバック地獄に陥るので、Task<T>
化して、フラットに書けるようにします。
このような、「非同期処理で完了がイベントやコールバックで通知されるやつ」を Task<T>
な非同期メソッドに変換するために TaskCompletionSource<T>
を使う方法、は非常によく使うので覚えておくとよいと思います。過去にはダイアログボックスの表示について同様のテクニックで async/await 化する方法を書きました。
この Task<T>
を使ったテクニックは 「C# ならでは」 でしたが、Androidアプリ開発の公式言語である Kotlin でも同じようなことができます。
package nepula.net.soundsample
import android.media.MediaPlayer
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.experimental.launch
import kotlin.coroutines.experimental.suspendCoroutine
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val sounds = arrayOf(
R.raw.trumpet1,
R.raw.trumpet2
)
button.setOnClickListener {
launch {
sounds.forEach {id ->
playAsync(id)
}
}
}
}
suspend fun playAsync(id:Int) : Boolean {
return suspendCoroutine { cont : Continuation<Boolean> ->
val mp = MediaPlayer.create(this, id)
mp.setOnCompletionListener {
cont.resume(true)
}
mp.setOnErrorListener ( object : MediaPlayer.OnErrorListener {
override fun onError(p0: MediaPlayer?, p1: Int, p2: Int): Boolean {
cont.resume(false)
return true
}
})
mp.start()
}
}
}
TaskCompletionSource<T>
の代わりに Continuation<T>
を使う感じで。
非同期処理でも、レスポンス(or エラー)が一発で終わるものは RxJava を使う必要はないので、上記のようなパターンもよく使いますね。