2015年11月29日 星期日

Program, Process, Thread

----------------------------------------------------------------------
1. Program 與 Process 觀念:
----------------------------------------------------------------------
Program:
一群程式碼的集合,用以解決特定的問題。以物件導向的觀念來類比,相當於Class。

Process:
由Program所產生的執行個體,一個Program可以同時執行多次,產生多個Process。以物件導向的觀念來類比,相當於Object。每一個Process又由以下兩個東西組成
    1. 一個Memory Space。相當於Object的variable,不同Process的Memory Space也不同,彼此看不到對方的Memory Space。
    2. 一個以上的Thread。Thread代表從某個起始點開始(例如main),到目前為止所有函數的呼叫路徑,以及這些呼叫路徑上所用到的區域變數。當然程式的執行狀態,除了紀錄在主記憶體外,CPU內部的暫存器(如Program Counter, Stack Pointer, Program Status Word等)也需要一起紀錄。所以Thread又由下面兩項組成
            a. Stack:紀錄函數呼叫路徑,以及這些函數所用到的區域變數
            b. 目前CPU的狀態


參考資料:







----------------------------------------------------------------------
2. Thread的使用
----------------------------------------------------------------------
- Thread 線程:
指運行中的程序的調度單位。一個線程指的是進程(Processes)中一個單一順序的控制流。它是系統獨立調度和分派的基本單位。同一進程中的多個線程將共享該進程中的全部系統資源,比如文件描述符和信號處理等等。 一個進程可以有很多線程,每個線程并行執行不同的任務。.所以意思就是當主程式在進行時,同時又需要去執行別的任務,就可以另起一個Thread(線程),將要執行的內容在背後運作,等到執行完成後,再把結果秀出來

 - Android Thread:
    1. Android於Thread有一個重要的限制「Android為了考量安全性和執行效能,不允許副執行緒直接更改主畫面的資料」,也就是說在我們另起的Thread都沒辦法做出如:將畫面變色與改變文字內容等畫面上的行為,而解決方法就是利用"Handler"來做溝通的橋樑。
    2. MainThread(UI Thread)和Worker Thread:
        a. 所有与UI有关的代码写在MainThread当中。
        b. 主线程不能阻塞,主线程一般不进行耗时较长的操作,比如联网下载,读取大文件等。

 - Android Activity 間传递数据的方法:
Intent对象的基本概念:
    1. Intent是Android应用程序组件之一
    2. Intent对象在Android系统中表示一种意图
    3. Intent当中最重要的内容是action与data
使用Intent在Activity之间传递数据的方法:
    1. 在Activity之间可以通过Intent对象传递数据
    2. 使用putExtra()系列方法向Intent对象当中存储数据
    3. 使用getXXXExtra()系列方法从Intent对象当中取出数据

 - 整個流程是:.
1. 主執行緒開始執行。.
2. 需要Thread(副執行緒)運行比較久的程序,開始執行Thread。.
3. Thread於背景執行(通常附帶時間控管,如Thread.sleep(1000);).
4. Thread完成,傳送訊息告知Handler。.
5. 執行Handler內的邏輯程式,改變畫面內容。


 - 如何產生Thread
1. Class Thread有兩個Constructor: 
    (1) Thread()
    (2) Thread(Runnable)
2. Runnable是一個interface,定義於java.lang內,其宣告為
    public interface Runnable {
      public void run(); 
    }    
