I perform some decoding process in the native code and call a Java method from it to write samples into the AudioTrack instance. Decoding process runs fine, the callback from native code to Java:
private void writeToAudioTrack(final byte[] buffer)
is called , but once the Runnable object starts to write samples into the AudioTrack I get a NullPointerException immediately. I am quite sure it is caused by wrong threading, but cannot figure out what is wrong there. I am attaching the complete Java code:
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();
Even if I create a buffer in place of try-catch block:
byte [] buffer = new byte[256];
track.write(buffer, 0, buffer.length);
I get the same result – NullPointerException.
Stack Trace:
java.lang.NullPointerException
at com.mautilus.audioplayer.Player$2.run(Player.java:99)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:876)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:634)
at dalvik.system.NativeStart.main(Native Method)
The error was in the type of one of the native methods –
initEngine, it must not be declared as static – in this case.According to the documentation:
http://docs.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html#wp16660
The
GetMethodID()causes an uninitialized class to be initialized. So if the library loading is not tied with any class instance (that’s normal):then the native code:
called from a static method –
initEnginein my case, can’t obtain any existing instance of the class (even if such instance exists) and creates new one (with fields initialized to default values) – that’s it…