进来这里的朋友相信是有Java线程基础的,当然,可以点击(浅谈Java多线程)回去叙叙旧。废话不多说,其实,Android多线程编并不比Java多线程编程特殊,基本都是使用相同的语法。不同的是Android多线程编程自己搞出了一套异步消息处理机制。还是先来回忆一下Java线程的基本使用吧!
//Java两种创建线程的方法 //创建一个类继承Thread class MyThread extends Thread{ @Override public void run() { //处理具体的逻辑 } } //启动线程 //new MyThread().start(); //更多时候我们会选择使用实现Runnable接口来定义一个线程 class MyThread implements Runnable{ @Override public void run() { //处理具体的逻辑 } } //启动线程 MyThread myThread=new MyThread(); new Thread(myThread).start(); //当然,这里也有偷懒的秘籍,如果你不想专门再定义一个类去 //实现Runnable接口,也可以使用匿名类的方式(很常见的写法)。 new Thread(new Runnable(){ @Override public void run() { //处理具体的逻辑 } }).start();
线程在Java中是处理一些耗时操作、异步、多任务下载等等。线程到了Android这个环境以后有了新的变化,Android使用线程更多的是为了用户有更好的体验,用户是有脾气的,他们不想等、不愿去思考、还不耐烦。还有就是Android在子线程中更新UI和Java更新UI有所不同。在Android SDK提供的API中,Google进行了多次封装,因此在Android里面是不能直接通过子线程去操作UI控件的,因为在Android框架里,线程是不安全的。为了解决这个问题,官方建议我们通过这Handler个类去实现UI更新。为了更好的理解,来个例子吧!在Android平台通过子线程更新UI界面:
package com.xhm.demo.providertestling; import android.app.Activity; import android.os.Bundle; import android.widget.ImageView; /** * Created by Dell on 2017/4/16. */ public class MyThread extends Activity { private ImageView mImageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); mImageView=(ImageView) findViewById(R.id.imageView); new Thread(new Runnable() { @Override public void run() { try{ //休眠三秒后切换墙纸 Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } mImageView.setBackgroundResource(R.drawable.bi3); } }).start(); } }
布局里就一个控件,不贴代码咯!然后运行之后看到报错(程序崩溃),这样可以看出Android确实不允许在子线程中去更新UI操作的。
不着急!我们先来学习异步消息处理机制。
Android中的异步消息处理主要由4个部分组成:
Handler、Message、MessageQueue、Looper。
1.Handler:Handler顾名思义就是处理者的意思,Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。发送消息是使用Handler的sendMessage()方法,而发出去的消息经过一系列的辗转处理后,最终又传递到Handler的handleMessage()方法中。
2.Message:Message是线程间通讯的消息载体。它可以携带少量的信息,用于在不同线程之间交换数据。
2.Message:Message是线程间通讯的消息载体。它可以携带少量的信息,用于在不同线程之间交换数据。
3.MessageQueue:MessageQueue是消息队列,先进先出,它的作用是保存有待线程处理的消息,也就是用于存放所有通过Handler发送的消息。这部分消息一直存放于消息列队中,等待被处理,每个线程只会有一个MessageQueue对象。
4.Looper:Looper是每个线程中的MessageQueue管家,Looper负责管理线程的消息队列和消息循环。每个线程也只有一个Looper对象。
异步消息处理流程图:
异步消息处理流程图:
好了,时候把上面的错误修改下咯!
package com.xhm.demo.projiect00; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.ImageView; public class MainActivity extends Activity { private ImageView mImageView; private static final int MESSAGE_ID=0x00000001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mImageView=(ImageView) findViewById(R.id.imageView); Button button=(Button) findViewById(R.id.buttons); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //创建一个子线程 new Thread(new Runnable() { @Override public void run() { try{ //休眠三秒后切换墙纸 Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } //通过mHandler对象的obtainMessage() //方法得到一个消息msg对象实例 Message msg=mHandler.obtainMessage(); //封装消息ID msg.what=MESSAGE_ID; //通过mHandler对象将消息发送出去 mHandler.sendMessage(msg); } }).start(); } }); } //创建一个Handler局部类对象 Handler mHandler=new Handler(){ @Override public void handleMessage(Message msg) { //得到封装的消息ID进行匹配 if (MESSAGE_ID==msg.what){ //更换ImageView的背景 mImageView.setBackgroundResource(R.drawable.bi3); } } }; }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/buttons" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="换墙纸"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageView" android:background="@drawable/bi2"/> </LinearLayout>
效果图从左到右。当然,除了切换墙纸外,计时器也是一个很好的例子,这个作业交给大家咯!好好写代码。
Handler.post:
handle的post方法也可以用来更新UI组件,因为handler的post方法也是在主线程运行的;使用实例代码如下:
Handler mHandler = new Handler();//首先创建一个Handler对象 private void initHandle() { mThread=new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(5000);//阻塞5秒 mHandler.post(new Runnable() { @Override public void run() { //更新墙纸 mImageView.setBackgroundResurce(R.drawable.bi3); } }); } catch (Exception e) { e.printStackTrace(); } } });//.start() }使用的时候在onCreate()方法或者需要的地方调用initHandle()即可。
Handler.postDelayed:
postDelayed和post方法大同小异,只是postDelayed比post高级一些,写法也简单一些。请看代码,一看便知。
//首先还是需要创建一个Handler对象 Handler mHandler=new Handler(); //这是一个类,当然,你可以写成一个方法,比如:Runnable mRunnable=new Runnable(){} private class mRunnable implements Runnable { @Override public void run() { //更新墙纸 mImageView.setBackgroundResurce(R.drawable.bi3); } } //这是调用postDelayed,意思是每隔五秒执行run方法 //和post一样,在onCreate()方法或者你需要的地方调用 mHandler.postDelayed(new mRunnable(),5000); //那么,如果你不想用了,可以这样操作 mHandler.removeCallbacks(runnable);
最后:
最后我们来一点题外话(其实是干货),扩展运动哈!话说没过几秒更新墙纸这种活,java里有个更简单的方法,我也是后来才知道的哈!往下看:
TimerTask task = new TimerTask() { public void run() { //execute the task //更新墙纸 mImageView.setBackgroundResurce(R.drawable.bi3); } }; Timer timer = new Timer(); //这里的意思是,从现在开始,每隔5秒执行此方法;简单的说,相当于定时器 timer.schedule(task, 5000);
好了,如果大家还有好的方法,不妨留下言,当当雷锋也是不错啊! 谢谢!