Android多线程从入门到精通

本文为大家总结了Android中多线程的知识点,如果对Android多线程还不是很了解的朋友建议来此一游。如果讲解过程中有不对的地方,也请各位朋友多为我指正,大家共同学习进步。

首先先为大家展示一下我自己做的思维导图,我也会通过按照这个图为大家介绍Android中的多线程。(PS:这段时间一直没有更新博客,自己在下面一直在对所学的知识进行整理和深入的学习,自我感觉通过这种思维导图的方式来整理知识点很清晰,也推荐大家使用,我用的思维导图软件是XMind。这是图中的一小部分所以左边有一条线,各位见谅!)

这里写图片描述

在我进入到正是的内容之前,大家可以思考一下我们Java中多线程的知识,和你能想到Android中的多线程知识有哪些。图中的你是不是都知道,都非常清楚。

思考片刻我们进入正文,Java中的多线程我在此不做介绍,大家可以去参考其他资料。先来介绍一下我们Android中的多线程。

Android中多线程

为什么使用多线程

多线程的概念我们已经非常熟悉了,几乎所有的编程语言中都有多线程编程,那我们在Android为什么要使用多线程。我觉得看这篇文章的人应该都有Android手机,大家玩手机的时候应该都遇到过ANR(应用程序无响应)的现象吧,每次出现这种情况我们都非常恼火对吧。那么这种情况是怎么发生的呢?这要先从我们的主线程说起。
什么是我们的主线程?
这个问题初学者最容易忽视(以前我就是,这里自我批评),其实在我们的Android编程中,主线程就是我们的UI线程。UI线程负责我们应用程序事件的分发,组件的绘制这些东西。但是UI线程又比较笨,他并不会给每一个组件响应事件创建新的线程去处理。这时候如果响应事件是一个比较耗时的操作,时间超过了5秒,那Android系统就会自动的抛出ANR提示。
除此之外,Android UI toolKit又不是线程安全的,就是说你不能在非UI线程去操作UI组件,所以我们的UI线程的单线程模型要遵循两条原则:
1>不能有操作阻塞UI线程
2>不能再非UI线程的操作UI组件
所以我们要用多线程来处理耗时操作,以防止阻塞UI线程。

Handler+Thread(或Runnable)

在我们Java中大家应该都非常熟悉Thread和Runnable,这两种方法在我们的Android中同样适用。我们可以将一个耗时操作放入一个Thread,或者实现一个Runnable接口即可。就这么简单。但是我们大多数情况并不是我们的线程运行完了就完了,他一般都会将结果或者中间过程在我们的界面上展示给我们用户。可是上面说了呀,我们不能在非UI线程操作UI控件,那我要怎么办呢?这就要用到我们Android中线程间的消息传递机制。这里面我们用的最频繁的就是Handler。

Handler的使用方法

Handler的使用方法很简单,来看一下代码,代码很简单。

//1.在Handler中重载handleMessage()方法,编码UI线程要做的操作,mTextView是一个TextView
private Handler mHandler1 = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.i(TAG, "mHandler1 received message.");
        mTextView.setText("Handler消息传递完成。");
    }
};
//2.在响应事件中创建一个非UI线程,该非UI线程中新建一个Message,并调用mHandler1的sendMessage方法
new Thread(new Runnable() {
    @Override
    public void run() {
        Log.i(TAG,"Thread start running.");
        int i= 0;
        while(i<=3){
            i++;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
        }
        Message mMessage = new Message();
        mHandler1.sendMessage(mMessage);
    }
}).start();

很简单是吧。如果想要会用,基本这样就可以了。通过这种方法你基本上可以实现所有Android中的多线程操作。要想成为一个出色的程序猿,我们不仅要知其然也要知其所以然。那么我们就探究一下在背后做了什么。

Handler,Looper,Message,MessageQueue的关系

