(每次学习一点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