Android多线程及异步处理问题详细探讨
程序员文章站
2023-08-12 19:50:10
1、问题提出 1)为何需要多线程? 2)多线程如何实现? 3)多线程机制的核心是啥? 4)到底有多少种实现方式? 2、问题分析 1)究其为啥需要多线程的本质就是异步处理,直...
1、问题提出
1)为何需要多线程?
2)多线程如何实现?
3)多线程机制的核心是啥?
4)到底有多少种实现方式?
2、问题分析
1)究其为啥需要多线程的本质就是异步处理,直观一点说就是不要让用户感觉到“很卡”。
eg:你点击按钮下载一首歌,接着该按钮一直处于按下状态,那么用户体验就很差。
2)多线程实现方式implements runnable 或 extends thread
3)多线程核心机制是handler
4)提供如下几种实现方式
—-1—–handler
————————————说明1
创建一个handler时一定要关联一个looper实例,默认构造方法handler(),它是关联当前thread的looper。
eg:
我们在ui thread中创建一个handler,那么此时就关联了ui thread的looper!
这一点从源码中可以看出!
精简代码如下:
public handler() {
mlooper = looper.mylooper();
//当前线程的looper,在activity创建时,ui线程已经创建了looper对象
//在handler中机制中looper是最为核心的,它一直处于循环读messagequeue,有
//要处理的message就将message发送给当前的handler实例来处理
if (mlooper == null) {
throw new runtimeexception(
“can't create handler inside thread that has not called looper.prepare()”);
}
//从以上可以看出,一个handler实例必须关联一个looper对象,否则出错
mqueue = mlooper.mqueue;
//handler的messagequeue,它是fifo的吗?不是!我感觉应该是按时间先后排列
//的!message与messagequeue到底是啥关系?感兴趣可以研究一下源码!
mcallback = null;
}
在创建一个handler的时候也可以指定looper,此时的looper对象,可以是当前线程的也可以是其它线程的!
handler只是处理它所关联的looper中的messagequeue中的message,至于它哪个线程的looper,handler并不是很关心!
eg:
我们在ui线程中创建了handler实例,此时传进worker线程的looper,此时依然可以进行业务操作!
eg:
——————–创建工作者线程
private static final class worker implements runnable
{
private static final object mlock = new object() ;
private looper mlooper ;
public worker(string name)
{
final thread thread = new thread(null,this,name) ;
thread.setpriority(thread.min_priority) ;
thread.start() ;
synchronized(mlock)
{
while(mlooper == null)
{
try
{
mlock.wait() ;
}
catch (interruptedexception e)
{
e.printstacktrace();
}
}
}
}
@override
public void run() {
synchronized(mlock)
{
//该方法只能执行一次,一个thread只能关联一个looper
looper.prepare() ;
mlooper = looper.mylooper() ;
mlock.notifyall() ;
}
looper.loop() ;
}
public looper getlooper()
{
return mlooper ;
}
public void quit()
{
mlooper.quit() ;
}
}
我们可以在ui线程中创建一个handler同时传入worker的looper
eg:
—————-定义自己的handler
private final class myhandler extends handler
{
private long id ;
public myhandler(looper looper)
{
super(looper) ;
}
@override
public void handlemessage(message msg) {
switch(msg.what)
{
case 100 :
mtv.settext(“” + id) ;
break ;
}
}
}
———在activity中创建handler
this.mworker = new worker(“workerthread”) ;
this.mmyhandler = new myhandler(this.mworker.getlooper()) ;
———创建message
final message msg = this.mmyhandler.obtainmessage(100);
msg.put(“test” , “test”) ;
msg.sendtotarget() ;
需要注意的是,每一个message都必须要有自己的target即handler实例!
源码如下:
public final message obtainmessage(int what)
{
return message.obtain(this, what);
}
public static message obtain(handler h, int what) {
message m = obtain();
m.target = h;//可以看出message关联了当前的handler
m.what = what;
return m;
}
以上只是作了一点原理性的说明!
我们平时使用handler主要是用来处理多线程的异步交互问题!
由于android规定只有ui线程才能更新用户界面和接受用户的按钮及触摸事件!
那么就必须保证ui线程不可以被阻塞,从而耗时操作必须要开启一个新的线程来处理!
那么问题就来了,等耗时操作结束以后,如何把最新的数据反馈给用户呢?而我们目前工作worker线程中,从而不可以进行ui更新。
那么怎么办呢?必须要把最新的数据传给ui线程能处理的地方!现在就派到handler出场了!可handler到底干了啥呢?简要说明如下:
activity所在的ui线程在创建的时候,就关联了looper和messagequeue,那么我们又在ui线程里创建了自己的handler,那么handler是属于ui线程的,从而它是可以和ui线程交互的!
ui线程的looper一直在进行loop操作messagequeue读取符合要求的message给属于它的target即 handler来处理!所以啊,我们只要在worker线程中将最新的数据放到handler所关联的looper的messagequeue中,然而 looper一直在loop操作,一旦有符合要求的message,就第一时间将message交给该message的target即handler来处 理!所以啊,我们在创建message的时候就应该指定它的target即handler!
但我们也可以,new message() — > mhandler.sendmessage(msg) ;这是特例!
如果我们通过obtainmessage()方法获取message对象,此时handler就会自动设置message的target。可以看源码!
简单一点说就是:
ui线程或worker线程提供messagequeue,handler向其中填message,looper从其中读message,然后 交由message自己的target即handler来处理!!最终被从属于ui线程的handler的handlmessag(message msg)方法被调用!!
这就是android多线程异步处理最为核心的地方!!
有点罗嗦啊!!
*******************************************************************
在ui线程中创建handler[一般继承handlemessage(message msg)]
|
looper可以属于ui线程或worker线程
|
从属于looper的messgequeue,looper一直在loop()操作,在loop()中执行msg.target.dispatchmessage(msg);调用handler的handlemessage(message msg)
|
在 worker线程中获取message,然后通过handler传入messagequeue
*******************************************************************
—————–在创建一个looper时,就创建了从属于该looper的messagequeue
private looper() {
mqueue = new messagequeue();
mrun = true;
mthread = thread.currentthread();
}
—-2—–view
post(runnable action)
postdelay(runnable action , long miliseconds)
—–3—–activity
runonuithread(runnable action)
该方法实现很简单:
public final void runonuithread(runnable action) {
if (thread.currentthread() != muithread) {
//如果当前线程不是ui线程
mhandler.post(action);
} else {
action.run();
}
}
其中:
muithread = thread.currentthread() ;
mhandler = new handler()
—–4—–asynctask
params,progress,result都是数据类型,
params要处理的数据的类型
progress处理进度的类型
result处理后返回的结果
它是一个异步处理的简单方法!
方法的执行顺序:
1)
onpreexecute() –在ui线程中执行,作一些初始化操作
2)
doinbackground(params… params) –在worker线程中执行,进行耗时的后台处理,在该方法中可以调用publishprogress(progress progress) 进行进度处理
3)
onprogressupdate(progress progress) –在ui线程中执行,进行进度实时处理
4)onpostexecute(result result) –在ui线程中执行, 在doinbackground(params … params)返回后调用
5)
oncancelled() –在ui线程中执行,在asynctask实例调用cancle(true)方法后执行,作一些清理操作
几点注意:
asynctask必须在ui线程中创建,
asynctask.execute(params… params) ;在ui线程中执行,且只能执行一次
要想再次调用execute(params… params),必须重新创建asynctask对象
后3种方法本质上都是利用handler来实现的!
1)为何需要多线程?
2)多线程如何实现?
3)多线程机制的核心是啥?
4)到底有多少种实现方式?
2、问题分析
1)究其为啥需要多线程的本质就是异步处理,直观一点说就是不要让用户感觉到“很卡”。
eg:你点击按钮下载一首歌,接着该按钮一直处于按下状态,那么用户体验就很差。
2)多线程实现方式implements runnable 或 extends thread
3)多线程核心机制是handler
4)提供如下几种实现方式
—-1—–handler
————————————说明1
创建一个handler时一定要关联一个looper实例,默认构造方法handler(),它是关联当前thread的looper。
eg:
我们在ui thread中创建一个handler,那么此时就关联了ui thread的looper!
这一点从源码中可以看出!
精简代码如下:
复制代码 代码如下:
public handler() {
mlooper = looper.mylooper();
//当前线程的looper,在activity创建时,ui线程已经创建了looper对象
//在handler中机制中looper是最为核心的,它一直处于循环读messagequeue,有
//要处理的message就将message发送给当前的handler实例来处理
if (mlooper == null) {
throw new runtimeexception(
“can't create handler inside thread that has not called looper.prepare()”);
}
//从以上可以看出,一个handler实例必须关联一个looper对象,否则出错
mqueue = mlooper.mqueue;
//handler的messagequeue,它是fifo的吗?不是!我感觉应该是按时间先后排列
//的!message与messagequeue到底是啥关系?感兴趣可以研究一下源码!
mcallback = null;
}
在创建一个handler的时候也可以指定looper,此时的looper对象,可以是当前线程的也可以是其它线程的!
handler只是处理它所关联的looper中的messagequeue中的message,至于它哪个线程的looper,handler并不是很关心!
eg:
我们在ui线程中创建了handler实例,此时传进worker线程的looper,此时依然可以进行业务操作!
eg:
——————–创建工作者线程
复制代码 代码如下:
private static final class worker implements runnable
{
private static final object mlock = new object() ;
private looper mlooper ;
public worker(string name)
{
final thread thread = new thread(null,this,name) ;
thread.setpriority(thread.min_priority) ;
thread.start() ;
synchronized(mlock)
{
while(mlooper == null)
{
try
{
mlock.wait() ;
}
catch (interruptedexception e)
{
e.printstacktrace();
}
}
}
}
@override
public void run() {
synchronized(mlock)
{
//该方法只能执行一次,一个thread只能关联一个looper
looper.prepare() ;
mlooper = looper.mylooper() ;
mlock.notifyall() ;
}
looper.loop() ;
}
public looper getlooper()
{
return mlooper ;
}
public void quit()
{
mlooper.quit() ;
}
}
我们可以在ui线程中创建一个handler同时传入worker的looper
eg:
—————-定义自己的handler
复制代码 代码如下:
private final class myhandler extends handler
{
private long id ;
public myhandler(looper looper)
{
super(looper) ;
}
@override
public void handlemessage(message msg) {
switch(msg.what)
{
case 100 :
mtv.settext(“” + id) ;
break ;
}
}
}
———在activity中创建handler
this.mworker = new worker(“workerthread”) ;
this.mmyhandler = new myhandler(this.mworker.getlooper()) ;
———创建message
final message msg = this.mmyhandler.obtainmessage(100);
msg.put(“test” , “test”) ;
msg.sendtotarget() ;
需要注意的是,每一个message都必须要有自己的target即handler实例!
源码如下:
复制代码 代码如下:
public final message obtainmessage(int what)
{
return message.obtain(this, what);
}
public static message obtain(handler h, int what) {
message m = obtain();
m.target = h;//可以看出message关联了当前的handler
m.what = what;
return m;
}
以上只是作了一点原理性的说明!
我们平时使用handler主要是用来处理多线程的异步交互问题!
由于android规定只有ui线程才能更新用户界面和接受用户的按钮及触摸事件!
那么就必须保证ui线程不可以被阻塞,从而耗时操作必须要开启一个新的线程来处理!
那么问题就来了,等耗时操作结束以后,如何把最新的数据反馈给用户呢?而我们目前工作worker线程中,从而不可以进行ui更新。
那么怎么办呢?必须要把最新的数据传给ui线程能处理的地方!现在就派到handler出场了!可handler到底干了啥呢?简要说明如下:
activity所在的ui线程在创建的时候,就关联了looper和messagequeue,那么我们又在ui线程里创建了自己的handler,那么handler是属于ui线程的,从而它是可以和ui线程交互的!
ui线程的looper一直在进行loop操作messagequeue读取符合要求的message给属于它的target即 handler来处理!所以啊,我们只要在worker线程中将最新的数据放到handler所关联的looper的messagequeue中,然而 looper一直在loop操作,一旦有符合要求的message,就第一时间将message交给该message的target即handler来处 理!所以啊,我们在创建message的时候就应该指定它的target即handler!
但我们也可以,new message() — > mhandler.sendmessage(msg) ;这是特例!
如果我们通过obtainmessage()方法获取message对象,此时handler就会自动设置message的target。可以看源码!
简单一点说就是:
ui线程或worker线程提供messagequeue,handler向其中填message,looper从其中读message,然后 交由message自己的target即handler来处理!!最终被从属于ui线程的handler的handlmessag(message msg)方法被调用!!
这就是android多线程异步处理最为核心的地方!!
有点罗嗦啊!!
*******************************************************************
在ui线程中创建handler[一般继承handlemessage(message msg)]
|
looper可以属于ui线程或worker线程
|
从属于looper的messgequeue,looper一直在loop()操作,在loop()中执行msg.target.dispatchmessage(msg);调用handler的handlemessage(message msg)
|
在 worker线程中获取message,然后通过handler传入messagequeue
*******************************************************************
—————–在创建一个looper时,就创建了从属于该looper的messagequeue
private looper() {
mqueue = new messagequeue();
mrun = true;
mthread = thread.currentthread();
}
—-2—–view
post(runnable action)
postdelay(runnable action , long miliseconds)
—–3—–activity
runonuithread(runnable action)
该方法实现很简单:
public final void runonuithread(runnable action) {
if (thread.currentthread() != muithread) {
//如果当前线程不是ui线程
mhandler.post(action);
} else {
action.run();
}
}
其中:
muithread = thread.currentthread() ;
mhandler = new handler()
—–4—–asynctask
params,progress,result都是数据类型,
params要处理的数据的类型
progress处理进度的类型
result处理后返回的结果
它是一个异步处理的简单方法!
方法的执行顺序:
1)
onpreexecute() –在ui线程中执行,作一些初始化操作
2)
doinbackground(params… params) –在worker线程中执行,进行耗时的后台处理,在该方法中可以调用publishprogress(progress progress) 进行进度处理
3)
onprogressupdate(progress progress) –在ui线程中执行,进行进度实时处理
4)onpostexecute(result result) –在ui线程中执行, 在doinbackground(params … params)返回后调用
5)
oncancelled() –在ui线程中执行,在asynctask实例调用cancle(true)方法后执行,作一些清理操作
几点注意:
asynctask必须在ui线程中创建,
asynctask.execute(params… params) ;在ui线程中执行,且只能执行一次
要想再次调用execute(params… params),必须重新创建asynctask对象
后3种方法本质上都是利用handler来实现的!