首先我们先简单介绍一下每个类的简介(这里不太好懂,重点在下面):
Handler:一个Handler允许你去发和处理一个Message和与一个Runnable类关联这个线程的消息队列MessageQueue。
Looper:这个类用来在线程中运行Message loop的类,线程默认情况下是没有Message Loop与它关联的;通过prepare()在线程中运行一个loop,和通过loop()去处理里面的消息,直到这个loop停止。
Message:定义了一个可以传递给Handler的消息。
MessageQueue:Looper中一个用来快速处理Message列表的一个低级的类。

这样看很不好懂,那下面就让你真的懂,真的懂的最好的方法就是追踪源码,那就先看看sendMessage是如何工作的:

Handler的源码:

//1.sendMessage源码,调用sendMeesageDelayed(延时发送消息传入0,表示立即发送)
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
//2.这个又调用sendMessageAtTime方法(在确定的时间点发送消息)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//3.这个又调用enqueueMessage方法(将Message加入一个MessageQueue )队列中
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
//4.这个又调用MessageQueue中的enqueueMessage方法来添加Message。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
//5.下面我们追踪一下mQueue是怎么来的
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    //7.这里看到mLooper是通过Looper的静态方法得到的。这个静态方法是得到当前线程关联的Looper,看当前线程是哪个?是不是就是我们启动Activity的线程呀。那我们就去看看
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //6.这里通过一个Looper得到Message得到的,那我们看看mLooper是如何来的(因为在一个函数中看上面7)
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Activity部分源码,这时我们搜索一下Looper,我们并没有找到一个对象,但我们看到了一个这个函数:

//8.这个函数我们并不需要知道它具体是做什么,注意看9
public void recreate() {
    if (mParent != null) {
        throw new IllegalStateException("Can only be called on top-level activity");
    }
    //9.我们看到一个有意思的Looper.myLooper() != mMainThread.getLooper(),我们猜一下,其实在mMainThread中有一个Looper,既然猜了我们就去看看mMainThread(一个ActivityThread的对象)嘛。
    if (Looper.myLooper() != mMainThread.getLooper()) {
        throw new IllegalStateException("Must be called from main thread");
    }
    mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false);
}

ActivityThread源码中:

//10.看到这里我傻了,转了一圈又回来了然后我就随意的向下翻,看看还有什么有关的当,我翻到5000多行的时候,见到了这辈子我最想看到的东西。见11
final Looper mLooper = Looper.myLooper();
//11.看下面开不开心,我的天,主函数,有多少人编了半天Android程序还没有看到过主函数呢?
public static void main(String[] args) {
    //12.前面的代码省略,这句代码很关键,这既然是Looper中的一个静态方法,那我们就去看一下嘛。这个函数我不太好看出他是干什么的。那先去Google提供的官方API去看看,官方API的介绍是Initialize the current thread as a looper, marking it as an application's main looper.初始化当前的线程为一个Looper,并标记它为应用的主Looper。这是在主函数中,当然是主线程,这里把主线程初始化了一个Looper,主线程不就是我们UI线程嘛,恍然大悟。到此我们终于算是追踪到了个尽头。再看13
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
//13.启动我们应用Looper,loop方法是一个死循环,他会一直在那里转,这也是我追踪源码知道的,这里我就不再带着大家追踪,感兴趣的朋友自己下来追踪一下
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

那么我们总结一下上面追踪源码的结论:
1>我们的主线程已经默认为我们准备好了一个Looper(你可能在其他很多地方看到过这句话,然而我觉得这次是你唯一一次真正的理解了这句话)
2>Looper中包含一个MessageQueue
3>MessageQueue是一个Message队列
4>sendMessage就是将一个Message加入到MessageQueue中

那handleMessage方法又是怎么执行的呢?既然是UI中最后执行那一定是最后的操作了,Looper中loop方法之后才运作起来,并且一直运作,负责消息的分发执行,于是猜也是去loop方法中找看,于是很简单你便可以追踪到handleMessage方法。我在这里就不再带各位追踪。
结论即:
1>Looper的loop方法会不断的去处理我们消息队列中的消息,分发出去并执行。

上面我们搞清楚了Handler,Looper,Message,MessageQueue的关系,那么我们就想呀,在非主线程可不可以用Handler进行消息传递机制呢?

能否在非主线程中使用Handler经行消息传递

我这样问了肯定是可以的呀,哈哈,我们思考上面是通过Looper的prepareMainLooper方法把当前的线程初始化为一个Looper那么,我们就在想Looper应该也可以把非主线程初始化为一个Looper,好奇就去翻Google官方API看看方法,找到Looper类,你会发现人家Google真贴心,上来就教给你怎么做了。步骤很简单,看代码中我添加的注释。

//Google官方API的实例程序
class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        //1.prepare方法初始化当前线程为一个Looper
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        //2.调用loop方法让这个Looper运作起来
        Looper.loop();
    }
}

