进来这里的朋友相信是有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是线程间通讯的消息载体。它可以携带少量的信息,用于在不同线程之间交换数据。
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);
建议使用
ScheduledExecutorService替换timer使用,效果更好。
ScheduledExecutorService mService = Executors.newScheduledThreadPool(5);
mService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
//System.out.println("-------定期任务执行--------");
Thread.sleep(1000);
Log.e("TAG-MainActivity", "run: setSSSS=88888" );
LogoQQHelp.doLogoQQHelp(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 20, 20, TimeUnit.SECONDS);
好了,如果大家还有好的方法,不妨留下言,当当雷锋也是不错啊! 谢谢!