android线程的那些事

有些时候Thread里面更新UI是可以成功的。
比如在Activity里面的Oncreate里面调用thread.start(),在线程中更新UI,可能会成功。这是因为不能在子线程中更新UI的异常信息是在viewrootImpl里面抛出来的,但是viewrootImpl的创建是在Activity的onResume函数里面。所以会出现上述现象。如果再Thrad.start之前sleep几百毫秒,或者start放在onresume里面,就会抛出异常。
那为甚要设计成更新UI必须要在主线程呢?
1.解决多线程并发问题
2.提高性能
3.提供开发者简化的出来平台。

终止正在进行的Asynctask
如果要连续的执行Asynctask,前面执行中的Asynctask是不能被停止的,不过我们可以间接的实现该功能,比如通过变量控制其是否已经被停止.
public class PhotoTask extends AsyncTask<String, File, Void>{
    
    @Override
    protected Void doInBackground(String... path) {
        File parent = new File(path[0]);
        if (parent.isDirectory()) {
            // 获取当前目录下的目录和文件
            File[] files = parent.listFiles();
            for (int j = 0; j < files.length; j++) {
                 if(isCancelled()) return null; // Task被取消了,马上退出循环
                
                File file = files[j];  
                publishProgress(file );
            }
        }
    }

    @Override
    public void onProgressUpdate(File... files) {
         if(isCancelled()) return;   // Task被取消了,不再继续执行后面的代码
        .........
    }
}


public class PhotoGridView extends GridView implements OnItemClickListener{
     private PhotoTask task;   // 保持对Task的引用
    
    @Override
    public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
         if (task != null && task.getStatus() == AsyncTask.Status.RUNNING) {
            task.cancel(true);  //  如果Task还在运行,则先取消它
        }
        ......
        // 启动新的任务
         task = new PhotoTask();
        task.execute(path);
        
    }
} 

AsyncTask 不是萬能的
我们一般耗时的工作都放在thread中执行,比如AsyncTask,Thread在执行时,或多或少都有可能被强制杀掉,所以还不是最保险的方式.完整解決費時工作的方法,不僅要將費時的工作放在 Thread.run() 中執行,還要將這個 Thread 放在 Service 中執行。
其實為了減輕開發者的負擔, Android 1.5 已經加了 IntentService 這個新類別。如果你要寫個用到網路的應用,用這個 IntentService 才是你的完美解決方案。

要使用這個 IntentService 其實很簡單,你只要繼承這個 IntentService 並將該項費時的工作,移到 onHandleIntent() 中即可。onHandleIntent() 是被 non-UI thread 所喚起的。因此在這裏面你可以放心地去執行你的下載工作。

研究 IntentService 原始碼
這次,我們來深入研究 IntentService 的原始碼,看他是如何用短短幾行代碼,來解決這個問題。
	public abstract class IntentService extends Service { 
	  ... 
	}

從 class 的宣告中,你可以知道 IntentService 就是一般的 Service。

接著他定義一個 ServiceHandler。一般我們在創建 Handler 時,都是用預設的建構式。預設的 Handler 建構式,是依附在當前的 thread 身上。所以一般開發者都以為 Handler 一定是依附在 main-thread 上,這是不對的觀念。
	private final class ServiceHandler extends Handler { 
	  public ServiceHandler(Looper looper) { 
	    super(looper); 
	  } 
	  ... 
	}

這個 ServiceHandler,是依附在一個 non-UI thread 的 Looper 上。Looper 就是訊息迴圈 (message loop),這是 Android UI 在處理各式訊息時,最重要的元件之一。而 ServiceHandler 建構式中所傳入的 Looper 物件,是在 onCreate() 中所建立的。
	@Override 
	public void onCreate() { 
	  super.onCreate(); 
	  HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); 
	  thread.start(); 
	   
	  mServiceLooper = thread.getLooper(); 
	  mServiceHandler = new ServiceHandler(mServiceLooper); 
	}

看到了吧,mServiceLooper 就是 thread 物件中的 Looper 物件。這個 thread 物件就是一個 HandlerThread,他是一個 non-UI thread。

所以現在這個 Service,一個帶有 message loop(mServiceLooper) 的 non-UI thread (mServiceHandler) 正一直在背後執行著,監看是否有任何傳入的 message。

接著,每當你呼叫 startService(new Intent(this, MyIntentService.class)) 啟動 IntentService 時,其實他只是很簡單地送出一個 message 到 mServiceHandler 上。
	@Override 
	public void onStart(Intent intent, int startId) { 
	  Message msg = mServiceHandler.obtainMessage(); 
	  msg.arg1 = startId; 
	  msg.obj = intent; 
	  mServiceHandler.sendMessage(msg); 
	}

我們知道這個 message 最終會由 Handler.handleMessage() 來處理。由於整個 mServiceHandler 是在 non-UI thread 中執行。當他在 handleMessage() 中呼叫你的 onHandleIntent() 時,你的 onHandleIntent() 自然也是在 non-UI thread 中執行。
	private final class ServiceHandler extends Handler { 
	  ... 
	  @Override 
	  public void handleMessage(Message msg) { 
	    onHandleIntent((Intent)msg.obj); 
	    stopSelf(msg.arg1); 
	  } 
	}

onHandleIntent() 執行結束後,接著呼叫 stopSelf(int)。由於 IntentService 是設計用來可以在一個 Service 中服務多項工作,因此在這裡,不可以呼叫 stopSelf()。

如果你呼叫 startService() 多次,每一次的呼叫都會被轉成一個 message,並放在 mServiceLooper 的 message queue 中,等待被服務。一個 message 所對應的工作被完成後,才會繼續服務下一個工作。所以,這些等待被服務的工作,並不是一起並行 (Concurrent) 的,而是循序執行。

當所有的工作都處裡完時,也是 Service 該結束的時候。
	@Override 
	public void onDestroy() { 
	  mServiceLooper.quit(); 
	}

在 onDestroy() 中,唯一要做的事,就是要將 mServiceLooper 停下來。

從研究這個 IntentService 的原始碼,我們可以學到如何運用簡單的 pattern (Service + Handler + HandlerThread),幫我們更簡易與有系統地,完成我們所想要做的事。

最後我們先前說過,在 IntentServcie 中等待被服務的工作,並不會被一起並行,而是循序執行。如果你今天想要這些等待被服務的工作,能夠一起被並行,在研讀完這個 IntentService 的原始碼後,你自己知不知道如何寫個可支援並行工作的 IntentService?

提示:可以用 Service + AsyncTask 的組合。
参考: http://my.oschina.net/dingbuoyi/blog/61639

猜你喜欢

转载自null-point.iteye.com/blog/1728427