站內搜尋

2011/11/23

Android Tips 開發小技巧 - Thread 無法 Interrupted!?

雖然在 Android 裡開 Thread 的機會因為一些其他方便的機制而相對的變少了些,
但依然改變不了它是一個非常重要的 class 的事實。
最近在看的一個和 Android default Gallery3D 有關的 bug 就發現了一個有趣的問題。

基本上,這個問題最大的重點就是整個程式的  code flow 被導向了一個未被我們預期的方向,
而原因分析起來,則是因為有一個 Thread 無法被好好的 interrupted 掉。
這個結果再加上不足夠的 error handle,就很足夠造成一些很難以分析的問題了。

舉個例子,你可能在 interrupted 的時後會做某些事情,但卻晚做了,造成了 null pointer exception。

那,回到正題,一切的一切都是源至於 Thread 不肯乖乖的被 Interrupted 掉,Why?

在看完整個 Thread 的程式後,簡單的發現了二個可能性,
1. Thread.Sleep
2. synchronized

這二個看起來都是很有可能咬住 Thread 的兇手,為了驗證,小弟寫了一些簡單的 sample code。
實驗證明,Thread.Sleep 會需要你用一個 try catch 包起來不是沒有原因的!!!
在發生 Thread.interrupted 的時後,會直接 throw 出 InterruptedException 讓你順利的結束。

但,synchronized 可就沒這麼友善了,一但你進入了 synchronized 的等待,很不幸的是,什麼事情都叫不動你了。

所以,在 Thread 中使用 synchronized 就需要特別的小心。
假設 Thread 卡在 synchronized 裡,程式的控制上可能就不如你所想像的,interrupted 很可能不會立即的生效。而且,發生類似的問題,可能會造成 debug 上一定程度的複雜。

在這裡分享一下今天研究的這小東西,希望對大家有幫助。

下面是我實驗的 sample code。
FYI

public class CodeSample extends Activity 
{
    private static final String TAG = "CodeSample";
    Thread mTh;
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        Thread th = new Thread(new Runnable() {
            @Override
            public void run()
            {
                try
                {
                    synchronized (mTh)
                    {
                        Log.i(TAG, "lock");
                        mTh.start();
                        Thread.sleep(10000);
                    }
                } 
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                    Log.i(TAG, "1 .bye");
                    return;
                }
                Log.i(TAG, "1. done");
            }
            
        });
        th.start();
        mTh =  new Thread(new Runnable() {
            @Override
            public void run()
            {
                synchronized (mTh)
                {
                    Log.i(TAG, "My tune");
                }
                Log.i(TAG, "done");
            }
        });
    }
    
    @Override
    protected void onResume()
    {
        super.onResume();
        Handler handler = new Handler();
        handler.post(new Runnable() {
            @Override
            public void run()
            {
                Log.i(TAG, "shutdown...");
                repeatShuttingDownThread(mTh);
            }
            
        });
    }
    private void repeatShuttingDownThread(Thread targetThread) {
        for (int i = 0; i < 30 && targetThread.isAlive(); ++i) {
            targetThread.interrupt();
            try {
                targetThread.join(50);
            } catch (InterruptedException e) {
                Log.w(TAG, "Cannot stop the thread: " + 
                            targetThread.getName(), e);
                Thread.currentThread().interrupt();
                return;
            }
        }

        if (targetThread.isAlive()) {
            Log.w(TAG, "Cannot stop the thread (Timeout): " + 
                            targetThread.getName());
        }
    }
}

熱門文章