前言

之前一直用IntentService,Service,而且网上说IntentService比Service要好,好在那里?,工作中遇到关于IntentService的问题,才总结,亡羊补牢为时不晚。

背景:

在启动页上做广告图效果,app 启动时候开启一个IntentService 去下载 包含广告信息的Json串缓存到本地,再次启动app时候,解析上次缓存到本地json串,更新广告ui。
放在IntentService中有两个网络请求来实现信息同步应该可以完美解决,现实情况是同样的代码在Intentservice 中不能缓存广告url到sp中,但是修改为Service就可以正常展示广告,并缓存到sp中。

网上摘抄:

1. Service不是进程也不是线程他是android的一个组件 ,它和应用程序在同一个进程中。

2. Service不是一个线程,所以我们应该避免在Service里面进行耗时的操作
3. 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 也就是说Handler对象初始化
后,就默认与对它初始化的线程的消息队列绑定,因此可以利用Handler所包含的消息队列,制
定一些操作的顺序。

[问题]

那么为什么我不直接用Thread而要用Service呢?

翻开一些IntentService相关的blog大致有5点疑问

猜想一,线程嵌套,线程A会不会等待B,C线程执行完,停止A线程,即多个嵌套线程执行顺序是否是串行的

线程a{

线程B{…. }
线程C{…. }

stop线程A
}

代码:

package com.tseng.alldilaog.guess;

public class Threademo {


static Runnable runnable = null;
static Thread thread = null;

public static void main(String[] args) {

setRun();
thread = new Thread(new Runnable() {
@Override
public void run() {
Thread threadb = new Thread(runnable, "THREAD_B");
threadb.start();
System.out.println(Thread.currentThread().getName() + " 执行时间:");
thread.stop();
}
}, "Thread_A");
thread.start();

}

private static void setRun() {
runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + " 猜想会打印");
} catch (Exception e) {
e.printStackTrace();
}

}
};
}

}

执行结果

Thread_A  执行时间:
THREAD_B 猜想会打印

Process finished with exit code 0

答案:
线程B 和线程A,是不同的线程,在未受到人为干预(线程同步)情况下,A,B 互相不受影响,关闭线程B的宿主A,嵌套Thread B 仍然会执行完毕。

猜想二

IntentService 中的OnIntent( ) 方法内部是执行耗时操作的,是否可以执行【子线程】的操作,2️⃣ 执行子线程操作会进行阻塞线程吗?后面的stopService() 会先于线程执行吗?

package com.tseng.alldilaog.service;

import android.app.IntentService;
import android.content.Intent;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.widget.Toast;

import com.tseng.alldilaog.MyApp;

public class IntentServiceDemo extends IntentService {

public static String Tag = IntentService.class.getSimpleName();


public IntentServiceDemo() {
super(Tag);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {

Toast.makeText(MyApp.getmContext(),"弹窗",1000).show();
System.out.println(Thread.currentThread().getName() + " 当前线程" + getMainLooper() +" my looper" + Looper.myLooper());

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread threadb = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " 猜想会打印");
} catch (Exception e) {
e.printStackTrace();
}

}
}, "THREAD_B");
threadb.start();

System.out.println(Thread.currentThread().getName() + " 执行时间:");
}
}, "Thread_A");
thread.start();


}


@Override
public void onDestroy() {
super.onDestroy();
System.out.println(Tag + " OnDestroy");
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}

MainActivity

@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(MainActivity.this, IntentServiceDemo.class);
startService(intent);
}

答案:

在IntentService的 protected void onHandleIntent(@Nullable Intent intent)的方法中进行线程嵌套 (猜想一代码),结论一致。
只是IntentService 的 ondestroy( )会优先执行。
【注意】:网上所说的可以在 onHandleIntent()方法中做耗时操作。为了保持生命周期的连贯性 ondestroy()在耗时任务完成后调用。建议在onHandleIntent方法中做同步工作。

08-30 14:54:05.511 2709-2709/com.tseng.alldilaog I/System.out: Thread[main,5,main]   looper =Looper (main, tid 1) {a86f274}

08-30 14:54:05.549 2709-2781/com.tseng.alldilaog I/System.out: IntentService[IntentService] 当前线程Looper (main, tid 1) {a86f274}
08-30 14:54:05.570 2709-2783/com.tseng.alldilaog I/System.out: Thread_A 执行时间:
08-30 14:54:05.571 2709-2784/com.tseng.alldilaog I/System.out: THREAD_B 猜想会打印
08-30 14:54:10.574 2709-2709/com.tseng.alldilaog I/System.out: IntentService OnDestroy

