我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android中实现多线程操作的几种方式

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android中实现多线程操作的几种方式

前言

多线程一直是一个老大难的问题,首先因为它难以理解,其次在实际工作中我们需要面对的关于线程安全问题也并不常见,今天就来总结一下实现多线程的几种方式,可能并不全面,还请各位看官多多补充。

最基础的方式

继承Thread类并实现run()方法


class MyThread extends Thread{
 @Override
 public void run() {
  System.out.println("我是子线程");
 }
}

new MyThread().start();

匿名内部类


public class ManyThread {
 public static void main(String[] args) {
  new Thread(){
   public void run() {
    System.out.println("我是子线程");
   };
  }.start();
 }
}

实现Runnable接口


class MyRunnable implements Runnable{
 @Override
 public void run() {
  System.out.println("我是子线程");
 }
}
Thread thread = new Thread(new MyRunnable());
thread.start();

太简单了吧!!别着急,这只是热身~

callable+FutureTask

Callable接口类似于Runnable,区别在于使用callable可以拿到结果。
FutureTask实现了RunnableFuture,


public class FutureTask<V> implements RunnableFuture<V> {
    ...
}

RunnableFuture又实现了Runnable和Future


public interface RunnableFuture<V> extends Runnable, Future<V> {
    
    void run();
}

所以FutureTask既可以当Runnable使用又可以当Future使用get()来拿到结果

future的API:get():当任务结束后,返回一个结果,如果调用时,任务还没有结束,则会阻塞线程,直到任务执行完毕。可以设置等待时间,超出时间或者返回结果为null,则抛出异常cancel():


//自定义CallAble并实现call()方法
class MyCallable implements Callable<Integer>{

 @Override
 public Integer call() throws Exception {
  
  int a = 1;
  for (int i = 0; i <100000; i++) {
   a++;
  }
  Thread.sleep(2000);
  return a;
 }
 
}

public static void main(String[] args) {
  
                //新建CallAble
  MyCallable callable = new MyCallable();
                
                //新建FutureTask任务
  FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
                
                //新建Thread并与FutureTask关联
  Thread thread = new Thread(futureTask, "A");
  thread.start();
  int sum;
  try {
                        //拿到callable的结果
   sum = futureTask.get();
   System.out.println(sum);
  } catch (InterruptedException e) {
   e.printStackTrace();
  } catch (ExecutionException e) {
   e.printStackTrace();
  }
 }

线程池

线程池的创建方式有两种,

手动创建线程池


public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

要注意线程池的几个参数:

  • corePoolSize :核心线程数
  • maximumPoolSize :允许创建的最大线程数
  • keepAliveTime :这是空闲线程保持存活的最大时间。
  • unit : keepAliveTime的时间单位
  • workQueue :任务队列,保存等待执行的任务的阻塞队列
  • threadFactory :用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有含义的名字
  • handler :饱和策略,当队列和线程池都满了,说明线程处于饱和状态,必须采取一种策略处理提交的新任务。默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。

向线程池提交任务execute()和submit():


ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
//使用execute提交任务
threadPoolExecutor.execute(new Runnable() {
    @Override
    public void run() {
 System.out.println("我是线程池");
    }
});

//之前说的callable
MyCallable callable = new MyCallable();
//使用submit提交任务 返回一个future
Future<Integer> future = threadPoolExecutor.submit(callable);
try {
    //通过future拿到执行callable的结果
    int x = future.get();
    System.out.println(x);
} catch (InterruptedException e) {
    //中断异常
    e.printStackTrace();
} catch (ExecutionException e) {
    //无法执行任务异常
    e.printStackTrace();
}finally {
    //关闭线程池
    threadPoolExecutor.shutdown();
}

使用自定义线程池要合理的配置各个参数。

  • 根据任务的性质:CPU密集型还是IO密集型设置核心线程数的大小,cpu密集型应配置尽可能小的线程,可以配置为 CPU个数+1。由于IO密集型任务线程并不是一直在执行任务,应配置尽可能多的线程,可以配置为 2*cpu个数
  • 优先级不同的任务,可以使用优先级队列PriorityBlockingQueue来处理,它可以让优先级高的任务先执行
  • 建议使用有界队列,有界队列能增加系统的稳定性和预警能力。

使用Executors创建线程池

这种创建方式本质上也是对ThreadPoolExecutor的参数进行不同的配置,每条创建后边把源码中不同的配置放到后边,以便更清晰的展示出来


