Reputation: 3844
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)
Upvotes: 0
Views: 525
Reputation: 3844
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):
static
{
// Load native library
System.loadLibrary("decoder");
}
then the native code:
jmethodID writeToAudioTrackMethodID = NULL;
jclass cls = (*env)->FindClass(env, "com.mautilus.audioplayer.Player");
if (!cls)
writeToAudioTrackMethodID = (*env)->GetMethodID(env, cls, "writeToAudioTrack", "([B)V");
called from a static method - initEngine
in 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...
Upvotes: 0
Reputation: 116908
Edit:
To add some more protection, I'd add a:
if (!isInitialized) {
return;
}
to the front of the writeToAudioTrack
method to make sure that the callback is not being called when isInitialized
is false
. How is the callback registered? Can you show that code?
Maybe a stupid answer but in your constructor, are you sure it should not be:
if (!isInitialized) {
...
}
If not then are you sure that the run()
method is not being called if isInitialized
was returned as false
from the call to initEngine
? If this is the case then track
would be null which would explain the NPE in the run()
method.
An easy way to figure this out would be to put an assert
in the run()
:
assertNotNull(trace);
or do a proper test and throw an IllegalStateException
if trace
is null
.
The only other thing that could cause the NPE is if a null
value for buffer
was passed into the writeToAudioTrack
method. Testing for null
there and throwing an IllegalArgumentException
might be a good idea.
Hope this helps.
Upvotes: 1