7

Music別のセカンダリから呼び出されるクラスにメディアプレーヤーがありActivityます。それは正常に動作します。

しかし、(タイムアウトまたはボタンによって)画面がオフになると、音楽の再生が停止し、戻ってアクティビティを閉じようとすると、プログラムは「アプリが応答しません」にIllegalStateExceptionなりますmediaplayer.isPlaying()

画面が消えたときにメディアプレーヤーが停止しないようにするにはどうすればよいですか?

それはサービスを介する必要がありますか??

答えが「はい」であると仮定して、Musicクラスをサービスに変換しようとしました (以下を参照)。にも追加<service android:enabled="true" android:name=".Music" />し、次のようにクラスManifest.xmlを呼び出しています。Music

startService(new Intent(getBaseContext(), Music.class));
Music track = Music(fileDescriptor);

メイン アクティビティの 2 つの新しい行は、対応するインポートと共に とstartService(new Intent(getBaseContext(), Music.class));のみです。stopService(new Intent(getBaseContext(), Music.class));

しかし、サービスを開始しようとするとInstantiationExceptionエラーが発生します。can't instantiate class私は何が欠けていますか?

これは例外です:

E/AndroidRuntime(16642): FATAL EXCEPTION: main
E/AndroidRuntime(16642): java.lang.RuntimeException: Unable to instantiate service com.floritfoto.apps.ave.Music:                                                                             java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor                                                                   
E/AndroidRuntime(16642):    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2249)
E/AndroidRuntime(16642):    at android.app.ActivityThread.access$1600(ActivityThread.java:127)
E/AndroidRuntime(16642):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1213)
E/AndroidRuntime(16642):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(16642):    at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime(16642):    at android.app.ActivityThread.main(ActivityThread.java:4507)
E/AndroidRuntime(16642):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(16642):    at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime(16642):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
E/AndroidRuntime(16642):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
E/AndroidRuntime(16642):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(16642): Caused by: java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor
E/AndroidRuntime(16642):    at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime(16642):    at java.lang.Class.newInstance(Class.java:1319)
E/AndroidRuntime(16642):    at android.app.ActivityThread.handleCreateService(ActivityThread.java:2246)
E/AndroidRuntime(16642):    ... 10 more

これは Music.class です:

package com.floritfoto.apps.ave;

import java.io.FileDescriptor;
import java.io.IOException;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.IBinder;
import android.widget.Toast;

public class Music extends Service implements OnCompletionListener{
    MediaPlayer mediaPlayer;
    boolean isPrepared = false;

    //// TEstes de servico
    @Override
    public void onCreate() {
        super.onCreate();
        info("Servico criado!");
    }
    @Override
    public void onDestroy() {
        info("Servico fudeu!");
    }
    @Override
    public void onStart(Intent intent, int startid) {
        info("Servico started!");
    }   
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    public void info(String txt) {
        Toast toast = Toast.makeText(getApplicationContext(), txt, Toast.LENGTH_LONG);
        toast.show();
    }
    //// Fim testes de servico

    public Music(FileDescriptor fileDescriptor){
        mediaPlayer = new MediaPlayer();
        try{
            mediaPlayer.setDataSource(fileDescriptor);
            mediaPlayer.prepare();
            isPrepared = true;
            mediaPlayer.setOnCompletionListener(this);
        } catch(Exception ex){
            throw new RuntimeException("Couldn't load music, uh oh!");
        }
    }

    public void onCompletion(MediaPlayer mediaPlayer) {
        synchronized(this){
            isPrepared = false;
        }
    }

    public void play() {
        if(mediaPlayer.isPlaying()) return;
        try{
            synchronized(this){
                if(!isPrepared){
                    mediaPlayer.prepare();
                }
                mediaPlayer.seekTo(0);
                mediaPlayer.start();
            }
        } catch(IllegalStateException ex){
            ex.printStackTrace();
        } catch(IOException ex){
            ex.printStackTrace();
        }
    }

    public void stop() {
        mediaPlayer.stop();
        synchronized(this){
            isPrepared = false;
        }
    }

