Jensz
Jensz

Reputation: 89

Android NDK multithreading block UI responds

I recently write an Android program for game. Here is my design: There are two threads written in NDK/C++ taking charges of working tasks such as read and write buffers. For UI, I used Java surfaceview, which runs in a thread to draw graphics. I found that when NDK threads were running, UI main thread does not response any events like button click or screen touch. Is that anyone would help me for that? This is my layout xml:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:weightSum="1">

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center" >

            <Button
                android:id="@+id/embedded_soundtrack"
                android:text="I Love Rock n&apos; Roll"
                android:layout_width="140dp"
                android:layout_height="wrap_content"
                android:singleLine="true" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Croatian Rhapsody"
            android:id="@+id/button" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="    Stop    "
            android:id="@+id/Stop"
            android:onClick="onStop"
            android:nestedScrollingEnabled="false" />
    </LinearLayout>
    <com.example.nativeaudio.MainView
        android:id="@+id/surfaceView1"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight = "1" />

</LinearLayout>

This is my Java code: MainActivity:

public class NativeAudio extends Activity {
    static MainView v = null;
    ....
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        assetManager = getAssets();

        v = (MainView)findViewById(R.id.surfaceView1);


        ((Button) findViewById(R.id.embedded_soundtrack)).setOnClickListener(new OnClickListener() {
            boolean created = false;
            public void onClick(View view) {
                Log.v(TAG, "click play button");
                createEngine(assetManager, fMusic[playIndex]);
            }
        });

        ((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            public void onClick(View view) {
                Log.v(TAG, "click play button");
                createEngine(assetManager, fMusic[playIndex]);
            }
        });
    }
}

SurfaceView:

public class MainView extends SurfaceView
{
    private SurfaceHolder holder = null;
    int x, y;
    private MainThread t = null;
    Context context;
    volatile float touched_x, touched_y;

    public MainView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

    }

    public MainView(Context context, AttributeSet attrs) {
        super(context, attrs);

        x = y = 0;
        holder = getHolder();
        this.context = context;

    }

    // Constructor 
    public MainView (Context context) 
    {
        super(context);

    }

    public void pause ()
    {
        t.setRunning(false);
        while (true)
        {
            try
            {
                t.join();


     }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        break;
    }
    t = null;
}

public void resume () 
{
    Log.i("Beat", "main view is started.");
    t = new MainThread (holder, context);
    t.setRunning(true);
    t.start();
    }
}

UI rendering thread:

public class MainThread extends Thread 
{

    private SurfaceHolder holder;
    private static final Object lock = new Object(); 
    private Bitmap background = null;
    private Bitmap star = null;
    private Context context;
    ...
    public MainThread (SurfaceHolder surfaceHolder, Context context)
    {
       this.context = context;
       holder = surfaceHolder;

       // Load the image
       background = BitmapFactory.decodeResource (context.getResources(), R.drawable.bg);
       star = BitmapFactory.decodeResource (context.getResources(), R.drawable.star);

    }

    public void setRunning(boolean b) {isRunning = b;}

    @Override
    public void run() 
    {
        while (isRunning) 
        {
            // Lock the canvas before drawing
            Canvas canvas = holder.lockCanvas();    
            if (canvas != null) 
            {
                render(canvas);
                holder.unlockCanvasAndPost (canvas);    
            }
        }
    }   

This is my NDK C++:

static CBufferWrapper g_bufferwrapper;
static pthread_rwlock_t lock;
/*
 *  @function: JNI interface
 *  @input  :  JNI parameters: env, obj, assetManager, filename
 *  @output :  void
 *  @describe: call from java function to play music
 */
JNIEXPORT void JNICALL Java_com_example_nativeaudio_NativeAudio_createEngine
(JNIEnv* env, jobject obj, jobject assetManager, jstring filename){

    Print ("JNI Start to create Engine;");
    g_bufferwrapper.setEnviroment (env, obj, assetManager, filename);
    g_bufferwrapper.CreateThread ();

  }

void CBufferWrapper::CreateThread ()
{
    pthread_t th1,th2;
    int Num;

    Print ("Create Thread");
    pthread_rwlock_init (&rwlock, NULL);

    RingBuffer *ring = RingBuffer::getRingBuffer ();
    ring->Reset ();
    int ret = pthread_create (&th1, NULL, WriteThread,
        (void*)this);

    int ret1 = pthread_create (&th2, NULL, ReadThread,
        (void*)this);


    void *status;
    ret = pthread_join (th1, &status);
    ret1 = pthread_join (th2, &status);


    pthread_rwlock_destroy (&rwlock);
}

/*
 *  @function: run
 *  @input   : a void pointer of parameter.
 *  @output  : a void pointer
 *  @describe: running body of working thread. Will ternimate automatically 
 *      after read over PCM data
 */
void *CBufferWrapper::WriteThread (void *parameter)
{
    CBufferWrapper *bufwrapper = (CBufferWrapper*)parameter;
    while (bufwrapper->m_readSize < bufwrapper->m_totalSize)
    {

        bufwrapper->ConvertMp3toPCM ();
        usleep (1000);
    }

    bufwrapper->Reset ();
    Print ("Write Thread body exit");

    return NULL;
}

void *CBufferWrapper::ReadThread (void *parameter)
{
    Print ("Start to run read thread.");

    CBufferWrapper *obj = (CBufferWrapper*)parameter;
    while (!obj->m_isTerminate)
    {
        //doing read buffer task
        usleep(100);
    }
}

While NDK thread is running, buttons in main UI don't response. I guess that's because in CreateThread() function pthread_join block function return, so the main UI doesn't receive any message. Is that any solution for this issue?

Upvotes: 3

Views: 1336

Answers (1)

Jensz
Jensz

Reputation: 89

I have solved this problem by myself. Just comment out pthread_join function to let create_thread() return. Android main thread has itself message loop. pthread_join isn't necessary.

Upvotes: 2

Related Questions