HandlerThread的使用

越深入学习Android我越觉得的Google是一个培养懒人的地方。看看这个类,HandlerThread,我自己在给类,对象等等命名的时候很喜欢前面表示这个类的功能,最后表示这个类是什么类(也可以叫继承与哪个类),那我猜他就是一个具有可以让Handler传递消息的Thread。猜了就去验证一下嘛。看看Google官方API(我以前也很喜欢百度一下,但是后来觉得百度上讲的多数知其然不知其所以然,或者是别人加上了自己的理解,味道有点变了,所以现在我基本看到一个什么类就是先看Google API,然后看源码。这样可能一两天才能学会一个类,但是这样你就真的是掌握了一个类)。

官方介绍:
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
这个类是为了方便开始一个包含Looper的线程。这个Looper之后可以用于创建Handler类,注意,start()一定要调用。

看里面的方法也很简单,于是自己写了一个例子,这个类人们用的比较少,人们介绍的也比较少,所以把完整代码附上,注意看代码中注释:

HandlerThreadActivity代码:

public class HandlerThreadActivity extends Activity {

    private static final String TAG = HandlerThreadActivity.class.getSimpleName();
    private HandlerThread mMyHandlerThread;
    private Button mThread_Button;
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
        //1.创建一个HandlerThread对象,并调用start()方法
        mMyHandlerThread = new HandlerThread(TAG+"handlerThread");
        mMyHandlerThread.start();
        //2.通过getLooper得到线程中的Looper,并构造一个Handler
        mHandler = new Handler(mMyHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                String mName = getLooper().getThread().getClass().getSimpleName();
                Log.i(TAG, "在" + mName + "线程中的Handler消息传递完成。");
            }
        };

        mThread_Button = (Button)findViewById(R.id.thread_start_button);
        mThread_Button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //3.开启另一个线程使用handler与之前的HandlerThread线程通信
                new Thread(mRunnable).start();
            }
        });

    }


    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            int i= 0;
            while(i<=3){
                i++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {}
            }
            Message mMessage = new Message();
            mHandler.sendMessage(mMessage);
        }
    };
}

布局文件代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
tools:context="com.jack.csdn.csdn_dome.threads.handlerthread.HandlerThreadActivity">
    <Button
        android:id="@+id/thread_start_button"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="开启一个Thread"/>
</RelativeLayout>

总结HandlerThread的使用步骤:
1>创建一个HandlerThread对象,并调用start()方法
2>通过getLooper得到线程中的Looper,并构造一个Handler
3>开启另一个线程使用handler与之前的HandlerThread线程通信

注意:第一步不能和其他两步颠倒顺序。而且必须是start()之后才能调用getLooper。这里我们再去带大家开一下HandlerThread,学习一个相对新的类嘛。

