前言
之前一直用IntentService,Service,而且网上说IntentService比Service要好,好在那里?,工作中遇到关于IntentService的问题,才总结,亡羊补牢为时不晚。
背景:
做启动页广告,在app 启动时候开启一个IntentService 去下载广告Json,并缓存广告图片url,广告时间等信息,在下次开屏时候,如果有广告的缓存的url,就进行开屏广告展示。
在IntentService中有两个网络请求,现实情况是同样的代码在Intentservice 中不能缓存广告url到sp中,但是修改为Service就可以正常展示广告,并缓存到sp中。
网上摘抄:
1. Service不是一个单独的进程 ,它和应用程序在同一个进程中。
2. Service不是一个线程,所以我们应该避免在Service里面进行耗时的操作
3. 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 也就是说Handler对象初始化
后,就默认与对它初始化的进程的消息队列绑定,因此可以利用Handler所包含的消息队列,制
定一些操作的顺序。
[问题]
那么为什么我不直接用Thread而要用Service呢?
翻开一些类似IntentService的blog大致有5点疑问
猜想一,线程嵌套,内部会不会等待所有线程执行完后,调用stop线程,
线程A stop后,线程B 内部逻辑是否会执行
线程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,宿主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是不绑定的,为了方便系统提供的方法
这里可以是同一线程,多个message,也统一是不同线程的Message,放到消息队列中
延伸:
IntentServive 原理 ,HandlerThread 用法,Handler 是子线程Handler 还是主线程Handler,
引用:
http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038469.html