Android 异步处理

2018-06-17  本文已影响67人  tuacy

       咱们都知道在Android主线程中处理耗时任务会产生ANR,所有很多耗时的任务都需要异步处理。Android里面的有四种常见的异步处理类见:Thread、HandlerThread、AsyncTask、IntentService。

一、Thread

       线程是执行任务的最基本的单元,当然了在Android中线程分为两大部分:主线程、子线程。Thread线程主要有以下几种状态:

       当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

1.1、Thread实现方式

       实现线程有两种方式:继承Thread类、以及实现Runnable接口

1.1.1、继承Thread类

    private class YourThread extends Thread {

        YourThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            //执行耗时操作,做你想做的
        }
    }

    YourThread yourThread = new YourThread("我的线程");
    yourThread.start();

1.1.2、实现Runnable接口

    private class YourRunnable implements Runnable {
        @Override
        public void run() {
            //执行耗时操作,做你想做的
        }
    }

    Thread yourThread = new Thread(new YourRunnable(), "我的线程");
    yourThread.start();

       Thread两种实现方式,推荐使用实现Runnable接口的方式。因为 当继承了Thread类,则不能继承其他类,而实现Runnable接口可以,而且实现Runnable接口的线程类的多个线程,可以访问同一变量,而Thread则不能。

1.2、Thread 主要函数介绍

    /**
     * 线程运行时需要执行的代码
     */
    public void run()

    /**
     * 启动线程
     */
    public synchronized void start()

    /**
     * 线程休眠,交出CPU,让CPU去执行其他的任务,然后线程进入阻塞状态,sleep方法不会释放同步锁
     *
     * @param millis 休眠时间,单位毫秒
     */
    public static void sleep(long millis) throws InterruptedException
    /**
     * @param millis 休眠时间,单位毫秒
     * @param nanos  额外的休眠时间,单位纳秒
     */
    public static void sleep(long millis, int nanos) throws InterruptedException

    /**
     * 线程让步,使当前线程交出CPU,当前线程重置为就绪状,然后又通过和其他线程公平竞争来看谁先执行。yield方法不会释放锁
     */
    public static native void yield()

    /**
     * 等待线程终止,再继续执行。直白的说 就是发起该子线程的线程 只有等待该子线程运行结束才能继续往下运行
     */
    public final void join() throws InterruptedException
    /**
     * @param millis 等待时间,单位毫秒
     */
    public final void join(long millis) throws InterruptedException
    /**
     * @param millis 等待时间,单位毫秒
     * @param nanos  额外等待时间,单位纳秒
     */
    public final void join(long millis, int nanos) throws InterruptedException

    /**
     * 中断线程,我们通过interrupt方法和isInterrupted()方法来停止正在运行的线程,注意只能中断已经处于阻塞的线程
     */
    public void interrupt()

    /**
     * 获取当前线程id
     */
    public long getId()

    /**
     * 设置获取当前线程的名字
     */
    public final void setName(String name)
    public final String getName()

    /**
     * 设置获取当前线程优先级
     */
    public final void setPriority(int newPriority)
    public final int getPriority()

    /**
     * 设置和判断是否是守护线程 setDaemon()函数一点要在start()函数之前调用
     */
    public final void setDaemon(boolean on)
    public final boolean isDaemon()

    /**
     * 获取当前线程
     */
    public static native Thread currentThread()

1.2.1、sleep(),wait(),yield(),join()函数的区别。

wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用。

1.2.2、用户线程和守护线程

       Java里面将线程分为User Thread(用户线程)和Daemon Thread(守护线程)两种。Daemon Thread是运行在后台的线程。我们Android里面的MainThread就是User Thread而不是Daemon Thread。Daemon Thread一般用来给User Thread提供服务。

调用setDaemon()函数去设置是否是守护线程的时候,该函数一点要在start()函数之前调用。否则无效。

1.3、如何优雅的终止Thread

       JDK里面Thread提供了一个stop()方法,但是stop()方法是一个被废弃的方法。为什么stop()方法被废弃而不被使用呢?因为stop()方法太过于暴力,会强行把执行一半的线程终止。这样会就不会保证线程的资源正确释放,通常是没有给与线程完成资源释放工作的机会,因此会导致程序工作在不确定的状态下。既然stop()方法不让用了,那咱们还有哪些方法来终止Thread呢。

1.3.1、监视某个退出标志位

       简单地设置一个标志位,需要配合while()循环使用,并通过设置这个标志来控制循环是否退出。

    public class YourThread extends Thread {

        public volatile boolean mExit = false;

        @Override
        public void run() {
            super.run();
            while (!mExit) {
                System.out.println("I'm running");
            }
        }
    }

