xamarin學習筆記A14(安卓AsyncTask和RunOnUiThread)

(每次學習一點xamarin就做個學習筆記和視頻來加深記憶鞏固知識)

如有不正確的地方,請幫我指正。

簡介

    AsyncTask類和Activity.RunOnUiThread()方法都是對Hanlder消息處理的封裝。

    在android中 1.只能在子線程進行耗時任務。2.只能在主線程進行UI更新。 所以要使用異步消息處理機制

方法/

說明

作用

Activity.RunOnUiThread(回調方法)

Handler+Message的封裝

創建消息並關聯回調方法,發送到消息隊列。

AsyncTask

Handler+Message+Thread的封裝

創建消息和使用線程池子線程,發送消息到隊列,最後執行相應的回調方法。

先看Activity.RunOnUiThread()方法

    從android源碼中抽出主要部分如下

public class Activity {
    //......省略其它代碼......
    //1
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
}


public class Handler{
    //......省略其它代碼......

    //2
    public final boolean post(Runnable r)
    {
        return  sendMessageDelayed(getPostMessage(r), 0);
    }
    //3
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();//創建一個消息
        m.callback = r; //這裏設置了消息關聯的回調方法
        return m;
    }
    //4
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    //5
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {return false;}

        return enqueueMessage(queue, msg, uptimeMillis);
    }
    //6
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        return queue.enqueueMessage(msg, uptimeMillis);//將消息發送到消息隊列
    }
    //7 在主線程執行(自動被調用)
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);//調用消息對象關聯的回調方法
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    //8 在主線程執行(自動被調用)
    private static void handleCallback(Message message) {
        message.callback.run();//調用消息對象關聯的回調方法
    }
}

主要看標記的序號,執行runOnUiThread方法時先設用了post方法,post方法則調用的getPostMessage方法來創建了一個Message對象並關聯了回調方法,最後通過enqueueMessage將Message對象發送到了消息隊列。然後主線程的Looper對象自動從MessageQueue中取出消息並調用消息對象關聯的回調方法。

 

    舉個例子,我們可以這樣使用,貼上部分代碼:

private TextView textView;
private string address = "http://www.hao123.com";
public void OnClick(View v)
        {
            switch (v.Id)
            {
                //請求網絡數據(記得在AndroidManifest.xml的Applincation節點之上聲明網絡權限<uses-permission android:name="android.permission.INTERNET"/>)
                case Resource.Id.button1:
                    new Thread(new ThreadStart(
                            () =>
                            {
                                #region 子線程執行請求
                                HttpURLConnection conn = null;
                                try
                                {
                                    URL url = new URL(address);
                                    conn = (HttpURLConnection)url.OpenConnection(); //打開連接
                                    conn.RequestMethod = "GET";
                                    conn.DoInput = true;  //允許接收數據,以後就可以使用conn.InputStream

                                    Stream inStream = conn.InputStream; //實際請求數據
                                    string result;
                                    using (StreamReader reader = new StreamReader(inStream, Encoding.UTF8))
                                    {
                                        result = reader.ReadToEnd();
                                    }

                                    this.RunOnUiThread(
                                            //RunOnUiThread本質是對Handler異步消息處理的封裝,
                                            //它內部創建了一個消息對象,並把下面的匿名方法關聯到消息對象上,然後發送消息到隊列中(子線程中執行)
                                            //最後主線程運行的Looper對象會自動去從MessageQueue中取消息,並執行消息對象關聯的方法 (主線程中執行)
                                            () =>
                                            {
                                                Toast.MakeText(this, "請求成功", ToastLength.Short).Show();
                                                textView.Text = result;
                                            } //在主線程更新UI
                                        );                                }
                                catch (System.Exception e)
                                {
                                    Toast.MakeText(this, "請求出錯"+e.Message, ToastLength.Long).Show();
                                }
                                finally
                                {
                                    if (conn != null) conn.Disconnect();//關閉連接
                                }
                                #endregion
                            }
                        )).Start();
                    break;           
 }
}