//会根据需要创建新线程的线程池
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//核心线程数设置为0,最大线程数则是无界,的也就是说某些情况下会有无限多的线程被创建,从而导致一些问题,空闲线程最大等待时长是60秒
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

//使用单个worker线程
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
//核心线程数和最大线程数都设置为1,并使用无界队列作为线程池的工作队列,其容量为无限大,某些情况下会导致队列中有太多的任务而出错
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

//可重用固定线程数的线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
//核心线程数和最大线程数都设置为创建时传入的数量,空闲等待时长为0,说明空闲线程会立即终止,而任务队列容量也是无限大,可能会导致同样的问题
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

//可以用来在给定延时后执行异步任务或者周期性执行任务
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
//核心线程的个数为指定的数量,允许的最大线程数是无限的
public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory, handler);
}

Android中特有的实现多线程

使用HandlerThread

HandleThread本质上就是一个线程,其继承了Thread。它的内部有自己的looper对象,并在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()开启消息循环,这样在使用中就允许在HandlerThread中创建Handler了

HandlerThread和普通的Thread是有显著的区别的,普通的Thread主要在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务,由于handlerThread的run方法是一个无限循环,因此当明确不需要再使用HandlerThread时,可以通过其quit或者quitSafely方法来终止线程的执行。


public class HandlerThread extends Thread {
    
    Looper mLooper;
    private @Nullable Handler mHandler;

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    ...
}

具体使用:


        //创建handlerThread,标记一个名字
        HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
        //启动线程
        handlerThread.start();

        //创建工作线程的handler,关联handlerThread的Looper对象。
        //复写handleMessage处理消息
        Handler myHandler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {

                Log.i(msg.what + msg.obj.toString());
                super.handleMessage(msg);
            }
        };

        //定义消息
        Message msg = new Message();
        msg.what = 1;
        msg.obj = "A";
        //发送消息到其绑定的消息队列中
        myHandler.sendMessage(msg);
        //结束线程,停止线程的消息循环
        handlerThread.quit();

使用IntentService

本质上是一个Service,可以看做是Service和HandlerThread的结合体,它继承了Service并且是一个抽象类,因此需要创建它的子类才能使用IntentService。可以用于执行后台耗时任务,由于是服务,所以它的优先级比单纯的线程要高很多,并且不容易被系统杀死。有如下几个特点

  • IntentService是继承自service并处理异步请求的一个类,其内部有一个工作线程处理耗时请求
  • 任务执行完毕,会自动销毁
  • 如果IntentService启动多次,每个耗时操作会以队列的方式在IntentService的onHandleIntent方法中依次执行,串行执行结束后,会自动销毁。

首先看一下IntentService的源码:


//继承自service
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    private volatile ServiceHandler mServiceHandler;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //处理消息时调用onHandleIntent方法 我们需要重写这个方法实现自己的处理逻辑
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
    ...
    
    @Override
    public void onCreate() {
        super.onCreate();
        //创建handlerThread并启动
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        //初始化handler并绑定handlerThread的looper
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //通过handler发送消息给HandlerThread处理
        mServiceHandler.sendMessage(msg);
    }
    ...

当IntentService被启动时,它的onCreate方法被调用,onCreate方法会创建一个HandlerThread,然后使用它的Looper来构造一个Handler对象,这样通过Handler发送的消息最终会在HandlerThread中执行。每次启动IntentService,它的onStartCommand方法就会调用一次,IntentService在onStartCommand中处理每个后台任务的Intnet。onStartCommond调用了onStart,在onStart方法中,IntentService通过handler发送一个消息,这个消息会在HandlerThread中处理。当onHandleIntent方法执行结束后,IntentService会通过stopSelf方法尝试停止服务,onHandlerIntent是一个抽象方法,需要我们在子类中实现,它的作用是从Intent参数中区分具体的任务并执行这些任务。

如何使用?

  • 新建service类并继承自IntentService
  • 实现service的构造方法
  • 在manifast.xml中注册服务
  • 在服务的onHandleIntent方法中实现业务逻辑

自定义一个IntentService


public class MyIntentService extends IntentService {

    public MyIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if(intent != null){
            String action = intent.getAction();
            if(action.equals("A")){
                doSomeThingA();
            }else if(action.equals("B")){
                doSomeThingB();
            }
        }
    }
}

JobIntentService/JobScheduler

系统不允许后台应用创建后台服务,因此8.0引入了Context.startForegroundService(),以在前台启动新服务,
由于Android O的后台限制,创建后台服务需要使用JobScheduler来由系统进行调度任务的执行,而使用JobScheduler的方式比较繁琐,8.0及以上提供了JobIntentService帮助开发者更方便的将任务交给JobScheduler调度,其本质是Service后台任务在他的OnhandleWork()中进行,子类重写该方法即可。使用较简单。


