今天,我们来总结一下关于线程方面的知识。主要内容包括:认识线程、线程的基本用法、线程同步、子线程更新UI以及线程间通信机制。

首先,我们需要了解Android是单线程模型,我们创建的Service、Activity以及Broadcast均是在一个主线程处理,这里我们可以理解为UI线程。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们考虑使用Thread线程来解决。

接下来,我们来了解一下进程与线程的区别:

1. 地址空间:进程内的一个执行单元;进程至少有一个线程,它们共享进程的地址空间;而进程有自己独立的地址空间;

2. 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源;

3. 线程是处理器调度的基本单位,但进程不是;

4. 二者均可并发执行。

实现多线程有两种方法:继承java.lang.Thread类或者实现Runnable接口。启动线程的方法分别是调用run()方法和Start()方法。

当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),所以我们用同步机制来解决这些问题。同步机制的实现方法有同步代码块、同步方法等。

子线程更新UI有四种方法:handle.post(Runnable r)、handle.handleMessage(Message msg)、runOnUiThread(Runnable r)和View.post(Runnable r)。

最后,用于线程间通讯的类是Handler,它可以将Runnable和Activity交互起来,在run方法中发送Message,在Handler里,通过不同的Message执行不同的任务。

Android多线程机制主要包括以下几个部分:

1. Looper:在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。

2. Message:线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想要传递的消息。

3. MessageQueue:消息队列,先进先出,它的作用是保存有待线程处理的消息。

4. Handler:用于处理消息队列中的消息。一个Thread只能包含一个Handler对象。在实际应用中,读取消息队列一般需要循环执行,即不断地从消息队列中获取消息并进行相应处理,这就又需要一个Looper对象。

在Android系统中,线程是独立的程序单元,多个线程可以并行工作。一个应用程序中可能会包含多个线程(Thread),每个线程中都有一个run()方法,run()方法内部的程序执行完毕后,所在的线程就自动结束。每个线程都有一个消息队列,用于不同的线程之间传递消息。在run()方法内部,如果不主动去读取消息队列中的消息,这些消息就是一些无用的消息,因为它们没有被处理。在Android系统中,读取消息和处理消息是两个步骤,并由两个不同的部分完成,先读取消息,然后才能处理消息。无论是本线程的还是其它线程,都不能直接处理消息队列中的消息,而是需要通过在线程内部定义一个Handler类对象来处理消息队列。

您好,Android中的多线程编程主要分为两种:主线程和工作线程。主线程主要用于UI界面的绘制,而工作线程则用于执行一些耗时操作,例如读取数据库、网络连接等。

在Android中,Handler是一个非常重要的对象,它用于在不同的线程之间传递消息。Handler通常与Looper一起使用,Looper是消息队列的管理器,负责从线程的消息队列中循环读取消息值,再将这些消息传递给Handler对象。Handler对象中定义的消息处理函数会根据消息类型再调用Thread中定义的其他函数。如果Thread中没有Looper对象,那么Thread的执行体就无法读取消息队列;如果Thread中没有Handler对象,则不会处理任何消息。

MessageQueue、Looper、Handler之间的关系如下:在以上这个大循环中,Handler对象对应的就是Handler.handleMessage()部分完成的功能,Looper对象对应的就是整个while(1)循环控制和MessageQueue.getMessage()完成的功能。Looper对象负责从线程的消息队列中循环读取消息值,再将这些消息传递给Handler对象,Handler对象中定义的消息处理函数会根据消息类型再调用Thread中定义的其它函数。如果Thread中没有Looper对象,那么Thread的执行体就无法读取消息队列;如果Thread中没有Handler对象,则不会处理任何消息。

在Android中,定义线程的方法与Java相同,可以使用两种方法:一种是Thread类,另一种是Runnable接口。

1. Thread是一个类,根据Java继承风格,一个类只能有一个父类,继承了Thread的子类不能再继承其它类,这是一个缺陷。于是,出现了Runnable,其作用和Thread相同,都是启动另一个线程,不同的是,Runnable是一个接口(interface),因此可以同时实现多个接口。

2. Android中使用Thread与Java基本相同,所不同的是,Android抛弃了Java线程中一些不安全的做法。比如:终止一个Thread,在Java中可以调用线程名字.stop()、线程名字.destroy()等;而在Android中,这些方法都没有实现,即不能使用。

3. 新建一个Thread对象,需要实现两个方法:第一个是定义构造方法。在Android程序中,新建的线程多为Activity、Service等程序片断服务,而在线程的内部执行过程中,很多时候都需要使用应用程序内部的Context对象,因此,在实际应用中,线程的构造方法往往会传递应用程序的Context对象,从而在线程的内部可以调用Context相关的系统服务。当然,这不是必须的。第二个是run()方法。该方法是Thread对象中必须实现的方法,用于完成具体的任务。启动线程时,不能直接调用线程名字.run()方法,而是调用线程名字.start()方法启动,start()方法是Thread内部使用的,该方法包含初始化线程的工作,然后回调run()方法,这些对应用程序都是不可见的。

