Android多线程方式

Android多线程方式1 前言在 Android 开发中经常会使用到多线程 这里主要是总结 Android 开发中常见的多线程实现方式 以及这些多线程实现方式的一些特点多线程实现方式主要有 实现 Thread 的 run 方法或者实现 Runable 接口 HandlerThrea Thread 方式一般使用异步操作最常见的一种方式 我们可以继承 Thread 并

1、前言

  • 实现Thread的run()方法或者实现Runable接口
  • HandlerThread
  • AsyncTask
  • LoaderManager

2、Thread方式

一般使用异步操作最常见的一种方式,我们可以继承Thread,并重写run()方法,如下所示:

Thread syncTask = new Thread() { @Override public void run() { // 执行耗时操作 } }; syncTask.start();

还有另外一种启动线程的方式,即在创建Thread对象时,传入一个实现了Runable接口的对象,如下所示:

Thread syncTask = new Thread(new Runnable() { @Override public void run() { // 执行耗时操作 } }); syncTask.start();

Thread类中有几个方法的作用有些模糊,这里给出说明:

  • interrupt( ):
    我们一般会使用该方法中断线程的执行,但该方法并不会中断线程,它的作用只是设置一个中断标志位,我们还得在run( )方法中判断这个标志位,并决定是否继续执行,通过这样的方式来达到中断的效果。 但该方法根据线程状态的不同,会有不同的结果。结果如下:
    1. 当线程由于调用wait( )、join( )、sleep( )而阻塞时,中断标志位将会被清空,并接收到InterruptedException异常。
    2. 当线程由于正在进行InterruptibleChannel类型的I/O操作而阻塞时,中断标志位将会置位,并接收到ClosedByInterruptException异常(I/O流也会自动关闭)
    3. 当线程由于进行Selector操作而阻塞时,中断标志位将会置位,但不会接收到异常




  • join( )和sleep( )的区别:两个方法都会让线程暂停执行

3、HandlerThread

public class HandlerThread extends Thread {     int mPriority; Looper mLooper; / * Constructs a HandlerThread. */ public HandlerThread(String name, int priority) { super(name); mPriority = priority; } @Override public void run() { // 要想让某个线程成为Looper线程,先调用Looper.prepare()为该线程创建一个Looper对象,并初始化MessageQueue对象 Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); // 调用Looper.loop(),让该线程的Looper实例循环从MessageQueue中取出Message进行处理 Looper.loop(); } }

4、AsyncTask

这是我们最经常使用的一种异步方式,在前面的两种多线程方式中,如果在子线程中进行了耗时的处理操作(如:网络请求、读写数据库等),当操作完毕后,我们需要更新UI上的显示状态,但在Android开发中我们是不能在子线程中更新UI界面的,所以还得在子线程中发送一个通知到主线程,让主线程去更新UI。这样的操作流程有些复杂,且都是重复性的工作。所以Android sdk中为我们抽象出AsyncTask这个类。

public class CustomAsyncTask extends AsyncTask 
  
    { @Override 
   protected 
   void 
   onPreExecute() { 
   // 在开始执行异步操作前回调,该方法在主线程中执行 } @Override 
   protected String 
   doInBackground(String... strings) { 
   // 在该方法中进行异步操作,参数strings是在启动异步任务时execute(...)传递进来的 
   // 该异步任务放回的结果类型为String 
   return 
   null; } @Override 
   protected 
   void 
   onProgressUpdate(Integer... values) { 
   // 该方法用户通知用户doInBackground()方法的处理进度,在主线程中被回调,所以可在该方法中更新UI 
   // 参数values用于指示处理进度 } @Override 
   protected 
   void 
   onPostExecute(String result) { 
   // 该方法是在异步操作doInBackground()处理完毕后回调,参数result是doInBackground()的处理结果 
   // 该方法在主线程中被回调,可直接更新UI } @Override 
   protected 
   void 
   onCancelled(String result) { super.onCancelled(result); 
   // 当调用cancel(boolean), 则在doInBackground()完成后回调该方法 
   // 注意: 参数result可能为null, } } 
  

AsyncTask的内部使用了两个线程池,我们大概看一下AsyncTask的内部实现

// 顺序执行任务的线程池,注意这个线程池是静态的,每个AsyncTask对象共用这个线程池 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; // 我们启动异步任务的三个方法,都是向SerialExecutor.execute(runable)传递一个runable对象 public final AsyncTask 
   
   execute(Params... 
   params) { 
   return executeOnExecutor(sDefaultExecutor, 
   params); } 
   public final AsyncTask 
    
    executeOnExecutor(Executor exec, Params... 
    params) { ... exec.execute(mFuture); ... 
    return 
    this; } 
    public 
    static 
    void 
    execute(Runnable runnable) { sDefaultExecutor.execute(runnable); } 
    
  

看一下SerialExecutor的实现

private static class SerialExecutor implements Executor { 
    // 存储待执行的异步任务 final ArrayDeque 
  
    mTasks = 
   new ArrayDeque 
   
     (); Runnable mActive; 
    public 
    synchronized 
    void 
    execute( 
    final Runnable r) { 
    // 其实并没有马上执行,而是添加到队列mTasks中, 进行一个排队 mTasks.offer( 
    new Runnable() { 
    public 
    void 
    run() { 
    try { r.run(); } 
    finally { 
    // 一个任务执行完后,再执行下一个 scheduleNext(); } } }); 
    // 当前没有异步任务执行时,启动开始执行 
    if (mActive == 
    null) { scheduleNext(); } } 
    protected 
    synchronized 
    void 
    scheduleNext() { 
    if ((mActive = mTasks.poll()) != 
    null) { 
    // 使用另外一个线程池分配线程,并执行任务 THREAD_POOL_EXECUTOR.execute(mActive); } } } 
    
  
/ @hide */ public static void setDefaultExecutor(Executor exec) { sDefaultExecutor = exec; }

该方法是隐藏,但可使用反射,设置一个线程池。

5、Loader&LoaderManager

上面三种异步方式都可以用来加载一些耗时的数据,但有时我们加载数据的过程与Activity、Fragment的生命息息相关的。所以在使用上面说的那几种异步方式进行异步数据加载时,是需要去考虑Activity(Fragment)的生命周期是处于哪个阶段的。于是Android在Android 3.0以后引入了LoaderManager,主要用于执行一些耗时的异步数据加载操作,并根据Activity生命周期对异步处理进行调整,LoaderManager可以解决的问题包括:

  1. 加载的数据有变化时,会自动通知我们,而不自己监控数据的变化情况,如:用CursorLoader来加载数据库数据,当数据库数据有变化时,可是个展示变化的数据
  2. 数据的请求处理时机会结合Activity和Fragment的生命周期进行调整,如:若Acivity销毁了,那就不会再去请求新的数据

使用该方法加载数据涉及到两个类重要的类,Loader和LoaderManager:

Loader:该类用于数据的加载 ,类型参数D用于指定Loader加载的数据类型

public class Loader 
  
    { } 
  

一般我们不直接继承Loader,而是继承AsyncTaskLoader,因为Loader的加载工作并不是在异步线程中。而AsyncTaskLoader实现了异步线程,加载流程在子线程中执行。注意:对该类的调用应该在主线程中完成。

注意:其实AsyncTaskLoader内部实现异步的方式是使用AsyncTask完成的,上面我们说过AsyncTask的内部是有一个排队机制,但AsyncTaskLoader内部使用AsyncTask进行数据异步加载时,异步任务并不进行排队。而直接又线程池分配新线程来执行。

6、总结

我们来总结一下异步处理的方式,以及每种处理方式适合什么样的场景

  • 直接使用Thread实现方式,这种方式简单,但不是很优雅。适合数量很少(偶尔一两次)的异步任务,但要处理的异步任务很多的话,使用该方式会导致创建大量的线程,这会影响用户交互。
  • HandlerThread,这种方式适合子线程有序的执行异步操作,异步任务的执行一个接着一个。
  • AsyncTask, 通常用于耗时的异步处理,且时效性要求不是非常高的那种异步操作。如果时效性要求非常高的操作,不建议使用这个方式,因为AsyncTask的默认实现是有内部排队机制,且是整个应用的AsyncTask的任务进行排队,所以不能保证异步任务能很快的被执行。
  • LoaderManager,当请求处理时机需要根据Activity的生命周期进行调整,或需要时刻监测数据的变化,那LoaderManager是很不错的解决方案。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/207540.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午1:39
下一篇 2026年3月19日 下午1:39


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号