多线程
有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口
继承Thread Demo
class MyThread extends Thread {
private String name;
private int ticket;
public MyThread(String name, int ticket) {
super();
this.name = name;
this.ticket = ticket;
}
@Override
public void run() {
while (this.ticket > 0) {
//加上休眠是为了更好的看出效果
try {
Log.e("线程开始: " + name, " 卖票:ticket " + this.ticket--);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
启动线程:
MyThread mt1 = new MyThread("线程a", 10);
MyThread mt2 = new MyThread("线程b", 15);
MyThread mt3 = new MyThread("线程c", 20);
mt1.start();
mt2.start();
mt3.start();
通过打印的log,我们可以看出,线程启动的先后顺序并不相互影响
实现Runnable接口 Demo
class MyRunnable implements Runnable {
private String name;
private int ticket;
public MyRunnable(String name, int ticket) {
super();
this.name = name;
this.ticket = ticket;
}
@Override
public void run() {
while (this.ticket > 0) {
//加上休眠是为了更好的看出效果
try {
Log.e("线程开始: " + name, " 卖票:ticket " + this.ticket--);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
启动线程:
MyRunnable myRunnable = new MyRunnable("线程d", 45);
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
通过比较第一种和第二种线程的打印的log,我们可以发现第一种启动方式,是每个线程分配多少任务就完成多少任务,并不会共享资源;而第二种方式,共享一个资源myRunnable
的票数ticket
。
通俗来讲,第一种方式是大家各自做各自的事情,各不影响;第二种方式是大家一起做某件事情,也是互不影响各自的进程,数学比较好的各位应该也就可以看出,第二种方式的效率要较高一些。
除此之外,我们都知道在代码里我们只可以继承一个类,但是可以实现多个接口,由于以上两点,我们推荐使用Runnable
方式实现多线程的目的
拓展 –Handler
更新UI需要在主线程里进行,网络请求等耗时操作需要在子线程里实现,那么我如何在耗时操作完成后去更新UI呢,这就需要Handler了,直接看Demo
new Thread(new Runnable() {
@Override
public void run() {
//TODO 耗时操作
Bundle bundle = new Bundle();
bundle.putInt("key", 10);
Message message = mHandler.obtainMessage();
message.what = 1;
message.setData(bundle);
message.sendToTarget();
}
}).start();
除了这种方式,还有其他的方式
//message由handler创建,可直接向handler发送消息
Message msg = mHandler.obtainMessage();
msg.sendToTarget();
//message通过new的方式创建,可用handler.sendMessage(msg)来发送消息
Message msg = new Message();
mHandler.sendMessage(msg);
//message通过Message.obtain的方式创建,可用sendMessage(msg)来发送消息
Message msg = Message.obtain();
mHandler.sendMessage(msg);
那么Message message = mHandler.obtainMessage()
和Message message = new Message()
有什么区别呢:
obtainmessage()
是从消息池中拿来一个msg
,不需要另开辟空间new
;
new
需要重新申请,效率低,obtianmessage
可以循环利用。
已经发送信息后,如何处理:
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 1) {
int result = msg.getData().getInt("key");
//TODO UI处理
Toast.makeText(MainActivity.this, "结果为" + result, Toast.LENGTH_SHORT).show();
}
return false;
}
});
在这边也有个小小的拓展:
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
}){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
public boolean handleMessage
和public void handleMessage
有什么区别呢:
我查了一下,主要的区别的boolean handleMessage
的返回值决定继续传递给void handleMessage
处理信息
如果不使用Callback()
的boolean handleMessage
,我们是默认消息会传递给void handleMessage
处理信息
但是使用起来有什么区别,我现在还没有看出来,如果有知道的,也可以告诉我一下,互相学习学习。
除了使用Handler
来更新UI
,还有一下几种方式更新UI
// 使用 mHandler.post
new Thread(new Runnable() {
@Override
public void run() {
//TODO 耗时操作
mHandler.post(new Runnable() {
@Override
public void run() {
//TODO 更新UI
}
});
}
}).start();
// runOnUiThread
runOnUiThread(new Runnable() {
@Override
public void run() {
//TODO 更新UI
}
});
// 控件的post
ImageView.post(new Runnable() {
@Override
public void run() {
//TODO 更新UI ImageView
}
});
异步处理 AsnyncTask
直接上网络加载图片的Demo
public class DownImage extends AsyncTask<String, Void, Bitmap> {
private ImageView imageView;
public DownImage(ImageView imageView) {
this.imageView = imageView;
}
@Override
protected Bitmap doInBackground(String... params) {
//TODO 在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。负责执行那些很耗时的操作。必重写
String url = params[0];
Bitmap bitmap = null;
try {
InputStream is = new URL(url).openStream();
bitmap = BitmapFactory.decodeStream(is);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
//TODO 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread。必重写
imageView.setImageBitmap(result);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
//TODO 执行实际的后台操作前被UI thread调用,可以做一些准备工作,如显示进度条,按需求决定是否重写
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
//TODO UI thread展示任务的进展情况,可以通过一个进度条进行展示,按需求决定是否重写
}
}
调用:
new DownImage(imageview).execute(url);
解释:
AsyncTask的三个泛型参数说明(三个参数可以是任何类型)
demo :
public class DownImage extends AsyncTask<String, Void, Bitmap>{
...
}
第一个参数:传入doInBackground()
方法的参数类型
第二个参数:传入onProgressUpdate()
方法的参数类型
第三个参数:传入onPostExecute()
方法的参数类型,也是doInBackground()
方法返回的类型。
AsyncTask遵守准则 :
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
- Task的实例必须在UI thread中创建
- execute方法必须在UI thread中调用
- 不要手动的调用onPreExecute(),
onPostExecute(Result),doInBackground(Params…),
onProgressUpdate(Progress…)这几个方法
参考文章:AsyncTask异步处理