4. 停止线程时,不能调用线程名字.destroy()方法或者线程名字.stop()方法。run()方法执行完毕后,线程默认会自动停止。因此,如果需要线程循环执行run()方法内部的代码,可以在线程内部增加一个状态变量,run()方法内部通过检查该状态变量,决定是否继续执行;同时可在线程外设置该状态变量的值,从而终止该线程。

二、线程定义thread

1. 线程定义Thread,继承Thread类,并重写run()方法。在run()中放置代码的主体部分。

以下是重构后的代码:

```java

public class Thread1 extends Thread {

private Context mContext;

private boolean mRunState = true;

public Thread1(Context context) {

mContext = context;

}

@Override

public void run() {

while (mRunState) {

// 应用代码

}

}

public void setToStop() {

mRunState = false;

}

}

```

在Activity中启动和停止Thread1的方法如下:

```java

// 启动Thread1线程

Thread1 thread1 = new Thread1(this);

thread1.start();

// 停止Thread1线程

thread1.setToStop();

```

第3个参数是线程的名称。

8. 调用start()方法启动线程。直接用

```java

workThread.start();

```

9. 线程在run()方法返回后,线程就自动终止了;不推荐使用调用stop()方法在外部终止线程。

10. 最好的方法是通知线程自行终止,一般调用interrupt()方法通告线程准备终止,线程会释放它正在使用的资源,在完成所有的清理工作后自行关闭。方法是

```java

workThread.interrupt();

```

1 interrupt()方法并不能直接终止线程,仅是改变了线程内部的一个布尔字段,run()方法能够检测到这个布尔字段,从而知道何时应该释放资源和终止线程

2 在run()方法的代码,一般通过Thread.interrupted()方法查询线程是否被中断

11. 下面的代码是以1秒为间隔循环检测断线程是否被中断

1 第4行代码使线程休眠1000毫秒。

2 当线程在休眠过程中被中断,则会产生InterruptedException 。

3 在中断的线程上调用sleep()方法,同样会产生InterruptedException。

12. Thread.interrupted()方法功能

1 判断线程是否应被中断。

2 通过捕获InterruptedException判断线程是否应被中断,并且在捕获到InterruptedException后,安全终止线程。

Handler

一、使用Handler

1. Handler用于处理线程中的消息队列。当Looper对象从消息队列中获取消息后,会把消息派发给Handler对象。一个线程中只能有一个Handler对象,可以通过该对象向所在线程发送消息。因此,只要拥有其它线程中Handler对象的引用,就可以向其发送消息;除了给别的线程发送消息外,还可以给本线程发送消息。

2. Handler一般有两种用途:

1 实现一个定时任务。这个有点类似于Windows中的定时器功能,可以通过Handler对象向所在线程发送一个延时消息。当消息指定的时间到达后,通过Handler对象的消息处理方法完成指定任务。

2 在线程间传递数据。

3. Handler完成定时任务:

1 在一个Activity内部,经常需要做一些定时器的功能,比如周期性更新某个视图的内容、在指定时间后结束某个操作等。

在Android开发中,我们可以使用Handler对象的延迟发送消息方法来实现定时任务。首先,我们需要了解Message的数据结构。Message是一个描述消息的数据结构类,包含很多成员变量和方法。对于简单的消息处理,一般仅需了解3项:(1) int what,这是用户自定义的一个整型值,用于区分消息类型;(2) int arg1,这是额外消息参数;(3) int arg2,同arg1。对于需要包含更多数据的消息,可以使用message.setData()和getData()方法。

Handler发送消息的方式有两种:

1. 使用postXXX()方法,该方法用于把一个Runnable对象发送到消息队列。当消息被处理时,能够执行Runnable对象。

2. 使用sendXXX()方法,该方法用于发送一个Message类型的消息到消息队列。当消息被处理时,系统会调用Handler对象定义的handleMessage()方法处理该消息。

实现定时任务主要使用sendXXX()类,具体包含如下方法:

- sendEmptyMessage(int what):空消息是指该消息仅包含what值。

- sendEmptyMessageAtTime(int what, long uptimeMilis):在指定时间点发送空消息,uptimeMilis是指从本次开机开始运行的时间点,不包含系统休眠的时间,单位为毫秒。参照SystemClock类。

- sendEmptyMessageDelayed(int what, long delayMillis):在指定时间后发送空消息,指定的时间以毫秒为单位。

- sendMessage(Message):发送Message指定的消息。

- sendMessageAtTime(Message,long):在指定时间点发送该消息。

