Android中的线程

Posted 川峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中的线程相关的知识,希望对你有一定的参考价值。

本文主要是对android当中的线程相关的知识进行复习和总结。

new Thread

缺乏统一管理,无限制创建,可能占用过多系统资源导致死机或oom,不推荐。

AsyncTask

场景:需要知晓任务执行的进度,多个任务串行执行
缺点:生命周期和宿主的生命周期不同步,有可能发生内存泄漏,默认情况所有任务串行执行

class MyAsyncTask extends AsyncTask<String, String, String> {
    private static final String TAG = "MyAsyncTask";

    @Override
    protected String doInBackground(String... params) {
        for (int i = 0; i < 100; i++) {
            publishProgress(params[0] + " === "+ (i * 10));
        }
        return params[0];
    }

    @Override
    protected void onPostExecute(String result) {
        Log.e(TAG, "result: " + result);
    }

    @Override
    protected void onProgressUpdate(String... values) {
        Log.e(TAG, "onProgressUpdate: " + values[0]);
    }
}

测试代码:

 private void testAsyncTask() {
     int count = 100;
     for (int i = 0; i < count; i++) {
         //串行
         new MyAsyncTask().execute("MyAsyncTask[ " +i+ " ]");
         //并行
         new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "MyAsyncTask[ " +i+ " ]");
         final String name = "MyAsyncTask[ " +i+ " ]";
         //串行
         AsyncTask.execute(new Runnable() {
             @Override
             public void run() {
                 for (int i = 0; i < 100; i++) {
                     Log.e("MyAsyncTask", "onProgressUpdate:  " + name + " === "+ (i * 10) );
                 }
             }
         });
         //并行
         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
             public void run() {
                 for (int j = 0; j < 100; j++) {
                     Log.e("MyAsyncTask", "onProgressUpdate:  " + name + " === "+ (j * 10));
                 }
             }
         });
     }
 }

AsyncTask源码解析:

AsyncTask的三个泛型参数:

public abstract class AsyncTask<Params, Progress, Result>
  • Params: doInBackground方法的接受参数类型
  • Progress:onProgressUpdate方法的接受参数类型
  • Result:onPostExecute方法的接受参数类型,以及doInBackground方法的返回参数类型

AsyncTask的五个运行方法:

  • onPreExecute(): 在主线程中执行,在异步任务执行之前被调用,一般用于一些准备工作
  • doInBackground(Params… params) :在子线程运行,用来处理后台任务,params就是execute(Params… params)方法传递的参数
  • onProgressUpdate(Progress… values):在主线程运,在doInBackground中可以调用publishProgress来更新进度
  • onPostExecute(Result result):在主线程运行,后台任务处理完毕调用,并返回doInBackground的结果
  • publishProgress(Progress… values): 在doInBackground中调用,来开启进度更新

注意:

  • AsyncTask的实例必须在UI线程中创建
  • execute必须在UI线程中调用
  • 不能在doInBackGround中更改UI组件信息
  • 一个AsyncTask的实例只能执行一次任务,执行第二次会抛出异常

AsyncTask构造函数

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    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 对象,使用 Looper.getMainLooper().然后创建了两个对象:WorkerRunnableFutureTask,分别赋值为mWorkermFuture。这两个对象初始化都实现了两个回调方法,当用户执行了execute方法的时候在特定情况下会触发这两个对象的回调方法。

这里能够看到:doInBackground是在WorkerRunnablecall方法中被回调执行的

WorkerRunnable:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
   Params[] mParams;
}

它是一个实现了 Callable 接口的抽象类

Callable接口:

public interface Callable<V> {
    V call() throws Exception;
}

Runnable 是一对兄弟,主要用在线程调用中,区别是 Runnablerun() 方法没有返回值,而 Callablecall() 方法有返回值。Callable需要和ExcutorService结合使用,其中ExecutorService也是一个线程池对象继承自Executor接口,ExecutorService提供的方法:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
  • submit(Callable task),传递一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
  • submit(Runnable task, T result),传递一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
  • submit(Runnable task),传递一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。

FutureTask:

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

    public FutureTask(Callable<V> callable) {
        if (callable == null) throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
 
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

    public boolean isCancelled() {
        return state >= CANCELLED;
    }

    public boolean isDone() {
        return state != NEW;
    }
    
	public void run() {
		...
	}
	
	public boolean cancel(boolean mayInterruptIfRunning) {...}
	protected void done() { }
	public V get() {...}
	public void set(V v) {...}
}

