Android NDK 多线程块 UI 响应

Android NDK multithreading block UI responds

我最近写了一个Android游戏程序。下面是我的设计: NDK/C++写了两个线程,负责读写缓冲区等工作任务。对于UI,我使用了Javasurfaceview,它在一个线程中运行来绘制图形。我发现当 NDK 线程为 运行 时,UI 主线程不会响应任何事件,例如按钮点击或屏幕触摸。有人会为此帮助我吗? 这是我的布局 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>

这是我的 Java 代码: 主要活动:

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]);
            }
        });
    }
}

表面视图:

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渲染线程:

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);    
            }
        }
    }   

这是我的 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);
    }
}

虽然 NDK 线程是 运行,但主 UI 中的按钮没有响应。我想那是因为在 CreateThread() 函数 pthread_join 中阻塞了函数 return,所以 main UI 没有收到任何消息。 这个问题有解决办法吗?

这个问题我自己解决了。只需注释掉 pthread_join 函数,让 create_thread() return。 Android 主线程有自己的消息循环。 pthread_join 没有必要。