Android多线程及异步处理问题详细探讨

ms67673

ms67673

2016-02-19 09:06

在这个颜值当道,屌丝闪边的时代,拼不过颜值拼内涵,只有知识丰富才能提升一个人的内在气质和修养,所谓人丑就要多学习,今天图老师给大家分享Android多线程及异步处理问题详细探讨,希望可以对大家能有小小的帮助。
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来实现的!
展开更多 50%)
分享

猜你喜欢

Android多线程及异步处理问题详细探讨

编程语言 网络编程
Android多线程及异步处理问题详细探讨

Servlet及JSP中的多线程同步问题

Java JAVA基础
Servlet及JSP中的多线程同步问题

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

关于Servlet及JSP中遇到的多线程同步问题

编程语言 网络编程
关于Servlet及JSP中遇到的多线程同步问题

JNI 中文处理问题小结

C语言教程 C语言函数
JNI 中文处理问题小结

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

个人对于异步和多线程的关系的理解分享

编程语言 网络编程
个人对于异步和多线程的关系的理解分享

处理WinForm多线程程序时的陷阱

电脑网络
处理WinForm多线程程序时的陷阱

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

“不能执行已释放的Script代码”错误的原因及解决办法

“不能执行已释放的Script代码”错误的原因及解决办法

Android获取手机型号/系统版本号/App版本号等信息实例讲解

Android获取手机型号/系统版本号/App版本号等信息实例讲解
下拉加载更多内容 ↓