接着看AsyncTask類

   就不抽取android源碼了,太多了也不太好怎麼抽取。它內部也同樣是對Handler消息機制的封裝,不需要像上面那樣要求自己創建線程,它內部幫我們創建了線程,而且還用了線程池來提高性能。

    打開這個類的定義,可以看到這是個抽像類,主要有五個方法讓我們來重寫。

//範型參數1是輸入參數的類型,參數2是進度值的類型,參數3是任務返回值類型
public abstract class AsyncTask<TParams, TProgress, TResult> : AsyncTask
    {
        public AsyncTask();

	    //啓動異步任務
        public AsyncTask<TParams, TProgress, TResult> Execute(params TParams[] @params);

	   //在異步任務開始之前被調用(在主線程)
protected virtual void OnPreExecute();
        
//(在子線程運行具體任務)
protected abstract TResult RunInBackground(params TParams[] @params);

        //進度有變化時被調用(在主線程)
        protected virtual void OnProgressUpdate(params TProgress[] values);

        //異步任務執行完後被調用(在主線程)
protected virtual void OnPostExecute(TResult result);

//任務被取消後調用
protected virtual void OnCancelled();
}

下面就來具體使用這個類,貼上部分代碼
//異步任務處理
    public class MyTask : AsyncTask<string, int, string >//參數1是輸入參數的類型,參數2是進度值的類型,參數3是任務返回值類型
    {
        public interface IMyTask
        {
            void OnFinish(string result);//定義一個回調方法
        }

        private IMyTask _myTask;
        public void SetLitener(IMyTask listener)//傳遞一個實現了IMyTask接口的對象來監聽任務結果
        {
            _myTask = listener;
        }

        //在異步任務開始之前被調用(在主線程)
        protected override void OnPreExecute()
        {
            Log.Debug("MyTask", "在異步任務開始之前OnPreExecute()被調用");
        }

        //(在子線程運行具體任務)
        //此方法內部會創建子線程Thread,然後執行任務,然後會發送Message到消息隊列。
        protected override string RunInBackground(params string[] @params)////因爲params和C#關鍵字params同名,所以要加 @ 符號
        {
            Log.Debug("MyTask", "RunInBackground()被調用");
            HttpURLConnection conn = null;
            try
            {
                string address = @params[0];//取出請求地址
                URL url = new URL(address);
                conn = (HttpURLConnection)url.OpenConnection(); //打開連接
                conn.RequestMethod = "GET";
                conn.DoInput = true;  //允許接收數據,以後就可以使用conn.InputStream

                Stream inStream = conn.InputStream; //實際請求數據
                string result;
                using (StreamReader reader = new StreamReader(inStream, Encoding.UTF8))
                {
                    result = reader.ReadToEnd(); 
                }
                return result;
            }
            catch
            {
                return "";
            }
            finally
            {
                if (conn != null) conn.Disconnect();//關閉連接
            }
        }

        //進度有變化時被調用(在主線程)
        //protected override void OnProgressUpdate(params int[] values)
        //{
        //    base.OnProgressUpdate(values);
        //}

        //異步任務執行完後被調用(在主線程)
        protected override void OnPostExecute(string result)
        {
            Log.Debug("MyTask", "異步任務執行完後OnPostExecute()被調用");
            if (_myTask!=null)
            {
                _myTask.OnFinish(result); //執行回調方法
            }
        }

        //任務被取消後調用
        //protected override void OnCancelled()
        //{
        //    base.OnCancelled();
        //}
    }

然後在Activity中調用
public class SecondActivity : AppCompatActivity, IOnClickListener, MyTask.IMyTask
    {
        private TextView textView;
        private string address = "http://www.hao123.com";
        public void OnClick(View v)
        {
            switch (v.Id)
            {
                case Resource.Id.button1:
                    MyTask myTask = new MyTask();
                    myTask.SetLitener(this);
                    myTask.Execute(address); //啓動異步任務
                    break;
            }
        }

        public void OnFinish(string result)
        {
            textView.Text = result; //顯示請求結果
        }
}

最後的結果如下圖



代碼和視頻在我上傳的CSDN資源中http://download.csdn.net/download/junshangshui/10048297

發佈了74 篇原創文章 · 獲贊 50 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章