android中的多线程
一、基于消息队列的handler机制,别的线程与自己进行交互,需要使用消息队列,这样可以实现功能的分离和处理的异步,
Android中对消息队列进行了封装,具体的:
1、Looper 负责维护并发安全的消息队列
2、handler 负责进行消息的入队、出队,以及消息的处理函数 void handleMessage(Message msg)
3、Message 是OS系统内抽象的数据结构
4、相互关系:handler操作的是具体Looper代表的消息队列(不指定时默认是当前线程的looper),并且每个线程有自己的消息队列,因此别的线程可以通过handler来操作主线程
5、Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的 Looper对象
6、注意:Looper对message的处理是同步的,即一个handler会阻塞别的looper,且会阻塞后面所有消息的处理,所以注意 handleMessage中不能有耗时的操作(可以做完后发送消息来实现)
7、post过去的Runnable是接口,不会再创建新的线程
8、Looper.Callback 以Runnable为接口
1、handler机制是常见的消息队列的方式,但是每个回调都是同步的,例子如下(代码来自网络,但不知道是谁写的):
package com.weitm.testhandler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.app.Activity;
import android.graphics.Color;
import android.util.Log;
import android.view.Menu;
import android.widget.*;
import android.view.*;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
public class MainActivity extends Activity implements OnClickListener
{
private String TAG = "HandlerTest";
private boolean bpostRunnable = false;
private NoLooperThread noLooperThread = null; // 普通线程
private OwnLooperThread ownLooperThread = null; // 有自己消息队列的线程
private ReceiveMessageThread receiveMessageThread = null; // 主线程内的子线程(拥有自己的消息队列)
private Handler mOtherThreadHandler = null; // 主线程内的子线程对应的handler
private EventHandler mHandler = null;
private Button btn1 = null;
private Button btn2 = null;
private Button btn3 = null;
private Button btn4 = null;
private Button btn5 = null;
private Button btn6 = null;
private TextView tv = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(250, 50);
layout.setOrientation(LinearLayout.VERTICAL);
btn1 = new Button(this);
btn1.setId(101);
btn1.setText("UI thread 发给自己");
btn1.setOnClickListener(this);
layout.addView(btn1, params);
btn2 = new Button(this);
btn2.setId(102);
btn2.setText("別人发给UI thread");
btn2.setOnClickListener(this);
layout.addView(btn2, params);
btn3 = new Button(this);
btn3.setId(103);
btn3.setText("别人发给自己msg");
btn3.setOnClickListener(this);
layout.addView(btn3, params);
btn4 = new Button(this);
btn4.setId(104);
btn4.setText("别人发给UI runnable");
btn4.setOnClickListener(this);
layout.addView(btn4, params);
btn5 = new Button(this);
btn5.setId(105);
btn5.setText("主线程发给自己的子线程");
btn5.setOnClickListener(this);
layout.addView(btn5, params);
btn6 = new Button(this);
btn6.setId(106);
btn6.setText("exit");
btn6.setOnClickListener(this);
layout.addView(btn6, params);
tv = new TextView(this);
tv.setTextColor(Color.RED);
tv.setText("");
params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
params.topMargin = 10;
layout.addView(tv, params);
setContentView(layout);
// 不使用默认的activity内的Looper和handler,而是使用自定义的,以便与主线程相区分
receiveMessageThread = new ReceiveMessageThread();
receiveMessageThread.start();
}
class ReceiveMessageThread extends Thread
{
@Override
public void run()
{
Looper.prepare(); // 有了自己的消息队列
// 最直接的处理自己的消息队列,但是这个不同于主线程
mOtherThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
Log.e(TAG, "-------+>" + (String) msg.obj);
Log.e(TAG, "CurrentThread id:----------+>"+ Thread.currentThread().getId());
}
};
Log.e(TAG, "ReceiveMessageThread id:--------+>" + this.getId());
Looper.loop(); // 开始事件循环
}
}
class NoLooperThread extends Thread
{
private EventHandler mNoLooperThreadHandler;
@Override
public void run()
{
Looper myLooper = Looper.myLooper();
Looper mainLooper = Looper.getMainLooper();
String msgobj;
if (null == myLooper)
{
// 这里获得的是主线程的Looper,由于NoLooperThread没有自己的looper所以这里肯定会被执行
mNoLooperThreadHandler = new EventHandler(mainLooper);
msgobj = "NoLooperThread has no looper and handleMessage function executed in main thread!";
} else
{
mNoLooperThreadHandler = new EventHandler(myLooper);
msgobj = "This is from NoLooperThread self and handleMessage function executed in NoLooperThread!";
}
mNoLooperThreadHandler.removeMessages(0);
if (bpostRunnable == false)
{
// send message to main thread
Message msg = mNoLooperThreadHandler.obtainMessage(2, 1, 1, msgobj);
mNoLooperThreadHandler.sendMessage(msg);
Log.e(TAG, "NoLooperThread id:--------+>" + this.getId());
} else
{
// 下面new出来的实现了Runnable接口的对象中run函数是在Main
// Thread中执行,不是在NoLooperThread中执行 记得 null == myLooper么
// 注意Runnable是一个接口,它里面的run函数被执行时不会再新建一个线程
// 您可以在run上加断点然后在eclipse调试中看它在哪个线程中执行
mNoLooperThreadHandler.post(new Runnable() {
public void run()
{
// TODO Auto-generated method stub
tv.setText("update UI through handler post runnalbe mechanism!");
// 这里打印出来的是main thread的线程id,也就是说Runnable在handler机制中只是作为回调接口
// 并没有创建新的线程
Log.e(TAG, "update UI id:--------+>"+ Thread.currentThread().getId());
noLooperThread.stop();
}
}); // end of post
} // end of else
}
}
class EventHandler extends Handler
{
public EventHandler(Looper looper)
{
super(looper);
}
public EventHandler()
{
super();
}
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
Log.e(TAG, "CurrentThread id:----------+>" + Thread.currentThread().getId());
switch (msg.what)
{
case 1:
tv.setText((String) msg.obj);
break;
case 2:
tv.setText((String) msg.obj);
noLooperThread.stop();
break;
case 3:
// 不能在非主线程的线程里面更新UI,所以这里通过log打印信息
Log.e(TAG, (String) msg.obj);
ownLooperThread.stop();
break;
default:
Log.e(TAG, (String) msg.obj);
break;
}
}
}
class OwnLooperThread extends Thread
{
private EventHandler mOwnLooperThreadHandler = null;
@Override
public void run()
{
Looper.prepare();
Looper myLooper = Looper.myLooper();
Looper mainLooper = Looper.getMainLooper();
String msgobj;
if (null == myLooper)
{
mOwnLooperThreadHandler = new EventHandler(mainLooper);
msgobj = "OwnLooperThread has no looper and handleMessage function executed in main thread!";
} else
{
mOwnLooperThreadHandler = new EventHandler(myLooper);
msgobj = "This is from OwnLooperThread self and handleMessage function executed in !";
}
mOwnLooperThreadHandler.removeMessages(0);
// 给自己发送消息
Message msg = mOwnLooperThreadHandler.obtainMessage(3, 1, 1, msgobj);
mOwnLooperThreadHandler.sendMessage(msg);
// 开始循环
Looper.loop();
}
}
public void onClick(View v)
{
switch (v.getId())
{
case 101:
// 主线程发送消息给自己
Looper looper = Looper.myLooper();// get the Main looper related with the
// main thread
// 如果不给任何参数的话会用当前线程对应的Looper(这里就是Main Looper)为Handler里面的成员mLooper赋值
mHandler = new EventHandler(looper);
// 清除整个MessageQueue里的消息
mHandler.removeMessages(0);
String obj = "This main thread's message and received by itself!";
// 消息号是1
Message msg = mHandler.obtainMessage(1, 1, 1, obj);
// 将Message对象送入到main thread的MessageQueue里面
mHandler.sendMessage(msg);
break;
case 102:
bpostRunnable = false;
noLooperThread = new NoLooperThread();
noLooperThread.start();
break;
case 103:
tv.setText("please look at the error level log for other thread received message");
ownLooperThread = new OwnLooperThread();
ownLooperThread.start();
break;
case 104:
bpostRunnable = true;
noLooperThread = new NoLooperThread();
noLooperThread.start();
break;
case 105:
if (null != mOtherThreadHandler)
{
tv.setText("这是主线程向自己的子线程发送消息");
String msgObj = "message from mainThread";
Message mainThreadMsg = mOtherThreadHandler.obtainMessage(1, 1, 1, msgObj);
mOtherThreadHandler.sendMessage(mainThreadMsg);
}
break;
case 106:
finish();
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
// getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
2、例子说明
按钮依次演示了 主线程内的消息处理、子线程内的消息和runnable处理,以及往别的线程中发消息等操作,
并且重点区分了looper所对应的执行线程,以及handler所操作的具体线程等信息。
3、handler机制的问题
一个looper上可以挂多个handler,且looper内的message是依次进行处理的,这样 同一个message上的handler之间会相互阻塞,而且前一个消息会阻塞后面消息的处理,
这里的阻塞的意思是说只能等着前一个结束了才能进行后一个,如果要实现不阻塞,则只能每个handler都在不同的线程中,这样的话很快就会出现线程太多拖慢系统的问题。
举个应用中的例子:假如我们的app可以允许从网上下载内容,则如果要同时支持多个下载,则必须同时开多个线程,如果同时app支持从远端数据库中查询数据,从淘宝中捞出数据,和别人实时交互... 这里的每一样都需要
开一个新的线程,所以如果app复杂一些,则会有性能问题。解决方案是AsyncTask机制。
二、AsyncTask机制
前面说了需要每个handler的回调是异步的,即每次调到这里时可以直接返回,而不是一直堵在那里,那么只能使用异步的消息了(底层是使用线程池来进行调度,所以不会有一个handler占一个线程的问题)。
1、基本的实现思路
异步的基本实现思路是 将每个需要执行的task排队,之后依次取出进行执行,在执行时检查是否完成,没有的话直接返回给监听线程,这样监听线程可以查看别的task执行的情况,这样从总体上来看
好像所有的消息都得到了照顾,但是实际上这个只是个假象,因为实际在干活的只能是有限的worker thread,而且道理上讲 线程越多,系统越慢。
2、AsyncTask的抽象数据结构
抽象上讲,AsyncTask需要提供 输入的参数、更新时的参数、输出的参数等3个泛型的参数,为了实现异步需要提供
1、doInBackground(输入的参数) --- 实际干活的逻辑
2、onProgressUpdate(更新时的参数类型) --- 主线程要使用的回调
3、onPreExecute() ----- 初始化的工作
4、onPostExecute(输出时的数据类型) ------ 返回结果
3、可以使用的有用的函数 publishProgress(更新时的参数) --- 更新返回给调用线程的进度值
4、例子
package com.example.testasync;
import java.util.Random;
import android.R;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity1 extends Activity
{
private static final String TAG = "异步任务";
private static final String URL = "http://www.google.com.hk/";
private static final String STEP1 = "步骤1";
private static final String STEP2 = "步骤2";
private static final String STEP3 = "步骤3";
private static final String STEP4 = "步骤4";
private Button execute;
private Button cancel;
private ProgressBar progressBar;
private TextView textView;
private MyTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_item);
LinearLayout layout = new LinearLayout(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(250, 50);
layout.setOrientation(LinearLayout.VERTICAL);
execute = new Button(this);
execute.setId(101);
execute.setText("execute");
execute.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
mTask = new MyTask();
mTask.execute(URL);
execute.setEnabled(false);
cancel.setEnabled(true);
}
});
layout.addView(execute, params);
cancel = new Button(this);
cancel.setId(102);
cancel.setText("cancel");
layout.addView(cancel, params);
cancel.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//取消一个正在执行的任务,onCancelled方法将会被调用
mTask.cancel(true);
}
});
progressBar = new ProgressBar(this);
layout.addView(progressBar, params);
textView = new TextView(this);
layout.addView(textView, params);
setContentView(layout);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
//getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// 参数依次是输入、更新、输出的参数类型,为了说明问题故意将更新的参数类型设置为string 而不是integer
class MyTask extends AsyncTask<String, String, String> {
int times;
// onPreExecute方法用于在执行后台任务前做一些UI操作
protected void onPreExecute()
{
Log.i(TAG, "onPreExecute() called");
textView.setText("开始执行...");
}
// doInBackground方法内部执行后台任务,不可在此方法内修改UI
protected String doInBackground(String... params)
{
Log.i(TAG, "doInBackground(Params... params) called");
String result = "";
try{
// 模拟消耗时间的操作
for (int j = 0; j < 15;j++)
{
Random random = new Random();
int i = Math.abs(random.nextInt())%4;
result = "步骤" + i;
Thread.sleep(1000);
// 本来发布出去的一般是进度,这里故意用string
publishProgress(result);
}
// 随机返回,如果不是 STEP1则显示75%,否则是100%
// 这里是用这个来模拟task内部状态的变化,类似于游戏内的状态机
return result;
}
catch(Exception e)
{
return null;
}
}
// onProgressUpdate方法用于更新进度信息
protected void onProgressUpdate(String... progresses)
{
Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
if (progresses[0].equals(STEP1) )
progressBar.setProgress(100);
else
progressBar.setProgress(20);
textView.setText("loading..." + progresses[0]);
}
// onPostExecute方法用于在执行完后台任务后更新UI,显示结果
// 这个result具体的值是由doInBackground来设置的
protected void onPostExecute(String result)
{
Log.i(TAG, "onPostExecute(Result result) called");
textView.setText(result);
execute.setEnabled(true);
cancel.setEnabled(false);
}
// onCancelled方法用于在取消执行中的任务时更改UI
protected void onCancelled()
{
Log.i(TAG, "onCancelled() called");
textView.setText("cancelled");
progressBar.setProgress(0);
execute.setEnabled(true);
cancel.setEnabled(false);
}
}
}
5、说明
例子中演示了AsyncTask中 3个泛型参数的具体化,利用publishProgress实时的返回执行的情况,以及onProgressUpdate中实现的 操作异步化
三、内部实现猜测
尽管AsyncTask内部应该是用线程池来实现的,但是在保证消息处理不被堵住的情况下,异步的方式可以省很多的线程,进而整体上效率会高一点。
这个和网游服务端的处理方式是一样的,实际上协程只是透明化的异步处理方式而已。