public class SimpleJobIntentService extends JobIntentService {
    
    static final int JOB_ID = 1000;

    
    static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, SimpleJobIntentService.class, JOB_ID, work);
    }

    //在这里执行耗时任务
    @Override
    protected void onHandleWork(Intent intent) {
        Log.i("SimpleJobIntentService", "Executing work: " + intent);
        String label = intent.getStringExtra("label");
        if (label == null) {
            label = intent.toString();
        }
        toast("Executing: " + label);
        for (int i = 0; i < 5; i++) {
            Log.i("SimpleJobIntentService", "Running service " + (i + 1)
                    + "/5 @ " + SystemClock.elapsedRealtime());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
        Log.i("SimpleJobIntentService", "Completed service @ " + SystemClock.elapsedRealtime());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        toast("All work complete");
    }

    final Handler mHandler = new Handler();

    // Helper for showing tests
    void toast(final CharSequence text) {
        mHandler.post(new Runnable() {
            @Override public void run() {
                Toast.makeText(SimpleJobIntentService.this, text, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

activity中通过调用enqueueWork()方法来启动


    Intent workIntent = new Intent();
    num++;
    Log.d("houson", "onClick: "+num);
    workIntent.putExtra("work","work num:"+num);
    //调用enqueueWork方法
    MyJobIntentService.enqueueWork(getApplicationContext(),workIntent);

WorkManager

根据需求的不同,Android为后台任务提供了多种解决方案,如JobScheduler,Loader,Service等。如果这些API没有被适当地使用,可能会消耗大量的电量。WorkManager的出现,为应用程序中那些不需要及时完成的任务,提供统一的解决方案,以便在设备电量和用户体验之间达到一个比较好的平衡。

WorkManager最低可以兼容API Level 14,在API Level 23+,通过JobScheduler来完成任务,23一下的设备,通过AlarmManager和BroadCastReceivers组合完成任务。

WorkManager主要针对不需要及时完成的任务,如发送日志,同步应用程序数据,备份等。并且可以保证任务一定会被执行,即使应用当前不在运行中,WorkManager有自己的数据库,关于任务的所有信息和数据都保存在这个数据库中。因此只要你的任务交给了WorkManager,哪怕你的应用程序彻底退出,或者设备重新启动,WorkManager依然能够保证完成你交给的任务。
如何使用?

添加依赖


dependencies {
    def versions = "2.2.0"
    implementation "androidx.work:work-runtime:$versions"
}

定义Worker任务:

worker是任务的执行者,是一个抽象类,需要继承它实现要执行的任务。


//继承worker,自定义worker
class MyWork extends Worker{

    public MyWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    //执行耗时任务
    @NonNull
    @Override
    public Result doWork() {
        //拿到传进来的数据
        String input_data = getInputData().getString("input_data");
        // 任务执行完成后返回数据
        Data outputData = new Data.Builder().putString("output_data", "Success!").build();
//        执行成功返回Result.success()
//        执行失败返回Result.failure()
//        需要重新执行返回Result.retry()
        return Result.success(outputData);
    }
}

使用WorkRequest配置任务。

WorkRequest指定让哪个 Woker 执行任务,指定执行的环境,执行的顺序等。要使用它的子类 OneTimeWorkRequest(执行一次) 或 PeriodicWorkRequest(周期执行)。
通过WorkRequest配置我们的任务何时运行以及如何运行
 


       //设置任务的触发条件
        Constraints constraints = new Constraints.Builder()
                .setRequiresCharging(true) // 在充电时执行
                .setRequiresStorageNotLow(true) // 不在存储容量不足时执行  
                
                // 网络状态
                //NOT_REQUIRED--没有要求
                //CONNECTED--网络连接
                //UNMETERED--连接无限流量的网络
                //METERED--连接按流量计费的网络
                //NOT_ROAMING--连接非漫游网络
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresDeviceIdle(true) // 在待机状态下执行,需要 API 23
                .setRequiresBatteryNotLow(true)// 不在电量不足时执行
                .build();
        //然后将该Constraints设置到WorkRequest。WorkRequest是一个抽象类,
        //它有两种实现OneTimeWorkRequest和PeriodicWorkRequest,分别对应的是一次性任务和周期性任务。
        //定义要传递的数据
        Data inputData = new Data.Builder().putString("input_data", "Hello").build();
        OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(MyWork.class)
                .setConstraints(constraints)
                .setInputData(inputData)
                .build();

WorkManager

将任务提交给系统。管理任务请求和任务队列,发起的 WorkRequest 会进入它的任务队列。WorkManager.enqueue()方法会将你配置好的WorkRequest交给系统来执行。


WorkManager.getInstance(this).enqueue(oneTimeWorkRequest);

观察任务的状态

任务在提交给系统后,通过WorkInfo获知任务的状态,WorkInfo包含了任务的id,tag,以及Worker对象传递过来的outputData,以及任务当前的状态。有三种方式可以得到WorkInfo对象。

通过LiveData,我们便可以在任务状态发生变化的时候,收到通知。同时通过LiveData的WorkInfo.getOutputData(),得到从Worker传递过来的数据。


WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.getId()).observe(
                MainActivity.this, new Observer<WorkInfo>() {
                    @Override
                    public void onChanged(WorkInfo workInfo) {
                        Log.d("onChanged()->", "workInfo:"+workInfo);
                        if (workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED) {
                            String outputData = workInfo.getOutputData().getString("output_data");
                            Log.d("onChanged()->", "outputData:"+outputData);
                        }
                    }
                }
        );

取消任务


WorkManager.getInstance(MainActivity.this).cancelAllWork();

以上就是WorkManager的一些基本使用,更详细的可以参照官方文档或者其他文章,这里就不仔细讲解了。工作中,我们经常需要处理后台任务,如果处理后台任务所采用的API没有被正确使用,那么很可能会消耗大量设备的电量。Android出于设备电量的考虑,为开发者提供了WorkManager,旨在将一些不需要及时完成的任务交给它来完成。

使用协程

协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码,协程是轻量级的线程,为什么是轻量的?因为它基于线程池API。协程可以使用阻塞式的方式写出非阻塞式的代码,解决回调地狱的问题。

协程有如下的特点:

  • 轻量:您可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。
  • 内存泄漏更少:使用结构化并发机制在一个作用域内执行多项操作。
  • 内置取消支持:取消操作会自动在运行中的整个协程层次结构内传播。
  • Jetpack 集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发。

引入协程


dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
}

使用


 fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello") 
}

协程的使用当然不是这么简单,有很多的API,这里限于篇幅,就不深入讲解了。

AsyncTask

是一个轻量级的异步任务类,在高版本中已经过时了。但研究一下还是很有意义的。他可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新ui。AsyncTask封装了Thread和Handler,通过AsyncTask可以更加方便的执行后台任务以及在主线程中访问ui。

AsyncTask提供了四个核心方法:

  • onPreExecute:在主线程中执行,在异步任务执行之前,此方法会被调用,一般用于一些准备工作
  • doInBackground:在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的输入参数,此方法中可以通过publishProgress来更新任务的进度。
  • onProgressUpdata(Progress...values):在主线程中执行,当后台任务的执行进度发生改变时调用此方法
  • onPostExecute:在主线程中执行,在异步任务执行之后,此方法会被调用,result参数是后台任务的返回值,即doInbackground的返回值

AsyncTask在使用过程中有一些限制条件:

  • AsyncTask必须在主线程中加载,也就是第一次访问AsyncTask必须发生在主线程
  • AsyncTask的对象须在主线程中创建
  • execute方法必须在ui线程调用
  • 不要在程序中直接调用onPreExecute、onPostExecute、doInBackground、onProgressUpdate方法
  • 一个AsyncTask对象只能执行一次,也就是只能调用一次execute方法,否则会报异常

原理分析:

从execute方法开始分析,调用executeOnExecutor方法,sDefaultExecutor是一个串行的线程池如下:


    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    
    
    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;
       
        onPreExecute();

        mWorker.mParams = params;
        //线程池开始执行 封装为Futuretask交个线程池处理
        exec.execute(mFuture);

        return this;
    }

sDefaultExecutor是一个串行线程池,一个进程中所有的AsyncTask任务都在这个线程池中排队执行。首先系统会把AsyncTask的Params参数封装为FutureTask对象,这里FutureTask充当Runnable,接着这个FutureTask会交给SerialExecutor的execute方法处理,SerialExecute的execute方法首先把FutureTask对象插入到任务队列中,接着调用scheduleNext方法执行FutureTask任务,从队列中取出任务交给THREAD_POOL_EXECUTOR线程池去真正执行任务。InternalHandler用于将执行环境从线程池切换到主线程。


    //串行线程池
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    //线程池用于真正执行任务
    public static final Executor THREAD_POOL_EXECUTOR;
    //用于将执行环境从线程池切换到主线程
    private static InternalHandler sHandler;
   
    //初始化执行任务的线程池
    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    
    //用于调度任务的串行线程池
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            
            //从队列中取出任务交个THREAD_POOL_EXECUTOR线程池去真正执行任务
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

再看一下任务future,call方法中调用postResult方法,通过handler发送消息,最终在handleMessage方法中进行处理。


    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
     mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

结语

以上介绍了几种实现多线程操作的方式,有些并没有深入分析,当然了,实现多线程的方式不止以上的几种,还请各位看官多多补充,如有错误的地方还请指出。

到此这篇关于Android中实现多线程操作的几种方式的文章就介绍到这了,更多相关Android多线程操作 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android中实现多线程操作的几种方式

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

java中实现多线程的几种方式

Java多线程的使用有三种方法:继承Thread类、实现Runnable接口和使用Callable和Future创建线程。一、继承Thread类实现方式很简单,只需要创建一个类去继承Thread类然后重写run方法,在main方法中调用该类实例对象的start
java中实现多线程的几种方式
2016-12-01

全面详解Android实现多线程的几种方式

Android实现多线程的几种方式有:1. 使用Thread类:可以通过继承Thread类或者实现Runnable接口来创建线程类,并重写run方法来定义线程执行的代码。然后调用start方法启动线程。示例代码:```javapublic
2023-09-14

java多线程的实现方式有哪几种

在Java中,有以下几种实现多线程的方式:1. 继承Thread类:创建一个继承自Thread类的子类,并实现run()方法。通过调用子类的start()方法启动新线程。```javaclass MyThread extends Threa
2023-09-26

Android开发之子线程操作UI的几种方法

在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法:在看方法之前需要了解一下Android中的消息机制。 方法1 Activity.runOnUiThread 方法
2022-06-06

C# 使用多线程的几种方式

在C#中,有几种方式可以使用多线程:1. 使用Thread类:可以创建一个新线程并在其中执行指定的方法。可以使用Thread类来启动和停止线程,并检查线程的状态。以下是一个使用Thread类的例子:```csharpusing System
2023-09-15

java 多线程的几种实现方法总结

java 多线程的几种实现方法总结1.多线程有几种实现方法同步有几种实现方法多线程有两种实现方法,分别是继承Thread类与实现Runnable接口同步的实现方面有两种,分别是synchronized,wait与notifywait():使
2023-05-30

python实现多线程的方法有哪几种

在Python中,有多种方法可以实现多线程,其中最常用的有以下几种:使用 threading 模块:Python的 threading 模块提供了多线程编程的支持,可以通过创建 Thread 对象来实现多线程。可以通过继承 Thread 类
python实现多线程的方法有哪几种
2024-03-08

python实现多线程的两种方式

目前python 提供了几种多线程实现方式 thread,threading,multithreading ,其中thread模块比较底层,而threading模块是对thread做了一些包装,可以更加方便的被使用。 2.7版本之前pyt
2022-06-04

java 实现开启异步线程的几种方式

在Java中,有多种方式可以实现异步线程以避免在主线程中执行耗时操作导致界面卡顿的问题。以下是几种常用的方式: 使用Thread类:可以使用Thread类来创建一个新的线程,并在其run()方法中执行耗时操作。例如: new Thread(
2023-08-17

java多线程加锁的方式有哪几种

在Java中,有以下几种方式实现多线程加锁:使用synchronized关键字:synchronized关键字可以修饰方法或代码块,保证同一时间只有一个线程可以执行被修饰的代码,其他线程需要等待锁释放才能执行。使用ReentrantLock
java多线程加锁的方式有哪几种
2024-02-29

Android实现View滑动的几种方式

什么是View?实现View滑动的方式有哪些? 1. 关于View我们需要知道的(1)什么是View? Android中的View类是所有UI控件的基类(Base class),也就是说我们平时所有到的各种UI控件,比如Button
2022-06-06

Android 实现GIF播放的几种方式

1. Glide播放 Glide是Android上比较常见的图片加载框架,其介绍可以看Android Glide简单使用。 布局文件,GIF文件可以在ImageView里面显示 RelativeLayout xmlns:android="h
2023-08-18

Python多线程实现同步的四种方式

临界资源即那些一次只能被一个线程访问的资源,典型例子就是打印机,它一次只能被一个程序用来执行打印功能,因为不能多个线程同时操作,而访问这部分资源的代码通常称之为临界区。 锁机制 threading的Lock类,用该类的acquire函数进行
2022-06-04

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录