什么是androidandroid 多线程下载编程技术

用户名:laokaddk
文章数:983
评论数:107
访问量:2561250
注册日期:
阅读量:1297
阅读量:3317
阅读量:428208
阅读量:1116205
51CTO推荐博文
& &前几天看到一个帖子,有位朋友列出了一段代码1: & & & &/********************代码1:在非UI线程里面操作UI*****************************/class myThread extends Thread {private Handler myHpublic void run() {Looper myLooper, mainLLooper.prepare();myLooper = Looper.myLooper();mainLooper = Looper.getMainLooper();Sif (myLooper == null) {Log.d("ALOOP", "myLooper == null " + Thread.currentThread().getId());} else {myHandler = new Handler(myLooper) {//DebugPoint 2@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);switch (msg.what) {case MSG_TIMER2:String obj1 = "MSG_TIMER MainThread";Log.d("ALOOP","Print1 &myHandler::handleMessage begin thread id= "+ Thread.currentThread().getId());vText.setText(obj1);Log.d("ALOOP","Print2 myHandler::handleMessage end thread id= "+ Thread.currentThread().getId());}}};myHandler.sendEmptyMessage(MSG_TIMER2);}Looper.loop();};} & & & 显然,这段代码是在在非UI线程里面操作UI。Android 的UI元素不是线程安全的。也就是说,不应该在非UI线程里面操作UI元素。 & & & 但是这位朋友很困惑的是,这段代码确实运行正常。而且运行了若干次依然正常。 & & & 其实多线程编程,有时候你运行若干次,结果正确,并不表明你的逻辑就是对的。 & & & 那为什么这里运行多次一直正常呢?根据作者的以往的调试经验,这类代码通常会在运行的时候报 CalledFromWrongThreadException: “Only the original thread that created a view hierarchy can touch its views.” 。可是这段代码我运行多次,确实没有发现这个异常出来。 & & &为了刨根究底, 我们不妨跟着vText.setText代码往下看,看看这段代码的执行路径: & & &TextView::setText & & &View::invalidate & & &ViewRoot::invalidateChild & & &作者在 View::invalidate里面加入了打印信息,发现在myHandler.handleMessage函数的打印信息“Print1”和“Print1”之间,判断条件mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)是不成立的,也就是说并没有调用p.invalidateChild(this, r)。 & &public void invalidate() { & & & &if (ViewDebug.TRACE_HIERARCHY) { & & & & & &ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); & & & &} & & & &if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) { & & & & & &mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID; & & & & & &final ViewParent p = mP & & & & & &final AttachInfo ai = mAttachI & & & & & &if (p != null && ai != null) { & & & & & & & &final Rect r = ai.mTmpInvalR & & & & & & & &r.set(0, 0, mRight - mLeft, mBottom - mTop); & & & & & & & &// Don't call invalidate -- we don't want to internally scroll & & & & & & & &// our own bounds & & & & & & & &p.invalidateChild(this, r); & & & & & &} & & & &} & &} & &p.invalidateChild的调用路径是:
& &View::invalidateChild & &ViewGroup::invalidateChild & &...(ViewGroup::invalidateChild若干) & &ViewGroup::invalidateChild & &ViewRoot::invalidateChild & &ViewRoot::checkThread //这个地方会抛出CalledFromWrongThreadException & & 所以既然在 “Print1”和“Print1”之间没有调用p.invalidateChild,那自然就不会抛出CalledFromWrongThreadException的异常。 & &从实验结果来看,这里调用的 &TextView::setText,仅仅在于修改了TextView::mText的成员变量。但是并未对TextView 发起一次刷新。也就是说,并没有完成对textView的修改,修改的仅仅是他的模型(数据)。可是做过实验的朋友会发现TextView的文本在这句话之后界面确实会显示“MSG_TIMER MainThread” 。这个界面(视图)的显示其实不是TextView::setText的结果,而是Activity::setContentView的结果。 & &这部分代码的实际运行路径分为两条线,UI修改线和textView数据修改线。 & &UI修改线程: & &Activity::setContentView & & & & & & & & & & & & & &
& &ViewRoot::invalidateChild & &textView数据修改线程: & &TextView::setText & &View::invalidate(并未实际修改UI) & &ViewRoot::invalidateChild(因为状态不对,没有进入) & &这两条线是并行进行的。UI修改线程会将mPrivateFlags 置为DRAWN | HAS_BOUNDS。但是“textView数据修改线程”的执行路径很短,所以他很可能在mPrivateFlags被UI修改线程修改之前到达View::invalidate,从而导致判断的失败,代码也并没有进入ViewRoot::invalidateChild。 & &所以,如果我们在代码vText.setText(obj1);之前加上一个延时,让textView数据修改线程稍微等待UI修改线程一会会,系统基本上会一直报异常CalledFromWrongThreadException:try {//DebugPoint 3Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} &vText.setText(obj1); & 综上,这个代码本身是有问题的。之所以没有报异常,是UI线程和数据修改线程在时序上错过了。 & 要根本上解决这个问题,就要把数据修改线程的Looper修改为UI主线程的Looper: & //myHandler = new Handler(mainLooper) { //DebugPoint 1 &
&完整代码如下: &/*TempTestActivity.java*/package com.import android.app.Aimport android.os.Bimport android.os.Himport android.os.Limport android.os.Mimport android.util.Limport android.widget.TextVpublic class TempTestActivity extends Activity {private final int MSG_TIMER2 = 14;private TextView vText =myT@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);vText = (TextView) findViewById(R.id.tv01);//t.myHandler.sendEmptyMessage(MSG_TIMER2);//vText.setText("adnb");t = new myThread();t.start();}class myThread extends Thread {private Handler myHpublic void run() {//Looper myLooper, Looper mainL//Looper.prepare();//myLooper = Looper.myLooper();mainLooper = Looper.getMainLooper();S//if (myLooper == null) {Log.d("ALOOP", "myLooper == null " + Thread.currentThread().getId());//} else {myHandler = new Handler(mainLooper) { //DebugPoint 1//myHandler = new Handler(myLooper) {//DebugPoint 2@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);switch (msg.what) {case MSG_TIMER2:String obj1 = "MSG_TIMER MainThread";Log.d("ALOOP","myHandler::handleMessage begin thread id= "+ Thread.currentThread().getId());/*try { //DebugPoint 3Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}*/vText.setText(obj1);//vText.setVisibility(View.VISIBLE);//vText.invalidate();Log.d("ALOOP","myHandler::handleMessage end thread id= "+ Thread.currentThread().getId());}}};myHandler.sendEmptyMessage(MSG_TIMER2);//}//Looper.loop();};}} & &主要Layout文件: & &?xml version="1.0" encoding="utf-8"?&&LinearLayout xmlns:android="/apk/res/android"android:id="@+id/LinearLayoutTestInval" & &android:orientation="vertical" & &android:layout_width="fill_parent" & &android:layout_height="fill_parent" & &&&TextView &android:id="@+id/tv01" & &android:layout_width="fill_parent"
& &android:layout_height="wrap_content"
& &android:text="@string/hello" & &/&&/LinearLayout&
了这篇文章
类别:┆阅读(0)┆评论(0)页面已拦截
无锡网警提示您:
该网站已被大量用户举报,且存在未经证实的信息,可能会通过各种手段来盗取您的账号或骗取您的财产。Android开发中的多线程编程技术;日00:05来源:it168;Android中的线程;在Android平台中多线程应用很广泛,在UI更;HMMessageQ;Android线程应用中的问题与分析;为了介绍这些概念,我们把计时器的案例移植到And;publicclasschapter8_3ext;priva
Android开发中的多线程编程技术
日 00:05 来源:it168网站 作者:关东升 赵志荣 编辑:景保玉
Android中的线程
在Android平台中多线程应用很广泛,在UI更新、游戏开发和耗时处理(网络通信等)等方面都需要多线程。Android线程涉及的技术有:
HMMessageQLHandlerThread。
Android线程应用中的问题与分析
为了介绍这些概念,我们把计时器的案例移植到Android系统上,按照在Frame方式修改之后的代码清单8-4,完整代码请参考chapter8_3工程中 chapter8_3代码部分。
【代码清单8-4】
public class chapter8_3 extends Activity {
private String TAG = &chapter8_3&;
private Button btnE
private TextView labelT
private Thread clockT
private boolean isRunning =
private int timer = 0;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnEnd = (Button) findViewById(R.id.btnEnd);
btnEnd.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
isRunning =
labelTimer = (TextView) findViewById(R.id.labelTimer);
/* 线程体是Clock对象本身,线程名字为&Clock& */
clockThread = new Thread(new Runnable() {
public void run() {
while (isRunning) {
Thread.currentThread().sleep(1000
labelTimer.setText(&逝去了 & + timer + & 秒&);
Log.d(TAG, &lost
time & + timer);
} catch (InterruptedException e) {
e.printStackTrace();
clockThread.start(); /* 启动线程 */
程序打包运行结果出现了异常,如图8-8所示。
▲图8-8 运行结果异常图
我们打开LogCat窗口,出错日志信息如图8-9所示。
▲图8-9 出错日志
系统抛出的异常信息是“Only the original thread that created a view hierarchy can touch its views”,在Android中更新UI处理必须由创建它的线程更新,而不能在其他线程中更新。上面的错误原因就在于此。
现在分析一下上面的案例,在上面的程序中有两个线程:一个主线程和一个子线程,它们的职责如图8-10所示。
由于labelTimer是一个UI控件,它是在主线程中创建的,但是它却在子线程中被更新了,更新操作在clockThread线程的run()方法中实现,代码如下:
▲图8-10 线程职责
/* 线程体是Clock对象本身,线程名字为&Clock& */
clockThread = new Thread(new Runnable() {
public void run() {
while (isRunning) {
Thread.currentThread().sleep(1000);
labelTimer.setText(&逝去了 & + timer + & 秒&);
Log.d(TAG, &lost
time & + timer);
} catch (InterruptedException e) {
e.printStackTrace();
这样的处理违背了Android多线程编程规则,系统会抛出异常“Only the original thread that created a view hierarchy can touch its views”。
要解决这个问题,就要明确主线程和子线程的职责。主线程的职责是创建、显示和更新UI控件、处理UI事件、启动子线程、停止子线程;子线程的职责是计算逝去的时间和向主线程发出更新UI消息,而不是直接更新UI。它们的职责如图8-11所示。
▲图8-11 线程职责
主线程的职责是显示UI控件、处理UI事件、启动子线程、停止子线程和更新UI,子线程的职责是计算逝去的时间和向主线程发出更新UI消息。但是新的问题又出现了:子线程和主线程如何发送消息、如何通信呢?
在Android中,线程有两个对象―消息(Message)和消息队列(MessageQueue)可以实现线程间的通信。下面再看看修改之后的代码清单8-5,完整代码请参考chapter8_4工程中chapter8_4代码部分。
【代码清单8-5】
public class chapter8_4 extends Activity {
private String TAG = &chapter8_3&;
private Button btnE
private TextView labelT
private Thread clockT
private boolean isRunning =
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnEnd = (Button) findViewById(R.id.btnEnd);
btnEnd.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
isRunning =
handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
labelTimer.setText(&逝去了 & + msg.obj + & 秒&);
labelTimer = (TextView) findViewById(R.id.labelTimer);
/* 线程体是Clock对象本身,线程名字为&Clock& */
clockThread = new Thread(new Runnable() {
public void run() {
int timer = 0;
while (isRunning) {
Thread.currentThread().sleep(1000);
/* labelTimer.setText(&逝去了 & + timer + & 秒&); */
Message msg = new Message();
msg.what = 0;
handler.sendMessage(msg);
Log.d(TAG, &lost
time & + timer);
} catch (InterruptedException e) {
e.printStackTrace();
三亿文库包含各类专业文献、高等教育、幼儿教育、小学教育、专业论文、中学教育、外语学习资料、各类资格考试、Android开发中的多线程编程技术34等内容。 
 Android开发中的多线程编程技术_IT/计算机_专业资料。Android 开发中的多线程编程技术多线程这个令人生畏的“洪水猛兽”,很多人谈起多线程都心存畏惧。在 Android ...  超线程技术使用 CPU 的不同部分同时执行指令,当资源冲突时需要等待。 多核技术...Android开发中的多线程编... 6页 免费 android中使用多线程――... 2页 免费...  Android 多线程详解_计算机软件及应用_IT/计算机_专业资料。Thread 类常用方法 (...(3)调用 Thread 中的匿名内部类 在 Android 应用开发中经常使用该种方法 ...  在多线程编程里面,一些敏感数据不允许被多个线程同时...访问技术,保证数据在任何时刻,最多有一个线程访问,...36、在开发 Android 应用时必须遵守单线程模型的原则...  Android 实现多线程下载将上面 JavaSE 实现多线程下载的代码经过一定的改造,便可...(); 1.6.4 添加权限在该工程中不仅用到了网络访问还用到了 sdcard 存储,...  Condition 的功能类似于在传统的线程技术中的,Object...Race Condition(也叫做资源竞争),是多线程编程中比较...Android 开发之多线程处... 4页 免费 Android 多...  多线程:Android 的应用程序支持多线程(Thread),多线程编程为我们充分利 用系统...这种 Thread 构造方式也是在开发中最常 用到的方式。 (2)run()是 runnable ...  开源项目实现多线程下载 Android 下多线程的下载我们已经实现,在编写该业务逻辑的过程中我们做了大量的编码工作。下面 我们将通过一个开源项目来实现多线程的下载。 ...  android开发关键技术_计算机软件及应用_IT/计算机_专业...Linux 内核提供的基本功能, 如线程和底层内存管理。...我的电脑-&属性-&高级-&环境 变量-&系统变量中...老罗Android开发视频教程(15) - 多线程编程
- 爱酷学习网,免费高清视频教程在线观看
Begin to Learn
Descritpion
多线程无疑是提高用户体验的绝佳实际,也是编程中的重中之重,可以好好掌握。
Update Completed
请输入正确的邮件(格式:xxx@xxx.xx)
必须填本条信息!Android多线程详细教程
在Android应用的开发过程中,我们不可避免的要使用多线程,获取服务器数据、下载网络数据、遍历文件目录查找特定文件等等耗时的工作都离不开线程的知识。Android继承了Java的多线程体系,同时又实现了许多更加简易的API来操作线程。通过这些API,我们可以方便快捷的实现线程的创建、线程间的交互。我打算记下最近自己学习Android多线程机制时的学习笔记,一来可以供以后翻阅查看,二来为那些正疑惑与此的朋友提供一条解决问题的途径。先大致说一下我想写的内容:一、AsyncTask二、Thread 与 Handler三、HandlerThread全部内容分为3篇Bolg。下面正式开始第一部分吧!一、AsyncTask(0)参考:/reference/android/os/AsyncTask.html(1)简单介绍:  AsyncTask这是一个很便捷的关于线程的API,它将比较耗时(几秒)的工作放在非UI线程中运行,然后将结果的展示放在UI线程中。整个过程不涉及到任何关于Thread、Handler及Message等的处理。  先来说说AsyncTask的构造吧,它接收3个泛型参数,分别代表了后台工作的传入参数类型、进度的参数类型、结果的参数类型。每一个的具体含义会在接下来的AsyncTask的执行过程中讲到。要想使用AsyncTask就必须拓展为它的子类同时指定3个泛型参数,然后在子类的中重写几个重要的方法。下面就AsyncTask的执行过程来写。(2)AsyncTask的执行过程:  AsyncTask中的回调方法有:onPreExecute(),UI线程中执行,一般用来处理执行异步任务前的准备工作,比如弹出一个进度条,展现一个对话框告知用户正在执行一项任务。doInBackground(Params...),在非UI线程中执行,这里是异步任务的核心,所有需要耗时的工作全在这里执行,因为不是在UI线程中,因此不会存在阻塞UI线程的危险,不会引发ANR错误。onProgressUpdate(Progress...),在UI 线程中执行,一般用来更新任务执行的进度,它的调用时在publishProgress(Progress...)调用之后接着被调用的,我们可以在doInBackground(Params...)中多次调用publishProgress(Progress...)来告知用户任务的执行状态。onPostExecute(Result),在UI线程中执行,任务完成后(doInBackground(Params...)执行结束)且任务没被取消时,会执行该方法,用来处理异步任务得到的结果。onCancelled(Result),在UI线程中执行,当任务以柔和的方式结束(AsyncTask.cancel(false))这个方法会取代onPostExecute(Result)得以执行(当然这里也要看程序的执行逻辑,有没有在doInBackground(Params...)中检测任务是否被取消)  关于泛型参数:  三个泛型参数分别是:Params,Progress,Result。在上面的回调方法接受中我们可以看到它们分别所处那个方法,Params是启动异步任务时传入的参数,比如启动异步任务从网上下载一张图片传入图片的URL地址。Progress是进度参数类型,调用publishProgress(Progress...)时的参数需和Progress类型相容。比如用一个整数去描述进度,Progress及时Integer。Result是结果参数类型,异步任务完成后,如需返回一个结果,那么这个结果的类型就是Result类型。特别地,可以用Void去代替任何一个或多个泛型参数,这样做就以为着不需要参数。(3)一个简单的例子:话说百闻不如一见,下面我就用一个极简单的例子说明AsyncTask的使用过程。先说我要干啥,我用一个异步任务来更新一个进度条(简单吧!粗暴吧!颤抖吧,地球人!O(∩_∩)O)注:示例没什么格式,强迫症患者见谅!!先给出布局:
&?xml version="1.0" encoding="utf-8"?&
&RelativeLayout
xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"&
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="AsyncTask示例"
android:textSize="30sp"/&
&ProgressBar
android:id="@+id/progress"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/&
android:id="@+id/cancel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="cancel"/&
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/cancel"
android:text="start"/&
&/RelativeLayout&
下面是AsyncTask的逻辑代码:
package comfallblank.github.
import android.os.AsyncT
import android.os.B
import android.support.v7.app.AppCompatA
import android.util.L
import android.view.V
import android.widget.B
import android.widget.ProgressB
public class MainActivity extends AppCompatActivity {
private ProgressBar mProgressB
private Button mStartB
private Button mCancelB
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.progress);
//设置ProgressBar暂时不可见,一会在异步任务的中显示它。
mProgressBar.setVisibility(View.GONE);
final MyTask myTask =
new MyTask();
mStartButton = (Button) findViewById(R.id.start);
mStartButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
myTask.execute();
mCancelButton = (Button) findViewById(R.id.cancel);
mCancelButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
myTask.cancel(false);
private class MyTask extends AsyncTask&Void,Integer,String&{
private static final
String TAG = "MyTask";
protected void onPreExecute() {
super.onPreExecute();
//准备工作,显示进度条
mProgressBar.setVisibility(View.VISIBLE);
//打印日志,查看执行路径,下同
Log.d(TAG,"onPreExecute");
protected String doInBackground(Void... voids) {
Log.d(TAG,"doInBackground");
//每1s对进度条更新10%
synchronized (this){
for (int i = 1;i&=10;i++){
Thread.sleep(1000*1);
publishProgress(i*10);
if (isCancelled()){
return "Task cancelled";
} catch (InterruptedException e) {
e.printStackTrace();
return "Task executed";
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressBar.setProgress(values[0]);
Log.d(TAG,"onProgressUpdate");
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d(TAG, s);
Log.d(TAG,"onPostExecute");
protected void onCancelled(String s) {
super.onCancelled(s);
Log.d(TAG, s);
Log.d(TAG, "onCancelled");
LogCat日志:执行完毕,没有中途取消任务:10-06 13:55:05.871 /comfallblank.github.asynctasktest D/MyTask: onPreExecute10-06 13:55:05.891 /comfallblank.github.asynctasktest D/MyTask: doInBackground10-06 13:55:06.922 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:07.923 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:08.914 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:09.925 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:09.975 /comfallblank.github.asynctasktest W/art: Suspending all threads took: 18.035ms10-06 13:55:10.926 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:11.937 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:12.938 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:13.939 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:14.950 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:15.951 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:55:15.971 /comfallblank.github.asynctasktest D/MyTask: Task executed10-06 13:55:15.971 /comfallblank.github.asynctasktest D/MyTask: onPostExecute中途取消任务:10-06 13:56:10.644 /comfallblank.github.asynctasktest D/MyTask: onPreExecute10-06 13:56:10.654 /comfallblank.github.asynctasktest D/MyTask: doInBackground10-06 13:56:11.665 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:56:12.666 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:56:13.667 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:56:14.668 /comfallblank.github.asynctasktest D/MyTask: onProgressUpdate10-06 13:56:15.689 /comfallblank.github.asynctasktest D/MyTask: Task cancelled10-06 13:56:15.689 /comfallblank.github.asynctasktest D/MyTask: onCancelled关于AsyncTask的一些注意事项:(1)AsyncTask对象只能使用一次,也就是说一个对象执行完毕或者中途被cancel后,不能再次调用execute().(2)AsyncTask.execute()只能在主线程中调用。(3)AsyncTask不能处理并发,多个AsyncTask也是有执行顺序的,且同一个应用的所有AsyncTask都是在同一进程中执行,按照队列排序依次执行。(4)AsyncTask只能处理那些耗时不是特别长的任务,对于需要长时间执行的任务最好自己实现Thread。AsyncTask很方便,它屏蔽了所有Thread、Handler、Looper、Message及Messagequene的细节,这或许就是封装的魅力吧!但当你真正明白了Android的线程通讯之后你就会明白那些良好设计带来的美感。二、Thread 与 Handler  本节涉及的概念较多,有Thread,Handler,Looper,Message,MessageQuene,对于Looper和MessageQueue只是简单的谈及它们在线程通信中的作用,Thread,Handler及Message我会尽力讲清楚些。  1.Thread基础:  1)参考文档:/reference/java/lang/Thread.html  我包括我的好些朋友学东西时都会忽略官方文档的重要性,其中很大的原因是因为英文的缘故吧,但其实看懂文档要求英语能力并不高。看别人写的、博客、书籍,听别人讲技术,那始终是经过他人过滤后的知识,而这一切未必是你需要的。前车之鉴固然重要,但并不是每一个老人的话都得听。歪果仁写东西有个特点就是他不仅把原理给你讲清楚了,还爱举列子,该怎么做不建议怎么做都会涉及。说了这么多,就是建议自己去看看Android的开发文档,从Guide到Reference内化出自己的知识体系。  2)简单介绍:  看Android的Thread继承树,我们就知道它完全继承了Java的线程体系,在这就不赘言过多的Thread细节了,我就如何开启一个线程讲一讲。开启一个新线程有两种方法,第一种是拓展Thread类,在子类中重写run()方法;第二种是声明Thread对象时传入一个Runnable对象,因为只涉及到一个抽象方法,Runnable对象可以用jdk8的Lambda表达式来代替,这样使得线程的创建变得更加简单。创建好了线程后,如何让线程开始执行呢?调用Thread.run()即可让线程进入待执行队列,当获得CPU时间时它就运行起来了。具体的用法例子会在后面的示例中展现。  2.Handler基础:  1)参考文档:/reference/android/os/Handler.html  2)概述:  之前看过一部美剧《天蝎计划》,在介绍团队负责人时,这样说道”He is our goverment handler“。Android中的Handler就是这样一个概念,它是线程通信的发送者和处理者,线程要进行通信时会让handler发出相应的消息,通过Looper传递,Handler发出的消息会在目的线程中得以执行。再举个栗子吧,这是我在《Android编程权威指南》中看到的,它是这么描述线程通信的:两个线程的通信就好像现实中的两个人通过信件来通信,消息队列(MessageQueue)相对于通信时候的信箱;Message(消息)相当于信件;Looper相当于邮递员,它是MessageQueue的操作者;Handler时线程通信的发出者和处理者;每当Thread想要进行通信,它会让Handler投递一个Message给相应的MessageQueue,Looper会一直循环将MessageQueue里的Message发向它的目的地,到达目的地后Looper通知相应的Handler来处理消息。  每一个Handler都是和唯一的Looper对象绑定的,也就是说一个Handler既仅可以个一个Looper绑定,但一个Looper可以有好几个Handler与之关联。Looper操作的MessageQueue是Handler取得消息进行处理和发出消息的地方。  3)使用介绍:  前面说过每一个Handler都是和特别的Looper绑定好的,同时Handler又是处理消息的地方,所以Handler中既要说明和哪个Looper绑定,又要告知怎么处理消息。所以Handler有4个构造方法,下面我来一一介绍:
Handler()这是无参数构造方法,它默认和当前线程的Looper绑定,未指定消息的处理方式。
Handler(Looper looper)它需要一个Looper对象作为参数传入,构成出来的Handler对象将和给定的Looper对象绑定。
Handler(Handler.Callback callback)它需要一个Handler.Callback对象作为参数传入,构造处理的对象和当前线程的Looper绑定并用传入的Handler.Callback对象来处理消息。
Handler(Looper looper,(Handler.Callback callback)这是第二种和第三种的结合,构成出一个和指定Looper绑定并用指定Callback的回调方法处理消息的Handler对像
还有一种使用Handler的方法就是自己拓展Handler类,在子类中实现handlerMessage(Message msg)(这就是接口Callback中的抽象方法),这也是我用的比较多的方式。  Handler的使用就是发送和处理消息,处理消息是在Callback接口中定义好的,当Looper操作的MessageQueue中有消息时,Looper对通知所有与它绑定的Handler调用handlerMessage(Message msg)去处理消息。那怎么发送消息呢?Hander中有一个方法叫sendMessage(Message msg),当调用这个方法后Handler就往Looper操作的MessageQueue中投递一个Message对象,发送消息就算是完成了。简单吧?so easy!  关于Handler的其他方法还请查看文档,将主干的部分弄清楚了,指端末节随着使用慢慢就熟络了。一口气吃不成胖子。对了,下面的Message部分还会提及Handler的一些内容。  3.Message基础:  1)参考文档:/reference/android/os/Message.html  2)使用介绍:  线程通信既然叫通信肯定有一个消息的载体,Message就是这个消息的载体,它包含了线程想要通信的全部内容,比如线程运行后得到的结果就和可以包含在Message中。关于Message的构造本来可以按正常的方式构造(就是new一个Message对象,词穷不知道怎么说,就叫它”正常的方式“O(∩_∩)O~),但官方推荐的做法是通过Handler.obtainMessage()获得Message对象,因为这样的Message是从即将被回收的Message中取得的,会避免GC的反复运行和减少运行时的内存消耗。  Message是消息的携带者,它有许多的携带消息的方法,比如setData(Bundle),Message.obj,Message.arg1,Message.arg2等等,需要特别说明的是Message里有一个公开的整形的全局变量what,即Message.what,它一般用来阐述消息的类型,Handler的handlerMessage(Message msg)通常会先检验Message的what变量,然后在决定如何处理。毕竟一个应用中和主线程通信的不可能只用一个线程,一种消息。  4.Looper与MessageQueue简单介绍:  1)参考文档:  Looper:/reference/android/os/Looper.html  MessageQueue:/reference/android/os/MessageQueue.html  2)简单介绍:  需要注意的地方就是,一个普通Thread创建时是没有Looper对象和它关联的,我们必须在线程的创建中进行关联,具体做法就是在Thread的创建时调用Looper.prepare()进行绑定,调用Looper.loop()使得与线程绑定的Looper对象开始工作。Looper中有一个巨好用的方法,Looper.getMainLooper(),这个方法直接返回当前应用的UI线程的Looper对象,有了Looper对象就可以往主线程发消息了,一会我在示例中会用到这样方法。  关于MessageQueue,其实在线程通信中我们并不直接使用它,只需知道我们通过Handler发送出去的消息都是放在它里的就行了,它是一个”第层次“的对象,我们也不能直接往它里添加消息。但对于理解整个线程通信过程还是很重要的。  5.实践——示例  说了这么多,是时候用实践检验了!先说说我打算怎么做吧!我打算从UI线程向非UI线程(是不是叫主线程和子线程更好?)发一个消息,然后在非UI线程中处理这个消息(这里我只是打印一下日志),然后从非UI线程向主线程发送一个消息,然后在UI线程中处理这个消息(也是简单的打印一下)。好像有些偷懒,但真正使用也大致是这样的,就是把打印换成具体的任务。好了,开始吧!  先给出布局吧!
&?xml version="1.0" encoding="utf-8"?&
&RelativeLayout
xmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"&
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="Thread和Handler"/&
android:id="@+id/send_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="发送消息"/&
android:id="@+id/startThread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/send_message"
android:text="开启子线程"/&
&/RelativeLayout&
  布局很简单,就是一个textview和两个button,一个button开启线程,并接受来自子线程的信息,另一个从主线程发出消息给子线程。  下面给出代码逻辑:  第一部分是子线程的代码:
package comfallblank.github.
import android.os.H
import android.os.L
import android.os.M
import android.util.L
* Created by fallb on .
public class MyThread extends Thread {
public static final int MSG_WORKER_THREAD = 100;
private static final
String TAG = "MyThread";
private Handler mWorkerH
private Handler mMainH
public MyThread(Handler handler) {
mMainHandler =
mWorkerHandler = new Handler(){
public void handleMessage(Message msg) {
if(msg.what == MainActivity.MSG_MAIN){
Log.d(TAG,"Message:"+msg.obj);
public void run() {
Looper.prepare();
Message msg = mMainHandler.obtainMessage();
msg.what = MyThread.MSG_WORKER_THREAD;
msg.obj="子线程发出的消息";
mMainHandler.sendMessage(msg);
Looper.loop();
public Handler getWorkerHandler() {
return mWorkerH
  第二份是主线程的:
package comfallblank.github.
import android.os.B
import android.os.H
import android.os.M
import android.support.v7.app.AppCompatA
import android.util.L
import android.view.V
import android.widget.B
public class MainActivity extends AppCompatActivity {
public static final int MSG_MAIN = 100;
private static final String TAG = "MainActivity";
private Button mStartT
private Button mSendM
private Handler mHandler = new MyHandler();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyThread thread = new MyThread(mHandler);
mStartThread = (Button) findViewById(R.id.startThread);
mStartThread.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Log.d(TAG, "开启线程");
thread.start();
mSendMessage = (Button) findViewById(R.id.send_message);
mSendMessage.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Handler workerHandler = thread.getWorkerHandler();
Message msg = workerHandler.obtainMessage();
msg.what = MSG_MAIN;
msg.obj = "来自主线程的消息";
workerHandler.sendMessage(msg);
class MyHandler extends Handler {
public void handleMessage(Message msg) {
if (msg.what == MyThread.MSG_WORKER_THREAD) {
Log.d(TAG,"Message:"+msg.obj);
  Logcat打印日志如下:  10-07 19:34:34.424 /comfallblank.github.threadandhandler D/MainActivity: 开启线程  10-07 19:34:34.454 /comfallblank.github.threadandhandler D/MainActivity: Message:子线程发出的消息  10-07 19:34:37.267 /comfallblank.github.threadandhandler D/MyThread: Message:来自主线程的消息  6.关于Handler再说两句:  Handler对象除了发出消息外,还有一族方法,来发出一个Runnable对象,Handler.post(Runnable runnable)及时其中的一个,它向MessageQueue中添加这个Runnable对象,然后目的线程的MessageQueue会执行该方法。}

我要回帖

更多关于 android 什么是多线程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信