3. 使用Thread()產生的Thread,其進入點為Thread裡的run();
4. 使用Thread(Runnable)產生的Thread,其進入點為Runnable物件裡的run()。
5. 當run()結束時,這個Thread也就結束了 。
6. 啟動 thread 時,使用 start() 方法:Thread.start();
7. 清除 thread 時,使thread 為 null: Thread = null;

 (1)Thread thread = new Thread(){
      @Override
        public void run() {

      }
 
 (2)
Thread 
thread = new Thread();
Runnable1 r1  = new Runnable1(“test”);
thread(r1).start();
     
class Runnable1 implements Runnable {
      /* 附加執行的副程式 */
      private String name;

      public Runable1 (String name) {
          this.name = name;
      }
      /* Runnable 主體 */
      public void run(){
   }
};
Thread的優先權
優先權高的Thread其佔有CPU的機會比較高,但優先權低的也都會有機會執行到
Thread.setPriority(int)設定Thread的優先權,數字越大優先權越高。
public static final int MAX_PRIORITY 10 
public static final int MIN_PRIORITY 1 
public static final int NORM_PRIORITY 5
其他有關Thread執行的方法有:
yield():先讓給別的Thread執行
sleep(int time):休息time mini second(1/1000)
join():呼叫ThreadA.join()的執行緒會等到ThreadA結束後,才能繼續執行
Critical Section(關鍵時刻)的保護措施
如果設計者沒有提供保護機制的話,Thread取得和失去CPU控制權的時機是由作業系統來決定
Java的解決辦法是,JVM會在每個物件上擺一把鎖(lock),然後程式設計者可以宣告執行某一段程式,必須拿到某物件的鎖才行,這個鎖同時間最多只有一個執行緒可以擁有它
Ex: 1. 使用string 或 int 等變數 flag 及 if else 判斷式來進行保護。
     2. 
使用 try catch 排除例外狀況
 - Handler、Looper和MessageQueue的基本原理?
Handler把消息对象添加到MessageQueue中,Looper负责从MessageQueue的头部不断取出消息对象,取出一个消息对象之后交给Handler处理。
Message msg = handler.obtainMessage();//获取Message对象
msg.what = 2;//给Message属性赋值,msg.obj = s; // 6. 把s赋值给obj
handler.sendMessage(msg);//把消息对象放到消息队列当中去
//1. Looper将会从消息队列当中将消息对象取出,
//2. Looper将会找到与消息对象对应的Handler对象(每一个消息对象都有一个对应的Handler对象)。
//3. Looper将会调用消息对象的handleMessage(Message msg)方法,用于处理该消息对象

EX:
public class MainActivity enxtends Activity {
private Textview textview;
private Button button;
private Handler handler; //3. 定义一个变量
@Override
protected void conCreate(Bundle savedInstanceState) {
super.onCreate(savedINstanceState);
setContentView(R.layout.activity_main);

textView = (TextView)findViewById(R.id.textViewId);
button = (Button)findViewById(R.id.buttonId);

handler = new Myhandler(); //4.使用Myhandler类生成对象

button.setOnClickListener(new ButtonListener());//绑定监听器
}

class MyHandler extends Handler { 
//1.首先定义一个handler的实现类,继承Handler
@Override
public void handleMessage(Message msg) {
//2. 然后实现handleMessage方法

System.out.println("handleMessage--->" + Thread.currentThread().getName()); // 测试说明:打印当前线程的名字

String s= (String)msg.obj; // 8. 在主线程中将数值取出
textView.setText(s); //在主线程中操作UI

}
}

class ButtonListener implements OnClickListener {

@Override
public void onClick(View v) {
Thread t = new NetWorkThread();
t.start();
}
}

class NetworkThread extends Thread {
//本线程为Worker Thread
//@Override
public void run(){

System.out.println("network--->" + Thread.currentThread().getName()); // 测试说明:打印当前线程的名字

//模拟访问网络,所以当线程运行时,首先休眠2秒
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//变量s的值,模拟从网络中获取的数据
String s = "网络中的数据";
//textView.setText(s);//这样的操作是错误的,安卓系统中UI操作原则上只能在主线程中进行

Message msg = handler.obtainMessage(); // 5.生成Message对象
//sendMessage方法无论在主线程中发送还是在Worker Thread中发送都可以,这里在WorkerThread当中发送,然后在主线程将消息取出。
msg.obj = s; // 6. 把s赋值给obj
handler.sendMessage(msg); // 7. 调用sendMessage方法发送消息

}
}

}


參考資料:






----------------------------------------------------------------------
3. 安全中止一個Thread
----------------------------------------------------------------------
class MyThread implements Runnable{
      public void run() {
       // TODO Auto-generated method stub
       //  定义自己的变量
      while(!Thread.currentThread().isInterrupted()){
                try { 
                       doSomeThingNeed();//需要更新的操作   
                                                         //休眠控制最大帧率为每秒3绘制30次
                       Thread.sleep(30);
                } catch (Exception e) {
                       // TODO: handle exception
                       Log.v(tag2, "DrawSurfaceView:绘制失败...");
                       Thread.currentThread().interrupt();
                } 
       }
}


參考資料:







----------------------------------------------------------------------
4. Thread傳Message到Handler
----------------------------------------------------------------------
Thread中並沒有辦法改變畫面上的任何UI,所以會使用Handler來完成UI更新的目的
在 Android 中,Activity 可透過 Handler 傳遞Message 去切換不同的 UI 介面 (setContentView( ViewSurfaceViewR.Layout.xml_file )
但在計算中並不是都會正確的完成,所以在Handler中也必須判斷Thread的執行狀況來做出不同的反應,那麼就必須依靠Message來完成兩者間的資訊傳遞
1. 建立一個Thread,並傳遞指定相關資訊到Message中,利用handler.sendMessage傳遞出去。
Thread thread = new Thread(){  @Override  public void run() {  Message message;
  String obj = "OK";  message   handler.obtainMessage(1,obj);  handler.sendMessage(message);
  }
thread.start();
thread = null;
2. handler中依照傳遞的訊息做出相對應的反應
private Handler DOFindAttributehandler = new Handler(){    @Override    public void handleMessage(Message msg) {          super.handleMessage(msg);
          String MsgString = (String)msg.obj;
          if (MsgString.equals("OK"))          {
          ......
          } 
     }
};
P.S Override 為覆寫繼承之父類別中的方法,若覆寫完後仍須使用原方法時,即用super (指向繼承之父類別)


參考資料:


 - 範例:500毫秒,TextView 向上計數一次 : http://givemepass.blogspot.tw/2011/11/threadhandler.html
在 main thread 執行時,會產生 ANR (Application is Not Responding)的對話框,並且終止程式
text = (TextView)findViewById(R.id.textView);
int i =0;
while(true){ // 無窮迴圈造成 ANR
    i++;
    text.setText(Integer.toString(i));
}

         new Thread(new Runnable(){
                @Override        
                public void run() {
                // TODO Auto-generated method stub          
                while(true){ // 保護機制
                    try{
                        Message msg = new Message();
                        msg.what = 1;
                        mHandler.sendMessage(msg);
                        Thread.sleep(500); // 休息500ms
                    }
                   catch(Exception e){
                        e.printStackTrace();
                    }        
                }
              }           
           }).start();
             text = (TextView)findViewById(R.id.textView);
             mHandler = new Handler(){
             int i = 0;
             @Override
             public void handleMessage(Message msg) {
                    switch(msg.what){
                    case 1:
                            i++;
                            text.setText(Integer.toString(i));
                    break;
                    }
                    super.handleMessage(msg);
                } 
            };


參考資料:







-----------------------------------
-----------------------------------
5. 遇到問題
----------------------------------------------------------------------
  1. thread 在 interrup 後就不能重新 start,因為被 interrup過的 thread 會被移動到 待銷燬的記錄中,而這記錄不能更動
  2. 當重新 new 相同 thread 的時候,程式會因為 待銷燬記錄中 有此 thread ,而造成 thread 的執行出現 例外狀況 並跳出
  3. 我之前的 interrup 是寫在 畫布 destroyed 裡面,而會造成畫布的 destroyed 就是因為 intent activity 會使原activity finish 並 destroyed surfaceview
  4. 除非要整個離開跟destroyed 整個程式,不然surfaceView的 thread 必須一直在背景

沒有留言:

張貼留言