    public void switchTracks(){
        mediaPlayer.seekTo(0);
        mediaPlayer.pause();
    }

    public void pause() {
        mediaPlayer.pause();
    }

    public boolean isPlaying() {
        return mediaPlayer.isPlaying();
    }

    public boolean isLooping() {
        return mediaPlayer.isLooping();
    }

    public void setLooping(boolean isLooping) {
        mediaPlayer.setLooping(isLooping);
    }

    public void setVolume(float volumeLeft, float volumeRight) {
        mediaPlayer.setVolume(volumeLeft, volumeRight);
    }

    public String getDuration() {
        return String.valueOf((int)(mediaPlayer.getDuration()/1000));
    }
    public void dispose() {
        if(mediaPlayer.isPlaying()){
            stop();
        }
        mediaPlayer.release();
    }
}
4

2 に答える 2

6

Logcat の次の行は重要です。

Caused by: java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor

サービスには、引数を取らない別のコンストラクターが必要です。

public Music() {
    super("Music");
}

編集

画面がオフのときに音楽を再生し続けたい場合は、サービスを使用するのが正しいアプローチです。ただし、画面がオフの場合、電話機はスリープしようとするため、MediaPlayer.

最も信頼できる解決策は、部分的な WakeLockを使用して、音楽の再生中にデバイスがスリープ状態にならないようにすることです。WakeLock音楽を積極的に再生していないときは、必ず適切にリリースしてください。そうしないと、バッテリーが消耗します。

を使用することもできますstartForeground()。これにより、メモリが圧迫されたときにサービスが強制終了されるリスクが軽減されます。また、サービスの実行中に永続的な通知を表示することで、ユーザー エクスペリエンスを向上させます。

Musicでクラスをインスタンス化すると、Music track = Music(fileDescriptor);おそらく何らかの害があります。より良い方法は、ファイル記述子を にExtra渡すIntentことですstartService()

Intent serviceIntent = new Intent(this, Music.class);
serviceIntent.putExtra("ServiceFileDescriptor", fileDescriptor);
startService(serviceIntent);

Intent次に、サービスのonStartCommand()メソッドに渡されたときに、同じファイル記述子を取得します。

public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStart();

    Bundle bundle = intent.getExtras();
    // NOTE: The next line will vary depending on the data type for the file
    // descriptor. I'm assuming that it's an int.
    int fileDescriptor = bundle.getIntExtra("ServiceFileDescriptor");
    mediaPlayer = new MediaPlayer();
    try {
        mediaPlayer.setDataSource(fileDescriptor);
        ...
    ...
    return START_STICKY;
}

ここで注意すべき点がいくつかあります。コードを元のコンストラクター (削除する必要があります) から に移動しましたonStartCommand()onStart()このメソッドは 2.0 より前のデバイスでのみ呼び出されるため、削除することもできます。onStartCommand()最新の Android バージョンをサポートする場合は、代わりに を使用する必要があります。最後に、START_STICKY戻り値により、アクティビティから呼び出すまでサービスが実行され続けることが保証さstopService()れます。

編集2

サービスを使用すると、ユーザーは を中断することなくアクティビティ間を移動できますMediaPlayerActivityがメモリにとどまる時間をあまり制御することはできませんが、アクティブService(特に を呼び出しstartForeground()た場合) は、非常に強いメモリ プレッシャーがない限り、強制終了されません。

MediaPlayerサービスの開始後にと対話するには、いくつかのオプションがあります。Intentを作成し、アクション文字列 (および/またはいくつかの追加機能) を使用してサービスに実行させたいことを伝えることにより、追加のコマンドをサービスに渡すことができます。newをstartActivity()使用して再度呼び出すだけで、サービスで呼び出され、その時点で を操作できます。2 番目のオプションは、バインドされたサービス(例はこちら) を使用し、サービスと通信する必要があるアクティビティに出入りするたびにバインド/バインド解除することです。バインドされたサービスを使用すると、サービスを直接操作しているかのように "感じられます" が、バインドとバインド解除を管理する必要があるため、より複雑になります。IntentonStartCommand()MediaPlayer

于 2012-10-27T02:53:07.243 に答える