- sendMessageDelayed(Message, long):在指定的时间后发送该消息。

实现定时任务时,一般使用sendMessageAtDelayed()或者sendEmptyMessageAtDelayed()方法,即在指定的时间后发送消息。当收到该消息后,系统会调用Handler对象实现的消息处理接口handleMessage(),handleMessage()的参数是Message对象,可以通过Message的相关方法获得Message的具体值,并根据其消息完成不同的任务。二、Handler定时案例

1. handleMessage用于处理Activity所在线程接收到的消息,此处是把当前时间显示在文本框中。obtainMessage()方法用于从全局的消息池中获得一个已有的Message对象,系统为了加速线程间的消息传递,创建了一些全局的消息对象供各线程使用,这些全局消息对象称为全局消息池,使用该方法比重新创建一个消息对象的效率高。该方法的第一个参数用于指定初始化返回消息的what值。sendMessageDelayed()方法用于在1000毫秒后发送what值为100的消息,即在显示完当前时间后的1秒,再发送一次消息,从而可以每过1秒更新一次文本框的时间。此处使用100代表该消息类型。

2. 需要注意的是:在应用程序运行时,当用户按Back键返回后,尽管Activity进入了暂停或者停止的状态,但是消息的发送会依然在后台执行,因此,程序员需要根据情况决定是否要停止消息发送。例如可以在onPause()方法内将消息队列中的消息移除,并在onResume()方法中重新开始消息发送。removeMessage(100)方法用于移除消息队列中what值为100的全部消息。

3. Handler完成线程间传递数据。

1 使用Handler对象不但可以给本线程发送消息,还可以给其它线程发送消息,前提是需要获取其它线程中的Handler对象。

2 线程之间传递数据在GUI应用中十分广泛,比如后台线程正在执行具体的数据处理,前台界面需要显示出处理的进度,典型的就是进度对话框。在这种应用中,前台线程(一般是指Activity)创建一个后台线程,并把前台线程的Handler对象传递给后台线程,后台线程就可以通过该Handler对象向前台线程发送消息,报告后台数据处理的进度。

Looper

Thread类在默认情况下,只要run()方法执行完毕,线程就会自动结束。为了控制线程不主动退出,可以在run()方法内部添加一个while()循环。对于不需要接收消息的线程来说,这种方法基本上够用了。

然而,在某些情况下,新建的线程需要接收消息并处理。这时,在新线程中,除了需要添加一个Handler对象外,还需要从线程的消息队列中取出消息,并负责分发消息。这就需要使用Looper了。实际上,Activity内部就有一个Looper,只是Activity是一个特殊的Thread,操作系统已经将其封装了而已。

Looper和Handler通常一起使用。Looper.prepare()用于给该线程创建一个Looper对象;Looper.loop()用于开始执行Looper对象,让其开始读取线程的消息队列,并派发消息到Handler对象的handleMessage()方法。

以下是一个简单的示例:

```java

public class MainActivity extends Activity {

private Handler mHandler;

private int what = 100;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mHandler = new MyHandler(this);

Thread thread = new Thread(new MyRunnable());

thread.start();

}

class MyRunnable implements Runnable {

public void run() {

Looper.prepare();

mHandler.sendEmptyMessage(what++);

Looper.loop();

}

}

class MyHandler extends Handler {

public MyHandler(Context context) {

super(context.getMainLooper());

}

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case 100:

method1();

break;

case 200:

getLooper().quit();

break;

}

}

}

public void method1() {

Log.d("MainActivity", "method1");

}

}

```

在这个示例中,我们创建了一个名为MyHandler的自定义Handler类,它继承自Handler。在MyHandler类中,我们重写了handleMessage()方法,根据接收到的消息值执行不同的操作。在MainActivity类中,我们创建了一个新线程并启动它,然后在handleMessage()方法中调用method1()方法和getLooper().quit()方法。

该类是为了简化Looper操作而设计的,使用时只需重写`onLooperPrepared()`方法,并在其中添加一个Handler对象。其功能与在Thread中加入Looper对象和Handler对象完全相同。当`onLooperPrepared()`方法被调用时,类似于在Thread中执行完`Looper.prepared()`方法。

线程是独立的程序单元,多线程可以并行工作。每个线程都有一个消息队列,用于不同线程之间的消息传递。然而,无论是本线程还是其他线程,都无法直接处理消息队列中的消息,而是需要通过在线程内部定义一个Handler类的对象来处理消息队列。

请注意:一个Thread中只能包含一个Handler对象。在实际应用中,读取消息队列通常需要循环执行,即不断从消息队列中获取消息并进行相应处理,这就要求有一个Looper对象。今天就在这里给大家总结一下这个知识点,希望对同样从事Android开发的朋友们有所帮助。如果有任何指正或建议,请随时提出并与我联系!