Service解惑&关于IntentService你需要知道的几个问题

版权声明:---原创作者:风行云K ~转载请说明 https://blog.csdn.net/qq_31149079/article/details/84851578

(下面–>问题question简写为Q1,answer简写为A1,)

Q1.当我们新建一个service代表已经运行在一个新的线程里了嘛?

      A1: NO,NO,NO!  还记得有个小伙伴曾跟我说过:"service其实就是一个子线程的封装~~",
      从而误导了我许多年^^, 今天我要给自己和大家纠正过来:
      service只不过是依托于UI主线程运行的==>一个后台组件而已,
      你可以把他理解为一个看不见的activity,所以面试回答防止ANR经常会说不要在service做耗时操作!  

这里附组件超时时间:activity:5S service:20S Broadcast:10S

Q2.别人常说的:“在子线程post一下handler就跳到主线程了?”,这是真的嘛?

好,于是我傻乎乎的去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源码!

Q3: 在同一线程中 new handler().post()有什么好处?我们什么时候需要在子线程手动创建Loop.prepare?

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有风险! 举个栗子:

在这里插入图片描述

扫描二维码关注公众号,回复: 4394260 查看本文章

~~这是我们常用的一些写法: 先通过thread的start调用run方法!
~~然后在run中给我们创建了一个looper! 再将looper传到handler的构造方法中,
~~从而handler的回调方法就在子线程中运行了!
但这里的风险是: 在run方法执行时,Looper.prepare等方法还没执行完,
就执行了new Handler(mLooper)时,mLooper还是个空对象,则会抛出异常!

所以官方给我们封装了HandlerThread完美的解决了这个问题!~~

Q4: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就很好的运用了这个!

Q5: 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开启的服务对象)

Q6.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消息机制源码解读

猜你喜欢

转载自blog.csdn.net/qq_31149079/article/details/84851578
今日推荐