(每次學習一點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