它一个实现 RunnableFuture 接口的类,构造函数可接受 Callable 参数类型,也就是 AsyncTask构造函数中的 WorkerRunnable类型的 mWorker对象,同时它具备run方法以及canceldone和状态判断等方法。

至此,我们能看到: WorkerRunnablecall() 方法是在 FutureTaskrun() 方法中调用的
在这里插入图片描述
RunnableFuture

继承 RunnableFuture两个接口,run方法是来自Runnable接口

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}
public interface Future<V> {
     //取消任务
    boolean cancel(boolean mayInterruptIfRunning);

    //如果任务完成前被取消,则返回true。
    boolean isCancelled();

    //如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
    boolean isDone();

    //获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。
    V get() throws InterruptedException, ExecutionException;

    // 获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,
    //如果阻塞时间超过设定的timeout时间,该方法将返回null。
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; 
}

Future接口能够中断执行中的任务, 判断任务是否完成获取任务执行完成后的结果(get),说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操作。

因为 RunnableFuture 同时继承了 RunnableFuture两个接口,所以 FutureTask 既可以被当做Runnable使用也可以被当做Future来使用。而 FutureTaskRunnableFuture 的实现类,那么接下来关键就是FutureTaskrun方法在何时被执行?

AsyncTask的execute()方法:

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

调用了 executeOnExecutor 方法,sDefaultExecutor是一个线程池对象

executeOnExecutor方法:

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

这里先调用了 onPreExecute() 方法,然后执行了exec.execute(mFuture) ,也就是为啥onPreExecute() 是在主线程执行的,而 FutureTask 对象是被放入线程池中执行。

并且这里判断状态不等于 PENDING 时,只要状态等于 RUNNING 或者 FINISHED就会抛异常!这也是为啥 一个AsyncTask的实例只能执行一次,第二次就会报错的原因!

SerialExecutor:

 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
 
 private static class SerialExecutor implements Executor {
 		// 双端队列 既可以当成队列使用 也可以当成栈使用
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive; // 当前正在执行的那个 mFutureTask

        public synchronized void execute(final Runnable r) {
        	// offer 向队尾插入元素,成功返回 true
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            //判断如果没有Runnable在执行,就调用scheduleNext
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
        	// poll 获取并删除队首元素,失败返回null
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive); // 由线程池真正的执行任务
            }
        }
    }

sDefaultExecutor 是一个 SerialExecutor 类型的 全局静态的常量 对象 SERIAL_EXECUTORSerialExecutorExecutor 的实现类,在 execute() 方法中调用了 Runnable 对象的 run 方法,因此我们的 mFuture 对象最终是在这里被调用的。(前面提过 FutureTask既是Runnable的实现也是Future的实现)。

这里 SerialExecutor 实际维护了一个队列,每当 execute() 执行并传入一个 FutureTask对象,这个对象就会被加入到队尾,然后判断当前如果没有正在执行的FutureTask对象,就从队首获取一个FutureTask放入线程池中执行。
在这里插入图片描述
而加入队列中的 FutureTask对象也不是直接添加的,而是又包了一层 Runnable 对象,这个包一层又有什么用呢?看它这里使用了 try-finally 保证了scheduleNext()方法必被调用,也就是说当前这个 FutureTaskrun方法被执行完以后,会自动取下一个FutureTask放入线程池中执行。

也就是说当我们new一个AsyncTask任务开始execute时, 并不一定立马执行,要看它里面的任务队列情况,如果它是第一个就会立马执行,如果它前面还有,那么就会排队等待。也就是一个串行的任务队列。

至此,我们能得到一个大体的执行流程:
new AsyncTask() --> new FutureTask() --> AsyncTask#execute() —> SerialExecutor#execute() --> mTasks.offer() --> mTasks.poll() --> scheduleNext() --> THREAD_POOL_EXECUTOR.execute() --> FutureTask#run() -->WorkerRunable#call()–>doInBackground()

postResult方法:
再回来看AsyncTask构造函数中的结果返回
在这里插入图片描述
最终调用postResult方法:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
    
    ....
    
    private Handler getHandler() {
        return mHandler;
    }
    
	....
    
    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

这里没什么神秘的了,就是通过Handler发消息,而这里的Handler对象就是构造函数第一行代码初始化的运行在主线程的Handler对象(使用了 Looper.getMainLooper() )。

    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:
                    // mTask就是当前的AsyncTask对象
                    result.mTask.以上是关于Android中的线程的主要内容,如果未能解决你的问题,请参考以下文章

片段中的 Android 相机预览

Android - 片段中的 getIntent()

如何从Android片段中的相机获取图像

片段中的Android onActivityResult

Android - 片段中的联系人选择器

片段中的Android SharedPreferences