在进入实战之前,先讲解一些android相关的基础知识,因为这些知识会在后续阶段频繁使用,这小节主要讲解的是android消息处理机制。
基础APP在讲解之前,我们需要一个最基础的APP,我们使用Android Studio创建一个最简的工程,然后添加一个button,AS工程中app-> res-> layout-> activity_main.xml(text)的内容如下
xml文件就不进行讲解了,这里就不进行详细讲解了,总的来说,描述了一个总的界面,和一个按钮。 完成xml文件之后,我们在修改app-> java->com.example.administrator下的MainActivity.java文件,我们的目的比较简单,就是按下按钮的时候,执行一个函数,打印出一条信息即可,我们在AS工程中选中Button之后按shift+F1可以查看Button的使用方法(需要翻墙),编写以下代码:
代码如下:
package com.example.administrator.app_addons_0001_message;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
+ private Button mButton;
+ private final String TAG = "MessageTest";
+ private int ButtonCount = 0;
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ mButton = findViewById(R.id.button);
+ mButton .setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Log.d(TAG,"Send Message"+"");
ButtonCount++;
}
});
}
}
这样,我么编译运行程序之后,每次点击按钮就会打印“Send Message”,则代表实验完成。
android线程 方法一(创建线程)该处只针对android线程做简单的讲解,什么是线程,现场的作用是什么,这些基础知识,请另外查询其他的资料。在我们初始创建的APP中,其实就是一个线程,该进程中只有一个线程,我们称该线程为主线程,好比如主线程会执行MainActivity.java文件中 protected void onCreate(Bundle savedInstanceState)方法。那么我们还需要其他的线程怎么办呢?一般我们是在主线程中创建其他的线程。为了能够深入的了解android的线程,接下来我们会使用多种方法创建线程 在onCreate函数中添加如下代码:
+ mythread = new Thread(new MyRunndble(), "MessageTestThread");
+ mythread.start();
该代码也很好理解,就是创建一个线程,然后开始该线程,如果通过shift+F1查看我们可以知道,其中new MyRunndble()为Runnable是一个接口类型,其中Runnable有一个run方法,该方法为线程的主体循坏方法,那么我们参考Develop(shift+F1查看到的类容),在MainActivity.java中添加如下代码实现该接口:
private Thread mythread = null;
+ private final String TAG = "MessageTest";
+
+ class MyRunndble implements Runnable {
+
+ @Override
+ public void run() {
+ int count = 0;
+ for (;;){
+ Log.d(TAG,"MyThread1 " + count);
+ count ++;
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
该主体函数我们使用一个无限循环,做一个简单的打印函数,添加以上代码之后,我们运行APP程程序,可以看到,没三秒会出现"MyThread1 x "的打印信息,表示实验成功。但是我们直接使用Runnable去实现该线程的主体,我们的线程中只有一个主体run函数,为了摆脱这种局限性我们可以使用下面的方法去实现线程。
方法二(创建线程)我们自己编写一个类,该类继承于Thread,在public class MainActivity extends AppCompatActivit类中添加如下代码:
class MyThread extends Thread
{
public void run()
{
int count = 0;
for (;;){
Log.d(TAG,"MyThread2 " + count);
count ++;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
现在我们只重写run方法,后续阶段再添加其他方法,定义这个类之后,我们在onCreate方法中进行实例化并且开始该线程:
+ private MyThread mythread2 = null;
+ mythread2 = new MyThread();
+ mythread2.start();
然后我们编译运行APP可以看到两个线程的打印信息,表示实验成功。
机制框架在android系统中,线程使用同通信的本质,实质是使用pipe(管道:想了解内部机制,请查询其他资料),一个写一个读。即一个发送消息,一个处理消息,那么我们要做的至少要完成以下三点:
1.创建消息(MessageQueue)
2.发送者(Handler)
3.处理者(Looper)
Looper
前面我们已经通过两种方法,创建了两个线程,分别为mythread = new Thread(new MyRunndble(), “MessageTestThread”),mythread2 = new MyThread(),现在我们把mythread2作为接受者,即Looper,主线程作为发送者即Handler,在我们的APP中,有两个线程为接受者,在主线程发送消息的时候肯定需要指定一个接受者(Looper),那么怎么指定呢?在 class MyThread 中添加如下代码:
+ private Looper mLooper;
+ Looper getLooper(){
+ return mLooper;
+ }
在MyThread编写一个getLooper()的方法,主线程需要发送消息给那个次线程,调用MyThread类中的getLooper方法即可,前面提到过Thread的主体循环方法为run方法,我们对他进行重写,代码如下
public void run()
{
super.run();
Looper.prepare();
Looper.loop();
}
首先调用Looper.prepare(),使用(Ctrl+Alt+b)查看prepare()可以找到如下带代码
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以知道,调用Looper.prepare()他会创建一个消息队列,该消息队列会把所有接受到消息全部保存下来,然后还掉调用了 Looper.loop(),代码过于臃肿,此处只粘贴部分代码如下:
for (;;) {
Message msg = queue.next(); // might block
final long traceTag = me.mTraceTag;
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
msg.target.dispatchMessage(msg);
,其主要目的是从消息队列中取出消息进行处理,如果纤细队列为空,则进行睡眠等待。现在我们接受者已经实现了。接下来我们实现发送者(Handler)
Handler在主线程中,即onCreate(Bundle savedInstanceState)方法中,添加如下代码:
mHandler = new Handler(mythread2.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.d(TAG,"get Messsage"+mMessageCount);
mMessageCount++;
return false;
}
});
实例化一个Handler对象mHandler,并且绑定消息的接受者(Looper),已经一个回调方法Handler.Callback(),该方法会在子线程接收到消息的时候被调用,我们在该处仅仅打印出get Messsage x信息。
Looper优化前面在class MyThread中,只是实现了简单的功能,但是我们还有许多方面没有考虑到,完整办的代码应该如下
class MyThread extends Thread
{
private Looper mLooper;
public void run()
{
super.run();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
mLooper = Looper.myLooper();
Looper.loop();
}
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;
}
}
为什么要这样去实现class MyThread呢?我们先看onCreate的代码顺序:
mythread = new Thread(new MyRunndble(), "MessageTestThread");
mythread.start();
mythread2 = new MyThread();
mythread2.start();
mHandler = new Handler(mythread2.getLooper(), new Handler.Callback() {
我们先创建次线程,然后开始线程,通过前面的介绍,我们知道Looper的实例化是在次线程中实现的,如果主线线程在次线程没有实例化Looper之前调用了mythread2.getLooper()函数,那么此时发送的消息的接受者Looper则为空,有可能导致程序出错,故此我们在MyThread中的getLooper()方法中,先使用if (!isAlive())判断该Looper 是否有效,如果无效着进入等待状态。同时在run()方法中,调用了 mLooper = Looper.myLooper()实例化之后,再调用notifyAll()唤醒等待。 最后我们在 mButton.setOnClickListener添加如下类容
mButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG,"Send Message"+ButtonCount);
ButtonCount++;
+ Message msg = new Message();
+ mHandler.sendMessage(msg);
}
});
每次点击按钮的时候,都发送一个消息给次线程处理, 这样我们的代码就完善了,编译运行APP,点击button查看打印我们可以知道:
D/MessageTest: Send Message0
D/MessageTest: get Messsage0
每次点击button,发送一个消息给子线程,子进程接受到消息后进行处理。以上主要参考HandlerThread.java(通过Ctrl+Shift+n可查找)实现。
简易方法以上的代码是我们参考HandlerThread进行编写,那么我们可不可以直接使用HandlerThread实现呢,当然是可以的,在public class MainActivity添加如下代码:
mMessageCount++;
return false;
}
});
+ private HandlerThread mythread3;
+ mythread3 = new HandlerThread("MessageTestThread3");
+ mythread3.start();
+ mHandler3 = new Handler(mythread3.getLooper());
创建第三个线程接受消息。
Message msg = new Message();
mHandler.sendMessage(msg);
+
+ mHandler3.post(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG,"get Messsage for Thread3"+mMessageCount);
+ mMessageCount++;
+ }
+ });
每次点击按钮在发送消息给mHandler2的时候,也发送给mHandler3,mHandler3接受到消息后,执行mHandler3.post的run方法,打印”get Messsage for Thread3 x“信息。 编译运行APP后,每次点击按钮可以同时看到两个子线程的打印的信息。
D/MessageTest: Send Message0
D/MessageTest: get Messsage0
D/MessageTest: get Messsage for Thread31
则代表实验成功。
小节回顾本小节主要介绍了android基础知识中的消息处理机制,以及线程的使用方法。