任何程序都是静态代码,我们把这些静态代码打包好,然后放到运行环境当中,通过事件流的驱动使这些代码运行起来。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,以后可以学习参考:
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秒执行。
欢迎大家留言讨论。
后边将介绍造成安卓动态化的第三种因素:进程之间的通讯。