Android MediaPlayer не работает на API 21 (но НЕ API 19 или 22!) - PullRequest
0 голосов
/ 15 мая 2018

В моем приложении я хотел бы иметь простой метод, который использует класс MediaPlayer для однократного воспроизведения звукового ресурса (и снова, если необходимо).

Я успешно достиг этого (см. Код ниже) и успешно протестировал его на реальном аппаратном устройстве с API 23, а также на эмуляторах с уровнями API от 19 до P ... ЗА ИСКЛЮЧЕНИЕМДЛЯ API 21, ГДЕ ЭТО НЕ СДЕЛАНО.

Разве это не странно?Почему что-то работает на API 19 ... и 23, а не на 21?

Так что я имею в виду под неудачей?Что ж, в API 21, если вы нажмете кнопку play , звук не воспроизводится , и, похоже, ничего не произойдет (хотя, если вы посмотрите на LogCat, вы увидите все виды страшныхсообщения родной библиотеки, включая множество «смертей» и даже «надгробную плиту» или два).

Если вы продолжаете нажимать play во второй раз, происходит то же самое, с большим количеством внутренних сообщений об ошибках.генерируется в logcat ... и в третий раз вы, наконец, получаете сбой.

Единственная ошибка, которую я вижу, которая может быть связана с чем-то, что я контролирую, это исключение, которое перехватывается в Log.i:

"com.example.boober.stackqmediaplayer I / SFX: ошибка: java.io.IOException: подготовка не удалась .: status = 0x64"

Все остальные сообщения иокончательная трассировка стека генерируется внутренними внутренними библиотеками.

Я погуглил эти ошибки, прочитал документацию logcat и MediaPlayer.Надеюсь, я просто делаю что-то принципиально глупое, что кто-то может указать мне.

Я действительно надеялся, что кто-то там может взглянуть.

Я сделал очень минимальныйПример проблемы, которую очень легко вырезать / вставлять / перестраивать в Android Studio, чтобы можно было воспроизвести проблему:

MainActivity:

public class MainActivity extends Activity {

    MediaPlayer ourPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ourPlayer = MediaPlayer.create(this, R.raw.soundeffect);
    }

    public void play(View v) {
        try {
            ourPlayer.stop();
            ourPlayer.prepare();
            ourPlayer.start();
        } catch (IOException e) {
            Log.i("SFX", "error:" + e.toString());
        }
    }
}

activity_main.xml:

*Папка 1037 *

Res (используйте любой аудиофайл):

enter image description here

Build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.example.boober.stackqmediaplayer"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

Ответы [ 2 ]

0 голосов
/ 20 мая 2018

В случае, если у кого-то точно такой же вариант использования, как у меня ...

То, что вам нужно только воспроизводить звук с самого начала, при каждом нажатии кнопки (или при событии) ...

Я нашел другое простое решение / обходной путь, который, кажется, работает:

@Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // ourPlayer = MediaPlayer.create(this, R.raw.soundeffect);

    }

public void play(View v) {

        if (ourPlayer != null) {
        ourPlayer.release(); }
        ourPlayer = MediaPlayer.create(this, R.raw.soundeffect);
        ourPlayer.start();

    }

Пожалуйста, дайте мне знать, если по какой-то причине это плохая идея! Спасибо!

0 голосов
/ 19 мая 2018

Согласно документации MediaPlayer :

  1. ourPlayer начинается в состоянии инициализировано , а не в состоянии ожидания state, потому что вы использовали MediaPlayer.create().

  2. На графике состояний вы можете вызывать stop() только тогда, когда игрок играет или приостановлен, но при первом нажатии кнопкиработает, ваш игрок только инициализирован, поэтому ваш вызов stop() переводит игрока в состояние ошибки, а не в остановленное состояние.

  3. Ваш вызов prepare() в состоянии ошибки,IOException или IllegalStateException или могут быть брошены.В документах не говорится, когда первый брошен, но он попал в ваш try-catch.Последнее приводит к сбою приложения при третьем нажатии кнопки воспроизведения.

Решение состоит в том, чтобы вызывать stop() только тогда, когда игрок играет или * 1034.* paused , поскольку эти состояния имеют прямую стрелку в остановленном состоянии.

MediaPlayer имеет метод isPlaying(), но isPaused() нет.Некоторые обходные пути к этой проблеме могут быть найдены здесь

Итак, ниже мое предлагаемое решение:

public class MainActivity extends Activity {
    private MediaPlayer ourPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ourPlayer = MediaPlayer.create(this, R.raw.beer03);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ourPlayer.release(); // Free native resources
    }

    public void play(View ignored) {
        try {
            if (ourPlayer.isPlaying() || isPlaybackPaused()) {
                ourPlayer.stop();
                ourPlayer.prepare();
            }
            player.start();
        } catch (IOException e) {
            Log.i("SFX", "error:" + e.toString());
        }
    }

    private boolean isPlaybackPaused() {
        // TODO
    }
}
...