NullPointerException при записи в экземпляр AudioTrack в обратном вызове из собственного кода

63
8

Я выполняю некоторый процесс декодирования в собственном коде и вызываю из него метод Java, чтобы записать образцы в экземпляр AudioTrack. Процесс декодирования выполняется нормально, обратный вызов из собственного кода в Java:

private void writeToAudioTrack(final byte[] buffer)

вызывается, но как только объект Runnable начинает записывать сэмплы в AudioTrack, я сразу получаю исключение NullPointerException. Я совершенно уверен, что это вызвано неправильной резьбой, но не может понять, что там не так. Я прилагаю полный код Java:

public class Player
{
private AudioTrack track;
private boolean isInitialized = false;
private static Handler handler = new Handler();

public void init(String mediaSource)
{
// Call native function initEngine
isInitialized = initEngine(mediaSource);

if (isInitialized)
{
int bufSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STREAM);
track.play();
}
}

public void play()
{
// Call native rendering function in a separate thread
if (isInitialized)
{
new Thread(new Runnable()
{
@Override
public void run()
{
renderAudio();
}
}).start();
}
}

public void release()
{
isInitialized = false;
releaseEngine();
}

// This is callback from native code
private void writeToAudioTrack(final byte[] buffer)
{
handler.post(new Runnable()
{
@Override
public void run()
{
try
{
track.write(buffer, 0, buffer.length);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}

//
static
{
// Load native library
System.loadLibrary("decoder");
}

// Private native methods
private static native boolean initEngine(String mediaSource);
private static native void releaseEngine();
private static native void renderAudio();

Даже если я создаю буфер вместо блока try-catch:

byte [] buffer = new byte[256];
track.write(buffer, 0, buffer.length);

Я получаю тот же результат - NullPointerException.

Трассировки стека:

java.lang.NullPointerException на com.mautilus.audioplayer.Player $ 2.run(Player.java:99) на android.os.Handler.handleCallback(Handler.java:587) на android.os.Handler.dispatchMessage(Handler.java: 92) at android.os.Looper.loop(Looper.java:123) в android.app.ActivityThread.main(ActivityThread.java:4627) в java.lang.reflect.Method.invokeNative (собственный метод) в java. lang.reflect.Method.invoke(Method.java:521) в com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:876) в com.android.internal.os.ZygoteInit.main(ZygoteInit. java: 634) в dalvik.system.NativeStart.main (собственный метод)

спросил(а) 2021-01-28T02:16:51+03:00 4 месяца, 3 недели назад
1
Решение
62

Редактировать:

Чтобы добавить дополнительную защиту, я добавил:

if (!isInitialized) {
return;
}

перед методом writeToAudioTrack чтобы убедиться, что обратный вызов не вызывается, когда isInitialized является false. Как регистрируется обратный вызов? Можете ли вы показать этот код?

Может быть, глупый ответ, но у вашего конструктора вы уверены, что этого не должно быть:

if (!isInitialized) {
...
}

Если нет, то вы уверены, что метод run() не вызывается, если isInitialized был возвращен как false из вызова initEngine? Если это так, то track будет нулевой, что объясняет NPE в методе run().

Простым способом понять это было бы утверждение assert в run():

assertNotNull(trace);

или выполнить надлежащий тест и выкинуть IllegalStateException если trace равна null.

Единственная вещь, которая может привести к NPE является, если null значение для buffer был принят в writeToAudioTrack метод. Тестирование null и IllegalArgumentException могут быть хорошей идеей.

Надеюсь это поможет.

ответил(а) 2021-01-28T02:16:51+03:00 4 месяца, 3 недели назад
45

Ошибка была в типе одного из нативных методов - initEngine, он не должен быть объявлен как статический - в этом случае.

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

http://docs.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html#wp16660

Функция GetMethodID() приводит к инициализации неинициализированного класса. Поэтому, если загрузка библиотеки не связана с каким-либо экземпляром класса (нормальным):

static
{
// Load native library
System.loadLibrary("decoder");
}

то нативный код:

jmethodID writeToAudioTrackMethodID = NULL;
jclass cls = (*env)->FindClass(env, "com.mautilus.audioplayer.Player");

if (!cls)
writeToAudioTrackMethodID = (*env)->GetMethodID(env, cls, "writeToAudioTrack", "([B)V");

вызванный из статического метода - initEngine в моем случае, не может получить какой-либо существующий экземпляр класса (даже если такой экземпляр существует) и создает новый (с полями, инициализированными значениями по умолчанию) - это...

ответил(а) 2021-01-28T02:16:51+03:00 4 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема