Android中的 事件流----浅析安卓中的动与静(三) 线程间通讯
2016-10-01 18:00
330 查看
任何程序都是静态代码,我们把这些静态代码打包好,然后放到运行环境当中,通过事件流的驱动使这些代码运行起来。Android的环境也不例外。
静态的代码,在动态事件的驱动下,才会有效的运转起来。
驱动Android程序运行起来的事件大致可以分为以下几种:
用户事件:如点击屏幕,滑动等各种手势;
系统事件:如屏幕方向的转变;
线程通讯事件:线程之间互发消息,程序根据消息内容进行相应的响应;
进程通讯事件:这里的进程包括本程序开启的进程,也包括其他应用程序的进程。
下面来介绍动态事件驱动的第三种:线程通讯事件流
线程通讯事件流
线程通讯也是造成Android动态化的一个重要方面,当一个UI主线程收到其他线程发过来的消息,可以动态更改自己的页面。
下面总结下线程之间通讯的几种方式:
(从重要性和实用角度排序)
1.使用Handler实现
2.使用AsyncTask
3.Activity.runOnUiThread(Runnbale)
4.View.post(Runnbale)
5.View.postDelayed(Runnalbe,long)
2-5其实内部实现都是Handler。
下面一一介绍用法:
1.使用Handler实现
先来讲Handler的实现,因为后边的四个都是基于Handler的实现的,谈到Handler的运行机制,不得不提Looper、Message、MessagerQueue了,它们之间的关系,下边这个图片描述的很清楚了。
用一句话总结就是在主线程中定义Handler对象,然后在子线程中调用这个Handler对象将封装好的Message对象发送到主线程中Looper管理的MessageQueue中。上边两句话是说在子线程中向主线程发送消息。那我如果想从主线程向子线程发送消息呢?或者子线程之间发送消息呢?其实还是一个道理。线程A要想给线程B发送消息,就要获取线程B中的Handler对象,然后通过此对象向线程B中的Looer中的MessageQueue中发送消息。注意,Looper和Handler是一一对应的。一个handler只能向其所在的线程发送Message消息。只不过UI线程的Looper是自动启动好的,其他线程要想享受到Looper的服务,必须通过 Looper.prepare()和Looper.loop();自行启动。下边写了一个demo,以后可以学习参考:
2.使用AsyncTask
关于AsyncTask的使用网上已经有很多资料这里不再详述,只是简单介绍下用法,以及其内部与handler的关系。
AsyncTask屏蔽了很多多线程的实现细节,很适合初学者使用,现在通过代码来介绍:
AsyncTask内部实际上new了一个Handler。如下图:
再来看下InternalHandler的定义:
显而易见,InternalHandler继承自Handler。
重点不是Handler在哪个类中new的,而是只要是new了Handler,就可以通过Handler向创建Handler的那个线程中发送Message消息。
这就是为什么使用AsyncTask时必须在UI线程中创建的原因。
大概的原来不再赘述,原理与上一部分Handler的运行机制大同小异。
3.Activity.runOnUiThread(Runnbale)
在子线程中调用Activity.runOnUiThread(Runnbale)方法,在传过来的Runnbale参数的run方法里更新主线程UI。具体可以参考以下代码:
注意为了保证线程安全,要更改的UI控件必须是final的。为什么说Activity.runOnUiThread(Runnbale)内部是用Handler实现的呢?我们来看下runOnUiThread(Runnbale)的源码就清楚了。
看到mHandler对象了吗?我们来看下它的定义:
所以是不是很清楚了。不管Handler在哪个类中new出来,用Handler传值都会传到创建Handler的那个线程中,重点的不是在哪个类中new,而是Handler在哪个线程中。
4.View.post(Runnbale)
其实这个的用法和上边讲的很类似。在子线程中调用某个view的post(Runnbale)方法,在传过来的Runnbale参数的run方法里更新主线程UI。具体可以参考以下代码:
为什么可以这样调用,因为View中也定义了Handler的对象,看下边的源码:
找到mHandler的定义:
5.View.postDelayed(Runnalbe,long)
和上边的类似,无非是延迟一些毫秒数来执行Runnable里面的方法,如View.postDelayed(Runnalbe,2000)是延迟2秒执行。
欢迎大家留言讨论。
后边将介绍造成安卓动态化的第三种因素:进程之间的通讯。
静态的代码,在动态事件的驱动下,才会有效的运转起来。
驱动Android程序运行起来的事件大致可以分为以下几种:
用户事件:如点击屏幕,滑动等各种手势;
系统事件:如屏幕方向的转变;
线程通讯事件:线程之间互发消息,程序根据消息内容进行相应的响应;
进程通讯事件:这里的进程包括本程序开启的进程,也包括其他应用程序的进程。
下面来介绍动态事件驱动的第三种:线程通讯事件流
线程通讯事件流
线程通讯也是造成Android动态化的一个重要方面,当一个UI主线程收到其他线程发过来的消息,可以动态更改自己的页面。
下面总结下线程之间通讯的几种方式:
(从重要性和实用角度排序)
1.使用Handler实现
2.使用AsyncTask
3.Activity.runOnUiThread(Runnbale)
4.View.post(Runnbale)
5.View.postDelayed(Runnalbe,long)
2-5其实内部实现都是Handler。
下面一一介绍用法:
1.使用Handler实现
先来讲Handler的实现,因为后边的四个都是基于Handler的实现的,谈到Handler的运行机制,不得不提Looper、Message、MessagerQueue了,它们之间的关系,下边这个图片描述的很清楚了。
用一句话总结就是在主线程中定义Handler对象,然后在子线程中调用这个Handler对象将封装好的Message对象发送到主线程中Looper管理的MessageQueue中。上边两句话是说在子线程中向主线程发送消息。那我如果想从主线程向子线程发送消息呢?或者子线程之间发送消息呢?其实还是一个道理。线程A要想给线程B发送消息,就要获取线程B中的Handler对象,然后通过此对象向线程B中的Looer中的MessageQueue中发送消息。注意,Looper和Handler是一一对应的。一个handler只能向其所在的线程发送Message消息。只不过UI线程的Looper是自动启动好的,其他线程要想享受到Looper的服务,必须通过 Looper.prepare()和Looper.loop();自行启动。下边写了一个demo,以后可以学习参考:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { final Handler mHandler=new Handler(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyButton er=(MyButton) findViewById(R.id.ddd); Log.v("onCreate", "onCreate"); er.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.v("onClick", v.toString()); Intent sub= new Intent(MainActivity.this, SubActivity.class); startActivity(sub); } }); final Myrunnable murMyrunnable=new Myrunnable(); new Thread(murMyrunnable).start(); new Thread(new Runnable() { //延迟两秒 public Handler mHandler; public void run() { Looper.prepare(); Looper.loop(); mHandler = new Handler() { public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, "wewe"+msg.what, Toast.LENGTH_SHORT).show(); } }; } }).start(); //创建一个线程 new Thread(new Runnable() { @Override public void run() { //延迟两秒 try { Thread.sleep( 5000 ); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.post(new Runnable() { // @Override public void run() { er.setText("变了"); Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show(); //创建一个线程 Message sdfMessage=new Message(); sdfMessage.what=12 ; murMyrunnable.mHandler.sendMessage(sdfMessage); } }); } }).start(); //er.setClickable(false); } class Myrunnable implements Runnable{ //延迟两秒 public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, "wewe"+msg.what, Toast.LENGTH_SHORT).show(); } }; Looper.loop(); } } }
2.使用AsyncTask
关于AsyncTask的使用网上已经有很多资料这里不再详述,只是简单介绍下用法,以及其内部与handler的关系。
AsyncTask屏蔽了很多多线程的实现细节,很适合初学者使用,现在通过代码来介绍:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { final Handler mHandler=new Handler(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyButton er=(MyButton) findViewById(R.id.ddd); Log.v("onCreate", "onCreate"); er.setOnClickListener(new OnClickListener() { @Override public void onClick(View v){MyAsyncTask myAsyncTask = new MyAsyncTask(MainActivity.this); myAsyncTask.execute(20); } }); } /* * integer:启动任务执行的输入参数,integer:后台任务完成的进度值的类型;String:后台执行任务完成后返回结果的类型 * 这三个参数可以根据需要进行设定 */ class MyAsyncTask extends AsyncTask<Integer, Integer, String>{ ProgressDialog pDialog; Context mContext; public MyAsyncTask( Context mContext){ this.mContext=mContext; } //核心函数,可以理解为在子线程中执行 @Override protected String doInBackground(final Integer... params) { Integer percent=params[0]; while (percent<101) { percent++; publishProgress(percent); Log.v("percent", percent+""); } return null; } /* * 上个函数的结果作为参数,传给result形参,函数内部执行更新UId的更新操作,可以理解为在UI线程中执行 * @see android.os.AsyncTask#onPostExecute(java.lang.Object) */ @Override protected void onPostExecute(String result) { // TODO Auto-generated method stub super.onPostExecute(result); } /* * 在执行第一个函数之前执行,做一些准备工作,可以理解为在UI线程中执行 * @see android.os.AsyncTask#onPreExecute() */ @Override protected void onPreExecute() { //这里的准备工作是显示进度条 pDialog=new ProgressDialog(mContext); pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pDialog.show(); } /* * 可以在第一个函数中调用,更新新进度注意是可以,无需求可以不用使用。 * @see android.os.AsyncTask#onProgressUpdate(java.lang.Object[]) */ @Override protected void onProgressUpdate(Integer... values) { pDialog.setProgress(values[0]); } }
AsyncTask内部实际上new了一个Handler。如下图:
再来看下InternalHandler的定义:
private static class InternalHandler extends Handler { @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } }
显而易见,InternalHandler继承自Handler。
重点不是Handler在哪个类中new的,而是只要是new了Handler,就可以通过Handler向创建Handler的那个线程中发送Message消息。
这就是为什么使用AsyncTask时必须在UI线程中创建的原因。
大概的原来不再赘述,原理与上一部分Handler的运行机制大同小异。
3.Activity.runOnUiThread(Runnbale)
在子线程中调用Activity.runOnUiThread(Runnbale)方法,在传过来的Runnbale参数的run方法里更新主线程UI。具体可以参考以下代码:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyButton er=(MyButton) findViewById(R.id.ddd); //创建一个线程 new Thread(new Runnable() { @Override public void run() { //延迟两秒 try { Thread.sleep( 5000 ); } catch (InterruptedException e) { e.printStackTrace(); } runOnUiThread(new Runnable() { @Override public void run() { er.setText("变了"); Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show(); } }); } }).start(); //er.setClickable(false); } }
注意为了保证线程安全,要更改的UI控件必须是final的。为什么说Activity.runOnUiThread(Runnbale)内部是用Handler实现的呢?我们来看下runOnUiThread(Runnbale)的源码就清楚了。
/** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. * * @param action the action to run on the UI thread */ public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
看到mHandler对象了吗?我们来看下它的定义:
final Handler mHandler = new Handler();
所以是不是很清楚了。不管Handler在哪个类中new出来,用Handler传值都会传到创建Handler的那个线程中,重点的不是在哪个类中new,而是Handler在哪个线程中。
4.View.post(Runnbale)
其实这个的用法和上边讲的很类似。在子线程中调用某个view的post(Runnbale)方法,在传过来的Runnbale参数的run方法里更新主线程UI。具体可以参考以下代码:
new Thread(new Runnable() { @Override public void run() { //延迟两秒 try { Thread.sleep( 5000 ); } catch (InterruptedException e) { e.printStackTrace(); } er.post(new Runnable() { @Override public void run() { er.setText("变了"); Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show(); } }); } }).start();
为什么可以这样调用,因为View中也定义了Handler的对象,看下边的源码:
找到mHandler的定义:
5.View.postDelayed(Runnalbe,long)
和上边的类似,无非是延迟一些毫秒数来执行Runnable里面的方法,如View.postDelayed(Runnalbe,2000)是延迟2秒执行。
欢迎大家留言讨论。
后边将介绍造成安卓动态化的第三种因素:进程之间的通讯。
相关文章推荐
- Android中的 事件流----浅析安卓中的动与静(四) 进程间通讯
- Android中的 事件流----浅析安卓中的动与静(一) 用户事件流
- Android中的 事件流----浅析安卓中的动与静(二) 系统事件流
- 增加 addDataScheme("file") 才能收到SD卡插拔事件的原因分析 -- 浅析android事件过滤策略
- 14天学会安卓开发(第十天)Android网络与通讯
- Android中Handler的线程间通讯原理
- 增加 addDataScheme("file") 才能收到SD卡插拔事件的原因分析 -- 浅析android事件过滤策略
- android不同线程间通讯~
- 【Android开发】线程间通讯机制(提高篇)——深入浅出实现原理
- 增加 addDataScheme("file") 才能收到SD卡插拔事件的原因分析 -- 浅析android事件过滤策略
- 转:浅析Android单线程模型
- Android:线程间通讯的其他方法、runOnUiThread(action)、Handler.post(action)、post
- android 线程杀死 激活打电话 webkit的 cookie 内容存放在 Button1点击相应button2的事件
- Android蓝牙通讯模块源码(Android蓝牙开发浅析 续)
- android thread线程通讯
- 通过addDataScheme("file") 浅析android事件过滤策略
- 增加 addDataScheme("file") 才能收到SD卡插拔事件的原因分析 -- 浅析android事件过滤策略
- 在VC++6.0下如何用事件与线程通讯使线程同步和终止
- 增加 addDataScheme("file") 才能收到SD卡插拔事件的原因分析 -- 浅析android事件过滤策略
- android 线程间通讯