简单介绍一些Android的多线程机制:
我们在Android的开发中,要遵守的是 "单线程模式":
1.UI线程就是主线程,后台任务不能出现在主线程中,也就是不能阻塞UI线程
2.当我们需要UI更新的时候,这部分工作必须在主线程中完成, 而不能在其它工作线程中,也就是不要再主线程之外访问Android的UI组件包
引用自技术大牛:
" Android开发必须满足这两个要素,否则程序就会崩溃,Android开发中既有前台与用户的操作(UI的更新),也有后台与数据的连接(后台任务),比如你有一个页面,用户一点击查询,从后台数据库查询数据,返回给前台页面,页面再将数据罗列展示给用户,在这个过程中涉及到了前后台的交互,如果在点击查询的事件中直接调用select数据库的方法,程序就会崩溃。正确的做法应该是:点击查询后,开启子线程,在子线程里调用访问数据库的方法,然后子线程再将拿到的数据发送给主线程,在主线程中进行展示。 "
接下来再说到这几个概念:
什么是Message?
Message是一种消息体,用于装载需要发送的对象,Android中子线程与主线程之间通信的时候,就需要通过消息来传递,你可以理解为子线程与主线程之间进行“聊天”时所发送的“聊天消息”。
什么是MessageQueue?
MessageQueue消息队列是用来存放所有消息的,遵循先进先出规则(FIFO),如果一个线程需要接收来自其它线程的消息,则必须为其创建一个消息队列,将接收到的消息丢进这个队列中,当需要时再取出来。
什么是Looper?
Looper就相当于管理者的角色,管理当前所属线程的MessageQueue,循环不断地管理MessageQueue接收和分发Message。
什么是Handler?
Handler则相当于处理者的角色,处理和接收Looper派发出来的消息。
Handler是用一个Looper来初始化,在主线程中, 如果构造函数中没有传入任何参数,那么默认使用当前主线程(即UI线程)来初始化该Handler;在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。
下面是引用自技术大牛的部分:
我们通过一段简单的代码片段来分析整个流程:
- public class HandlerActivity extends Activity {
-
-
- private Handler mainhandler;
-
- private Thread myThread;
-
- private TextView textView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_handler);
-
- textView = (TextView)this.findViewById(R.id.hello);
-
- mainhandler = new Handler(){
- public void handleMessage(android.os.Message msg) {
- switch(msg.what){
- case 1:
- textView.setText(msg.what+"已经取到数据");
- }
- };
- };
-
- myThread = new Thread(new Runnable(){
-
- @Override
- public void run() {
-
- Message msg = new Message();
- msg.obj = "取到的数据:hello";
- msg.what = 1;
- mainhandler.sendMessage(msg);
- }
-
- });
- myThread.start();
-
- }
-
-
- }
可以看到,首先在主线程中创建了一个mainhandler,用来处理和接收子线程发送过来的消息。再创建了一个子线程myThread,在子线程中进行了访问数据库的操作,访问完成后让message将数据携带上,在这里模拟数据为"取到的数据:hello",通过msg.obj将数据给message,msg.what是标志位,因为本例只有一个子线程,当有多个子线程的时候,handler需要辨识消息是属于哪个子线程发送的,就是通过what变量来分别。mainhandler.sendMessage(msg)是通过mainhandler将消息发送给主线程,在主线程中,通过handleMessage(android.os.Message msg)方法接收到消息,这里的参数正是子线程发送过来的消息,然后再通过switch分支处理消息,并进行UI更新-->textView.setText(msg.what+"已经取到数据")。
你会问,刚才的流程哪里有提到Looper以及MessageQueue?
其实,在刚才的流程中,只涉及了一个子线程,当有多个子线程时,主线程就需要与多个子线程进行交互,这个时候主线程接收到的消息就不止一两条,那么这些消息放在哪里呢?就放在MessageQueue中,而刚才说到handler是用来接收和处理消息的,其实handler并不是直接处理message,而是通过Looper来处理,Looper不断地从MessageQueue中循环取消息,一旦发现MessageQueue中有消息,就将其派发给handler去处理。
关于Looper.prepare()和Looper.loop()方法:
在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);
在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息
。
Android中如果在子线程中新建Handler实例并在其handlerMessage方法中更新UI,则会报错,因为
android中只有主线程是默认带有Looper对象和消息队列的
,而子线程是没有的,需要自己生成,所以只需要在生成handler实例前调用Looper.prepare()方法,在生成handler实例后调用Looper.loop()方法即可。
其中,Looper.prepare()表示为当前子线程创建一个消息队列,Looper.loop()表示不断地从消息队列中取数据
在一个Thread中Looper也是唯一的,一个Thread对应一个Looper,建立Handler的Looper来自哪个Thread,Handler就属于哪个Thread。如果子线程也需要接收消息时,则需要创建一个消息队列,即在子线程中Looper.prepare()/Looper.loop()
如果在子线程中,新建的handler是:handler = new Handler(Looper.getMainLooper())则表示等下handler发送的消息都仍然是发给主线程,因为现在它所关联的looper依旧是主线程的looper
如果在子线程中,新建的handler是:handler = new Handler(Looper.myLooper())则表示这个handler发送的消息都是发给当前子线程,因为现在它所关联的looper是当前线程的looper
当然,Android还为我们提供了一个HandlerThread类,用来开启一个含Looper对象的线程