1.3.2、使用interrupt方法终止线程

       调用interrupt()方法之后interrupted()结果为true,最后采用抛异常的方式来终止线程,代码如下:

    public class YourThread extends Thread {

        @Override
        public void run() {
            try {
                while (true) {
                    if (interrupted()) {
                        throw new InterruptedException();
                    }
                    System.out.println("I'm running");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

二、HandlerThread

       HandlerThread是实现了Lopper的线程(归根到底他还是一个线程)。为什么需要HandlerThread这东西呢,因为Android中为了同时完成多个任务,经常会在应用程序当中创建多个线程。这个时候为了让多个线程之间能够方便的通信,这个时候我们通常会使用Handler实现线程间的通信。但是Handler对应的线程必须包含Looper(Android主线程默认已经包含Looper了)。默认线程是没有Looper的。不过我们可以自己去创建Looper,为线程创建Looper的方法如下:在线程run()方法当中先调用Looper.prepare()初始化Looper,然后再run()方法最后调用Looper.loop()启动循环。那,现在为了方便大家不用我们每次都去写这些Looper的创建。Android API就给我们提供了HandlerThread。

记住,想通过Handler把消息发送到那个线程里面去,对应的线程必须包含Looper。

2.1、HandlerThread使用

       HandlerThread使用步骤:

ublic class HandlerThreadActivity extends MobileBaseActivity {

    private HandlerThread mHandlerThread;
    private Handler       mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建一个线程,线程名字:handler-thread
        mHandlerThread = new HandlerThread("handler-thread");
        //开启一个线程
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
                Log.d("handler ", "消息: " + msg.what + "  线程: " + Thread.currentThread().getName());
            }
        };
        //在主线程给handler发送消息
        mHandler.sendEmptyMessage(1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //释放资源
        mHandlerThread.quit();
    }
}

千万要记得HandlerThread用完之后要调用quit()方法退出循环。

2.2、HandlerThread源码实现过程

       上面对HandlerThread的使用做了一个很简单的介绍,接下来咱们就来具体的看下HandlerThread的源码。对于HandlerThread得明确他是一个带Looper的线程。

       HandlerThread源文件也非常的简单,如下

public class HandlerThread extends Thread {

    ...

    /**
     * 线程开始loop之前的回调
     */
    protected void onLooperPrepared() {
    }

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

    /**
     * 获取Looper对象
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * 退出looper循环()
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * 和quit()方法一样,只不过这个更加的安全
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    ...

}

       这里我们主要关注run()方法里面的内容,其他的方法都很简单,大家肯定一看就懂。run()方法里面开始的时候调用了Looper.prepare();,run()方法快结束的时候调用了Looper.loop();这样就让HandlerThread循环起来了。run()方法里面大家有发现notifyAll();的调用把。这干啥的呀。来我们先看getLooper()方法了,是不是在getLopper()方法里面mLooper = null的时候调用了wait()。当mLooper=null的时候getLopper()就一直等在这里了,等mLooper不为null的时候才返回mLooper。notifyAll()就是run()方法里面获取到了mLooper之后通知wait()不用等了,可以去获取了。

三、AsyncTask

       AsyncTask是Android提供的另一个异步处理的工具类,它对Thread和Handler进行了封装,方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。使用AsyncTask的时候,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可。

       AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。这四个方法会在AsyncTask的不同时期进行自动调用,我们只需要实现这几个方法的内部逻辑即可。这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result。

四个重要方法介绍

三种泛型介绍

3.1、AsyncTask使用

       AsyncTask使用也非常简单,主要的关注点都在我们的业务逻辑上。比如我们来实现一个文件下载的任务。伪代码如下:

public class AsyncTaskActivity extends MobileBaseActivity {


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String[] urls = {"http://blog.csdn.net/iispring/article/details/47115879"};

        DownloadTask downloadTask = new DownloadTask();
        //开始执行任务
        downloadTask.execute(urls);
    }

    /**
     * Params - > String:文件url
     * Progress - > Float 文件下载进度
     * Result - > Long 下载的文件大小
     */
    private class DownloadTask extends AsyncTask<String, Float, Long> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //TODO:UI线程执行,开始执行任务的回调
        }

        @Override
        protected Long doInBackground(String... strings) {
            //TODO:1. 从参数里面获取到文件的下载地址。2. while循环读取网络文件信息,并且实时调用publishProgress告诉下载进度
            downloadTask(strings[0]);
            return null;
        }

        @Override
        protected void onProgressUpdate(Float... values) {
            super.onProgressUpdate(values);
            //TODO:下载进度回调上来
        }

        @Override
        protected void onPostExecute(Long aLong) {
            super.onPostExecute(aLong);
            //TODO:任务执行完成
        }

        private void downloadTask(String path) {
            //模拟耗时操作
            int count = 0;
            try {
                while (count < 100) {
                    count++;
                    Thread.sleep(3 * 1000);
                    publishProgress((float) count);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3.2、AsyncTask源码实现过程

       AsyncTask源代码的分析。

       先从构造函数开始,

构造函数

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        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);
                }
            }
        };
    }

       就是初始化了三个变量,mHandler、mWorker、mFuture。mHandler是用来post消息的,把onProgressUpdate()和onPostExecute()post到主线程里面去执行。

       接着,如果AsyncTask想执行任务,会调用execute()函数,我们直接看下execute()函数。

