(下面的问题question简写为Q1,answer简写为A1,)
A1: NO,NO,NO! 还记得有个小伙伴曾跟我说过:"service其实就是一个子线程的封装~~",
从而误导了我许多年^^, 今天我要给自己和大家纠正过来:
service只不过是依托于UI主线程运行的==>一个后台组件而已,
你可以把他理解为一个看不见的activity,所以面试回答防止ANR经常会说不要在service做耗时操作!
这里说明Android组件超时时间:activity:5S service:20S Broadcast:10S
好,于是我傻乎乎的去post一下! 完蛋,报bug吓我一跳~~别人说的不都是对的吗?
(我的错误写法)
new Thread(() -> {
new Handler().post(() ->System.out.println("工作代码"))
}).start();
在handler源码的200行报如下异常:
大概就是没有Loop.prepare的意思.这个稍后分析,我先看下以前正确的代码!
Handler handler=new Handler(){ //在主线程中如activity中创建
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(0);
}
}).start();
注意:发现这两者的区别了嘛? 第一个是在子线程中创建的handler,(不能正常运行),第二个是在UI主线程创建的handler,而UI主线程默认就创建了Loop.prepare方法,而我们新建的子线程是没有的,所以这里就会报错!
A2答案: 原来老司机所说的post并不是new Handler().post()这种在activity中常见的写法啊~~
而是在主线程new handler(),在子线程sendMsg一个消息,
也可以post一个runnable,这个runnable接口也会被handler转为msg传递,详见handler源码!
A3.1:当在主线程想执行一些耗时操作,但是又不影响后续代码的执行时,
我们就可以利用new handler执行耗时操作! 看这个小实验:
new Handler().post(() -> System.out.println("1")); //耗时操作1
System.out.println("2"); //执行操作2
new Handler().post(() -> System.out.println("3")); //耗时操作3
System.out.println("4"); //执行操作4
new Handler().post(() -> System.out.println("5"));
System.out.println("6");
依次输出结果: 2–>4–>6–>1–>3–>5 (先执行完所有执行操作(无handler),
再顺序执行handler耗时操作,因为handler消息是先进先出原则!)
~~所以我们可得出结论: 在同一线程时,我们想处理一些如加载图片,绘制百度地图等必须在UI主线程操作却会阻塞后续执行时,我们用new handler去执行!
A3.2:什么时候需要创建Loop.prepare呢?这里其实是第二个问题(Q2)的升级,在第二个
问题可发现,我们是因为误打误撞把handler写在了new Thread()中,所以handler就会在子线程回调,
则当我们需要在子线程回调handler的handleMessage()方法时,我们手动创建Loop.prepare.
实例:在子线程回调handler并创建loop
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() { //这样就可以在内部回调handler
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
官方对 Looper介绍: Looper是用于运行一个线程中的消息的类!
线程默认没有Looper,我们调用了 Looper.prepare() 方法就为线程创建了一个Looper,
然后再用 Looper.loop() 就可以将msg中的消息循环取出来给handler去发送了!
但是官方是不推荐我们这样做的,因为直接操作loop有风险! 举个栗子:
~~这是我们常用的一些写法: 先通过thread的start调用run方法!
~~然后在run中给我们创建了一个looper! 再将looper传到handler的构造方法中,
~~从而handler的回调方法就在子线程中运行了!
但这里的风险是: 在run方法执行时,Looper.prepare等方法还没执行完,
就执行了new Handler(mLooper)时,mLooper还是个空对象,则会抛出异常!
所以官方给我们封装了HandlerThread完美的解决了这个问题!~~
先看handlerThread源码:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//也可以指定线程的优先级,注意使用的是 android.os.Process 而不是 java.lang.Thread 的优先级!
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
// 子类需要重写的方法,在这里做一些执行前的初始化工作
protected void onLooperPrepared() {
}
//获取当前线程的 Looper
//如果线程不是正常运行的就返回 null
//如果线程启动后,Looper 还没创建,就 wait() 等待 创建 Looper 后 notify
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) { //循环等待
try {
wait();
} catch (InterruptedException e) {
}
}
return mLooper;
}
//调用 start() 后就会执行的 run()
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare(); //帮我们创建了 Looepr
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //Looper 已经创建,唤醒阻塞在获取 Looper 的线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop(); //开始循环
mTid = -1;
}
如果了解过Android消息机制源码(handler,msg&messageQueue,Loop)的童靴看着应该是so easy , 如不是很了解可参考文末的: [Android消息机制源码解读]
它很好的利用wait和notifyAll机制解决了我们上面所说的问题!
我们看下面的实际运用: 在Google封装的IntentService就很好的运用了这个!
A5:它是继承自service的抽象类,主要实现是在内部写了个子线程handlerThread,
然后暴露了一个onHandlerIntent()抽象方法供子类重写,
从而实现将工作代码运行在子线程中!(因为service默认的start等方法都是运行在UI线程的!)
下面我们来看一下源码:
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start(); //实现一个Q4说的HandlerThread,然后执行start初始化Looper
mServiceLooper = thread.getLooper(); //将获取的looper绑定到下面的handler
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;//生成msg,并把intent装到msg里
mServiceHandler.sendMessage(msg); //发送到下面的ServiceHandler回调
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj); //回调暴露给子类的onHandleIntent方法
stopSelf(msg.arg1); //在上面的抽象方法执行完毕后调取此方法就会销毁服务!
}
@WorkerThread //因为handler的Looper是在工作线程创建,所以这也执行在子线程
protected abstract void onHandleIntent(Intent intent);
}
上面截取了从onCreate到onStart的主要实现,注释写的很清楚,只需要注意两点:
1.继承IntentService的代码通常运行在onHandleIntent这个子线程方法里!
2.当我们的工作代码执行完毕后,会自动stop服务,这也是和service的不同!
~~这样我们就不用每次到在service中新建子线程了! 还要注意手动stop了!^^
## Q6: 我们在不同的activity,用不同的context开启和停止相同的服务,是操作的同一个服务,还是会新建两个不一样的服务呢?
A6: 这里先说结论吧,下面再看实验(有惊喜喔!) :
无论你是用相同的还是不同的context去startService,stopService,
他们的运行机制都是一样的! 即面向的是同一个服务,!
比如你在activity1开启了一个服务,在activity2关闭,能正常关闭!
(在activity1中) Intent heartIntent= new Intent(Activity1.this, HeartIntentService.class);
startService(heartIntent);
(在activity2中) Intent heartIntent= new Intent(Activity2.this, HeartIntentService.class);
stopService(heartIntent); (成功关闭在1开启的服务对象)
A6.1: 这里说一下本人在intentService中的实测结果!
疑惑的诞生==>:
我看到一篇文章说,如果重复的开启服务,则会依次将服务排在队列中,
等待上一个服务执行结束后,就会自动开启第二次开启服务!
My错误理解==>:
那在intentService中,执行完毕会自动关闭服务,那这个自动开启是不是
又重新开启了一个新服务(在上一个结束后),然后重新调用OnCreate方法?
正确结论!!=>:
当你多次调用同一个已开启的服务时(无论在哪个context),并不会创建一个新的服务~~
那前面的说法(service会等待在队列中),这就是错误的嘛,NO,他说的也有一定道理的,
但我们理解的姿势一定要对!55^^ 这里的依次执行,不是服务依次创建~~ 而是
onHandlerIntent这个方法的依次执行!
我下面做了一个小实验来验证这个结论(实践出真知嘛^^):
在IntentService (HeartIntentService.class)中:
public class HeartIntentService extends IntentService{
@Override
protected void onHandleIntent(Intent intent) {
ii=0; isRunner=true;//如果不初始化这两个值,会直接结束服务,
// 因为即使多次开启服务,但是同一个实例,所以i的状态还是为6,isRunner还是保持在false!
while (isRunner) {
ii++;
Log.k("onHandleIntent方法:执行"+ii+"次");
if (ii > 2) {
isRunner = false;
}
}
}
@Override
public void onCreate() {
super.onCreate();
Log.k("---心跳服务创建");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.k("---心跳服务销毁");
}
}
在activity中:重复运行两次服务:
Intent heartIntent= new Intent(Activity1.this,HeartIntentService.class);
startService(heartIntent); //执行两次!
输出日志结果如下~~:
---心跳服务创建
onHandleIntent方法:执行1次
onHandleIntent方法:执行2次
onHandleIntent方法:执行3次
onHandleIntent方法:执行1次
onHandleIntent方法:执行2次
onHandleIntent方法:执行3次
---心跳服务销毁
结论:
~我们重复的开启IntentService服务,onCreate如果创建过则不会重新创建.
~如果上一个服务的动作没有执行完,则等待执行完毕后再重复回调onHandleIntent方法,
~重复开启服务的实例都是同一个实例!所以导致我们代码要将int ii,和boolean isRun重新初始化!
~可理解为重复开启服务会调取父类Service的onStart方法,则重复回调onHandleIntent!
~当你使用不同的context去操作的都是同一个服务!
传送门:Android消息机制源码解读