猜想三

子线程中的getMainLooper(),拿到的是子线程的Looper 还是主ui线程的Looper,二 ,getLooper的阻塞指的是二次StartService()时候,才会调用吗?

样例代码:

@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(MainActivity.this, IntentServiceDemo.class);
startService(intent);
System.out.println("第二次调用");
Intent intent2 = new Intent(MainActivity.this,IntentServiceDemo.class);
startService(intent2);
}

答案:

getMainLooper():当前线程Looper (main, tid 1) {a86f274}

Looper.myLooper() :Looper (IntentService[IntentService], tid 481) {fda1c0c}

关于IntentService()内部原理方面,二次startService() 会以Message 队列方式进行运行,调用两次,执行两次onHandleIntent()方法

输出:

08-30 15:11:40.262 8294-8294/com.tseng.alldilaog I/System.out: Thread[main,5,main]   looper =Looper (main, tid 1) {a86f274}   这句是在Activity的Oncreat()中调用的

08-30 15:11:40.285 8294-8294/com.tseng.alldilaog I/System.out: 第二次调用
08-30 15:11:40.353 8294-8393/com.tseng.alldilaog I/System.out: IntentService[IntentService] 当前线程Looper (main, tid 1) {a86f274} my looperLooper (IntentService[IntentService], tid 493) {105e0a4}
08-30 15:11:40.354 8294-8394/com.tseng.alldilaog I/System.out: Thread_A 执行时间:
08-30 15:11:40.356 8294-8395/com.tseng.alldilaog I/System.out: THREAD_B 猜想会打印

08-30 15:11:45.417 8294-8393/com.tseng.alldilaog I/System.out: IntentService[IntentService] 当前线程Looper (main, tid 1) {a86f274} my looperLooper (IntentService[IntentService], tid 493) {105e0a4}
08-30 15:11:45.424 8294-8742/com.tseng.alldilaog I/System.out: Thread_A 执行时间:
08-30 15:11:45.424 8294-8743/com.tseng.alldilaog I/System.out: THREAD_B 猜想会打印

08-30 15:11:50.427 8294-8294/com.tseng.alldilaog I/System.out: IntentService OnDestroy

猜想四

Hander是主线程hander还是子线程hander,取决于Looper()所在的线程和Hander初始化的线程。
1.主线程有初始化Handler 不指定Looper(),那hander属于主线程Handler。如果主线程Hander,给的子线程Looper(),那么会是子线程的Handler(),
区分是主线程hander还是子线程Hander,在于是否可以直接操作ui线程的ui元素

答案:

以上猜想是正确的,Handler 具体是子线程的Handler还是主线程的Hander,取决于用的初始化的Looper 是在子线程中初始化,还是主线程中。

Hander hander =new Handler( Lopper) //默认使用的是主线程的Looper(),这也是为什么在子线程
中不能new Handler的原因,需要:

Looper.prepare();
...
...
...
Looper.loop();

子线程需要和Handler 进行手动绑定

猜想五

HandlerThread 原理是在子线程中,绑定Hander + Looper【消息队列】,默认线程和Handler是不绑定的,为了方便系统提供的方法

Android 的 IntentService 与  Service_ide

这里可以是同一线程,多个message,也统一是不同线程的Message,放到消息队列中

延伸:

​​IntentServive 原理​​ ,HandlerThread 用法,Handler 是子线程Handler 还是主线程Handler,

总结:

android service 有两种开启方式

1.startservice(),bindService() 开启
2.unstartservice(),unbindService() 反注册

startservice–>onCreat(){ startForeground(1,通知)}

允许创建一个前台服务,普通service默认是后台服务,无交互,运行主线程,而前台服务会在通知栏上展示一个通知图标,当服务
终止后通知图标也会立即消失。

3.bindService(context,serviceConnect,flag) 属于绑定形式开启。服务开启后会通过一个回调serviceconnect进行相互同行,需要
Binder 进行中转。

服务生命周期

startservice会调用 oncreate() onstartcommond(),重复调用会多次执行onstartcommond()
bindService会调用的oncreate()以及serviceConnect()回调。

?如何选择合适开启service方式,一种是于服务有绑定关系,一种是无绑定关系,根据调用服务后是否与服务有交互来选择合适开启方式。

startservice ,bindService后关闭服务同样需要调用两次解绑,startservice()是开启服务,bindservic是绑定服务。

service与thread 区别。

没有区别,service是运行在主线程,对用户是透明的。