execute()

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

       execute()函数直接调用了executeOnExecutor()函数。注意第一个参数是sDefaultExecutor。他是一个static修饰变量,是所有AsyncTask共有的。

sDefaultExecutor

    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() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

       分析代码我们发现sDefaultExecutor会把所有的任务依次加入到线程池中,而且保证了先进来的任务加入线程池,执行完一个任务就加入一个线程池。其中线程池THREAD_POOL_EXECUTOR也是一个static变量。

executeOnExecutor()

    @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;
        exec.execute(mFuture);

        return this;
    }

       先判断了状态,看的出来一个AsyncTask只能excute一次。在接着调用onPreExecute(); 回调告诉任务准备开始了。最后exec.execute(mFuture);把mFuture加入到sDefaultExecutor里面去。mFuture在构造函数里面已经声明了。从构造函数得知会执行mWorker变量的call()函数,call()函数执行完之后会执行mFuture对象的done()函数。

先执行mWorker的call()函数

        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;
            }
        };

mWorker的call()函数执行完之后执行mFuture的done()函数

        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);
                }
            }
        };

       mWorker的call()函数里面调用了doInBackground()吧,并且是在后台线程中执行的。call()函数的最后调用了postResult(result);把结果回调给主线程。

       我们看下postResult()是怎么回调到主线程的。

postResult()函数

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

很熟悉把,getHandler()对应的是哪个线程,就去哪个线程执行了。

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

       其中两种消息类型MESSAGE_POST_RESULT、MESSAGE_POST_PROGRESS一个用来回调执行结果的,一个用来回调执行进度的。一个对应onPostExecute()的回调,一个对应onProgressUpdate()的回调。当然了如果你想触发onProgressUpdate()执行进度的回调,你的自己在doInBackground()里面主动的去调用publishProgress()函数。

       AsyncTask源码的分析过程关键是抓住onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute函数的调用位置和所处的线程。AsyncTask的简单分析就到这里有疑问也可以留言。

四、IntentService

       IntentService 是继承自 Service 并处理异步任务的一个类。IntentServicer最大的特点是用完即走。处理玩异步任务IntentService会自动结束。

4.1、IntentService的使用

       IntentService有一个非常重要的方法onHandleIntent(),onHandleIntent()当某个请求需要处理时,这个方法会在工作者线程被调用,一次仅仅会有一个请求被处理。所以我们在onHandleIntent()函数里面处理我们的异步逻辑。

写一个非常的下载例子

public class IntentServiceActivity extends MobileBaseActivity {

    public final static String ACTION_TYPE_THREAD = "action.type.thread";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 启动异步任务
        DownloadIntentService.startDownload(IntentServiceActivity.this, "image path");
    }

    static class DownloadIntentService extends IntentService {

        public static final String EXTRA_DOWNLOAD_IMAGE = "com.tuacy.convenientinputedit.DOWNLOAD_IMAGE";

        public DownloadIntentService() {
            super("DownloadIntentService");
        }

        public static void startDownload(Context context, String path) {
            Intent intent = new Intent(context, DownloadIntentService.class);
            intent.putExtra(EXTRA_DOWNLOAD_IMAGE, path);
            context.startService(intent);
        }

        @Override
        protected void onHandleIntent(Intent intent) {
            if (intent != null) {
                String path = intent.getStringExtra(EXTRA_DOWNLOAD_IMAGE);
                downloadTask(path);
            }
        }

        private void downloadTask(String path) {
            //模拟耗时操作
            try {
                Thread.sleep(3 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //利用广播通知主界面更新
            Intent intent = new Intent(IntentServiceActivity.ACTION_TYPE_THREAD);
            intent.putExtra(EXTRA_DOWNLOAD_IMAGE, path);
            sendBroadcast(intent);
        }

        @Override
        public void onCreate() {
            super.onCreate();
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    }

}

IntentService的启动方式采用startService()。

4.2、IntentService源码分析

       IntentService的源码也非常的简单。我们做一个简单的分析。

public class IntentService extends Service {

    private volatile ServiceHandler mServiceHandler;

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

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    @Override
    public void onCreate() {

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    ...
}

       通过startService()方式启动Service之后会先回调onCreate()方法,然后回调onStart()或者onStartCommand()方法(android2.0以下调用onStart(),android2.0之上调用onStartCommand()方法)。onCreate()方法里面启动了一个HandlerThread线程。并且mServiceHandler绑定到HandlerThread线程上去了。后面通过mServiceHandler发送的消息都会在这个HandlerThread线程里面执行。onStart()的回调函数马上就发送了一个消息出去,消息到ServiceHandler类的handleMessage()里面处理。调用onHandleIntent()函数,之后调用stopSelf()整个IntentService也就自动停止了。


       以上就是对Android里面的异步处理做了一个简单的总结。

上一篇下一篇

猜你喜欢

热点阅读