@Override
public void run() {
    mTid = Process.myTid();
    //在run方法中调用的perpare方法,这下懂了吧。
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

这里,我们看上去与Handler有关的,至少名字中一看就能看出来的就学的差不多了。下面还有一个类在Android多线程中也是用的频率非常高。马上来学习。

AsyncTask的使用

好,Google又来培养懒人了,你想想,上面我们写个多线程就要写个Thread,又要写个Handler,又要写个Message,多麻烦。Google就很贴心,这不就又给了我们一个类,来简化我们的编码,为什么我说简化了我们的编码呢?之后在说明。

什么是AsyncTask

看我文章的人都知道了这时候怎么做,Google官方API,那就翻吧:
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
AsyncTask使我们能够简单适当的使用我们的UI线程。这个类允许在后台执行操作并且发布结果到UI线程而不需要去操纵Thread和Handler。

官方文档讲的很清楚吧。
那么我们就来看一下我们使用AsyncTask

AsyncTask的使用步骤

这里还是用我自己的写的示例程序来给朋友们介绍。
AsyncTaskActivity源码:

public class AsyncTaskActivity extends Activity {

    private ProgressBar mProgressBar;
    private TextView mTextView;
    private Button mButton;
    private int mProgressStatus = 0;

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

        mProgressBar = (ProgressBar)findViewById(R.id.asyncTask_progressBar);
        mTextView = (TextView)findViewById(R.id.asyncTask_textView);
        mButton = (Button)findViewById(R.id.asyncTask_button);

        mProgressBar.setProgress(mProgressStatus);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //TODO 在UI线程调用AsyncTask的execute
                new MyAsyncTask().execute();
            }
        });
    }

    //1.创建一个类来继承AsyncTask
    private class MyAsyncTask extends AsyncTask<Integer, Integer, Long>{

        @Override
        protected Long doInBackground(Integer... params) {
            //2.在该函数中添加后台要执行的操作  在非UI线程执行
            while(mProgressStatus<100){
                mProgressStatus++;
                //6.中间过程必须通过执行该函数
                publishProgress(mProgressStatus);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {}
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            //3.中间过程显示  在UI线程执行
            super.onProgressUpdate(values);
            mTextView.setText(mProgressStatus+"%");
            mProgressBar.setProgress(mProgressStatus);
        }


        @Override
        protected void onPreExecute() {
            //4.后台操作开始时调用  在UI线程执行
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Long result) {
            //5.后台操作结束时调用  在UI线程执行
            super.onPostExecute(result);
        }
    }
}

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.jack.csdn.csdn_dome.threads.asynctask.AsyncTaskActivity">


    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/asyncTask_progressBar"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:id="@+id/asyncTask_textView"
        android:layout_below="@+id/asyncTask_progressBar"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="29dp"
        android:text="0%"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="AsyncTask开始"
        android:id="@+id/asyncTask_button"
        android:layout_below="@+id/asyncTask_textView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="65dp"
        android:textSize="@dimen/abc_text_size_body_2_material"/>
</RelativeLayout>

总结AsyncTask的使用步骤:
1>继承AsyncTask类,重写其中的doInBackground,onProgressUpdate,onPreExecute,onPostExecute等方法;
2>在主线程中调用execute方法。
注意:
1>要使用onProgressUpdate,需要执行publishProgress方法;
2>execute方法一定是在UI线程中调用的。

AsyncTask深入剖析

这样的话我们来看一下源码吧,简单的分析一下它是怎么工作的:
AsyncTask部分源码:

//1.首先就看到一个有意思的属性,带Handler
private static InternalHandler sHandler;
//2.看一下InternalHandler,继承自Handler,说明什么,其实AsynTask的内部实现也是通过Handler,只不过是封装了而已。并且我们在这里面发现了一个更有意思的一句代码(见3)。
private static class InternalHandler extends Handler {
    public InternalHandler() {
        //3.getMainLooper方法,看名字也知道这个函数是干什么的吧,也真正理解了为什么一定要在UI线程中调用execute方法了吧。Handler都有了,我们猜他应该也会有一个Thread吧,猜了就去验证一下嘛。
        super(Looper.getMainLooper());
    }

    @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;
        }
    }
}
//4.找了半天我们发现我们没有找到想要的Thread,但是我们找到了什么,一个ThreadFactory。这一看就是一个Thread的工厂类,就是用来创建Thread的。那我们看看那里用了它。
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
//5.原来是来构造一个Executor的。Java并发编程中所用到的类。原来它不使用Thread合适使用的Executor,而Executor还是去创建一个一个线程嘛。虽然猜的有点出入,但还是擦边了。
public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

文章有点长,差不多写了4个多小时。如果你耐心的看完了,希望对你有一定的帮助。如果你觉得哪里讲的不对,请向我提出来,互相交流,共同学习,一起进步。

提醒:本文为个人原创,如有转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值