Android Service 基础知识点
IntentService
概念
IntentService是Android里面的一个封装类,继承自四大组件之一的Service
特性
- 在后台执行耗时的异步任务,当任务完成后会自动销毁
- 拥有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务
- 内部通过HandlerThread和Handler实现异步操作
- 创建IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作
由来
说了那么多,感觉IntentService与Service的区别主要是能实现异步操作,因为Service是默认运行在主线程中的,不能做一些耗时操作,否则容易ANR。而 IntentService 则是直接在子线程中工作的。另外当我们使用 startService(Intent) 方法启动 IntentService 后,IntentService 就会在工作线程中处理每一个 Intent ,并且在完成这些任务后停止它自己。
常规使用,如何使用?
需要继承 IntentService ,并提供一个构造函数,并且必须在其内部调用父类的有参构造函数,然后重写 onHandleIntent(Intent intent)方法,在 onHandleIntent 方法中可以去处理一些具体的业务逻辑,而且不用担心ANR的问题,因为这个方法已经是在子线程中运行了
MyIntentService的实例代码如下:
public class MyIntentService extends IntentService {
private final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
Log.e(TAG,"MyIntentService");
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG,"onCreate");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.e(TAG, "onHandleIntent intent = " + intent.getStringExtra("info"));
Log.e(TAG, "Thread id = " + Thread.currentThread().getId()+" Thread name = "+ Thread.currentThread().getName());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
}
在Activity中开启代码如下:
Intent intent = new Intent(MyActivity.this, MyIntentService.class);
intent.putExtra("info","threadInfo");
startService(intent);
开启后关闭然后再次打开,打印日志如下:
2021-06-09 16:31:47.274 5851-5851 E/MyActivity: Thread id = 1 Thread name = main
2021-06-09 16:31:47.277 5851-5851 E/MyIntentService: MyIntentService
2021-06-09 16:31:47.279 5851-5851 E/MyIntentService: onCreate
2021-06-09 16:31:47.281 5851-5851 E/MyIntentService: onStartCommand
2021-06-09 16:31:47.285 5851-5909 E/MyIntentService: onHandleIntent intent = threadInfo
2021-06-09 16:31:47.285 5851-5909 E/MyIntentService: Thread id = 131 Thread name = IntentService[MyIntentService]
2021-06-09 16:31:47.286 5851-5851 E/MyIntentService: onDestroy
2021-06-09 16:31:50.951 5851-5851 E/MyIntentService: MyIntentService
2021-06-09 16:31:50.952 5851-5851 E/MyIntentService: onCreate
2021-06-09 16:31:50.952 5851-5851 E/MyIntentService: onStartCommand
2021-06-09 16:31:50.952 5851-5916 E/MyIntentService: onHandleIntent intent = threadInfo
2021-06-09 16:31:50.953 5851-5916 E/MyIntentService: Thread id = 132 Thread name = IntentService[MyIntentService]
2021-06-09 16:31:50.963 5851-5851 E/MyIntentService: onDestroy
日志总结
- Activity(1)线程与MyIntentService(131)不在同一线程
- onHandleIntent打印完日志后很快就执行了onDestroy,并且我们没有调用stopService,说明是完成了任务自动销毁的
- onHandleIntent(@Nullable Intent intent)里面的intent是可以获取传递过来的Extra数据的,可以根据这个判断请求去分别做什么事
- 每次IntentService结束后再startService都会开启一个与上次不同的线程去处理请求(131/132)
IntentService 用来做耗时操作,如何使用?
MyIntentService中代码实例如下:
public class MyIntentService extends IntentService {
private final String TAG = "MyIntentService";
private LocalBroadcastManager mBroadcastManager;
private int progress = 0;
private int progress_again = 0;
public MyIntentService() {
super("MyIntentService");
Log.e(TAG, "MyIntentService");
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.e(TAG, "onStart" + " startId = " + startId);
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
mBroadcastManager = LocalBroadcastManager.getInstance(this);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.e(TAG, "onHandleIntent intent = " + intent.getStringExtra("info"));
Log.e(TAG, "Thread id = " + Thread.currentThread().getId() + " Thread name = " + Thread.currentThread().getName());
String info = intent.getStringExtra("info");
if (TextUtils.isEmpty(info)) {
return;
}
switch (info) {
case "thread1":
boolean isRunning = true;
while (isRunning) {
progress++;
if (progress >= 5) {
isRunning = false;
}
sendThreadStatus(Constants.ACTION_ONE, progress);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case "thread2":
boolean isRunning_again = true;
while (isRunning_again) {
progress_again++;
if (progress_again >= 5) {
isRunning_again = false;
}
sendThreadStatus(Constants.ACTION_TWO, progress_again);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
// 发送线程结果
private void sendThreadStatus(String action, int progress) {
Log.e(TAG, "action=" + action + ",progress=" + progress);
Intent intent = new Intent(action);
intent.putExtra("progress", progress);
mBroadcastManager.sendBroadcast(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
}
Activity中的实例代码如下:
public class MyActivity extends AppCompatActivity {
private static String TAG = "MyActivity";
private TextView mTvMsg;
private TextView mTvMsgAgain;
private LocalBroadcastManager mBroadcastManager;
private MyBroadCast mBroadCast;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
mTvMsg = (TextView) findViewById(R.id.msg);
mTvMsgAgain = (TextView) findViewById(R.id.msg2);
mBroadcastManager = LocalBroadcastManager.getInstance(this);
mBroadCast = new MyBroadCast();
Log.e(TAG, "thread id = " + Thread.currentThread().getId());
Log.e(TAG, "thread name =" + Thread.currentThread().getName());
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Constants.ACTION_ONE);
intentFilter.addAction(Constants.ACTION_TWO);
mBroadcastManager.registerReceiver(mBroadCast, intentFilter);
findViewById(R.id.startService).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MyActivity.this, MyIntentService.class);
intent.putExtra("info","thread1");
startService(intent);
}
});
findViewById(R.id.stopService).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MyActivity.this, MyIntentService.class);
stopService(intent);
}
});
findViewById(R.id.startServiceAgain).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MyActivity.this, MyIntentService.class);
intent.putExtra("info","thread2");
startService(intent);
}
});
}
public class MyBroadCast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
switch (action) {
case Constants.ACTION_ONE:
int progress = intent.getIntExtra("progress", 0);
mTvMsg.setText(String.format(Locale.getDefault(), "ACTION_THREAD 当前进度 = %S", progress));
break;
case Constants.ACTION_TWO:
int progress_again = intent.getIntExtra("progress", 0);
mTvMsgAgain.setText(String.format(Locale.getDefault(), "ACTION_THREAD_AGAIN 当前进度 = %S", progress_again));
break;
}
}
}
}
}
日志如下:
//点击开启服务后
2021-06-11 11:22:04.833 9200-9200/ E/MyActivity: thread id = 1
2021-06-11 11:22:04.833 9200-9200/ E/MyActivity: thread name =main
2021-06-11 11:22:06.851 9200-9200/ E/MyIntentService: MyIntentService
2021-06-11 11:22:06.852 9200-9200/ E/MyIntentService: onCreate
2021-06-11 11:22:06.852 9200-9200/ E/MyIntentService: onStartCommand
2021-06-11 11:22:06.853 9200-9229/ E/MyIntentService: onHandleIntent intent = thread1
2021-06-11 11:22:06.853 9200-9229/ E/MyIntentService: Thread id = 150 Thread name = IntentService[MyIntentService]
2021-06-11 11:22:06.853 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=1
2021-06-11 11:22:08.854 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=2
//点击再次开启服务
2021-06-11 11:22:10.620 9200-9200/ E/MyIntentService: onStartCommand
2021-06-11 11:22:10.856 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=3
2021-06-11 11:22:12.857 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=4
//第二次点击再次开启服务
2021-06-11 11:22:13.044 9200-9200/ E/MyIntentService: onStartCommand
2021-06-11 11:22:14.858 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=5
2021-06-11 11:22:16.860 9200-9229/ E/MyIntentService: onHandleIntent intent = thread2
2021-06-11 11:22:16.860 9200-9229/ E/MyIntentService: Thread id = 150 Thread name = IntentService[MyIntentService]
2021-06-11 11:22:16.860 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=1
2021-06-11 11:22:18.861 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=2
2021-06-11 11:22:20.861 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=3
//点击停止服务
2021-06-11 11:22:21.390 9200-9200/ E/MyIntentService: onDestroy
2021-06-11 11:22:22.862 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=4
2021-06-11 11:22:24.864 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=5
日志总结如下
- 开启服务后,任务如果没有执行完,也就是onHandleIntent没有处理完成,将不会重新创建onCreate只会调用onStartCommand,将请求放入队列中等待执行
- Thread id 都是150说明是同一个线程在运行,只是在处理不同的任务,排队按顺序执行
- 如果执行最后一个请求时,调用stopService任务(onDestroy->progress=4->progress=5)还是会继续执行完毕
- 故IntentService比较适用于单线程操作不耗时任务,不支持多并发的操作
源码解析
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//处理任务
onHandleIntent((Intent)msg.obj);
//处理完后销毁stopSelf
//注意:这里不是立马就去销毁而是去判断还有没有消息没有了才去销毁
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
//设置Intent是否重投递,如果重投递,在onHandleIntent方法返回前,
//如果进程死了,会重启进程,重新分发Intent
//但是只会分发最近的一个Intent
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
//通过实例化HandlerThreade新建线程&启动
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//获取工程线程的Looper&维护自己的工作队列
mServiceLooper = thread.getLooper();
//新建ServiceHandler,绑定上面的Looper
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
//获取ServiceHandler消息的引用
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
//发送消息,添加到消息队列
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
//调用onStart()方法
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
//Looper停止
mServiceLooper.quit();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
IntentService的源码不复杂,我们来总结看看它做了些什么?
- 当IntentService被创建时,就初始化了一个HandlerThread线程,故使用IntentService时,不需要额外新建线程。
- HandlerThread继承自Thread,内部封装了Looper,创建了一个ServiceHandler任务将会被传入mServiceLooper中执行。
- onStartCommand()中去调用onStart()方法,onStart()中获取ServiceHandler发送消息,添加到消息队列
- 最终在ServiceHandler中去处理任务onHandleIntent并调用stopSelf去判断还没有消息需要去处理销毁
这里的stopSelf我们继续往下看:
//Service.java
public final void stopSelf(int startId) {
if (mActivityManager == null) {
return;
}
try {
mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}
//ActivityManagerService.java
@Override
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) {
synchronized(this) {
return mServices.stopServiceTokenLocked(className, token, startId);
}
}
//ActiveServices.java
boolean stopServiceTokenLocked(ComponentName className, IBinder token,
int startId) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopServiceToken: " + className
+ " " + token + " startId=" + startId);
ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
if (r != null) {
if (startId >= 0) {
// Asked to only stop if done with all work. Note that
// to avoid leaks, we will take this as dropping all
// start items up to and including this one.
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
if (si != null) {
while (r.deliveredStarts.size() > 0) {
ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
cur.removeUriPermissionsLocked();
if (cur == si) {
break;
}
}
}
if (r.getLastStartId() != startId) {
return false;
}
if (r.deliveredStarts.size() > 0) {
Slog.w(TAG, "stopServiceToken startId " + startId
+ " is last, but have " + r.deliveredStarts.size()
+ " remaining args");
}
}
synchronized (r.stats.getBatteryStats()) {
r.stats.stopRunningLocked();
}
r.startRequested = false;
if (r.tracker != null) {
r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
r.callStart = false;
final long origId = Binder.clearCallingIdentity();
bringDownServiceIfNeededLocked(r, false, false);
Binder.restoreCallingIdentity(origId);
return true;
}
return false;
}
StopSelf方法调用停止服务,那么多个任务来临时,后面的任务岂不是不会被执行了?
并不是的,stopSelf方法调用时传入了一个startId,这个startId是在每次startService时都会往onStartCommand传入一个新值。调用stopSelf,如果传入的是一个大于0的值,会判断该值是否等于最后一次传入的startId(r.getLastStartId() != startId) ,如果是最后一个Service会走到onDestory流程,如果不是则Service仍然存活)
每处理一个请求就调用一次stopSelf(msg.arg1),这个参数是onStartCommand方法第三个参数,并不是真的销毁自己,会判断如果ServiceLooper的队列里还有消息,就不会销毁自己
如果开启了多个请求,如A,B,C三个请求,那就startService3次,如果在处理A请求调用了stopService,那A请求会继续执行完,但是B和C请求就不会执行;如果在执行C请求时候调用了stopService,C请求还是会执行完
原因:
//只要调用stopService后,后续请求就不会得到执行,因为消息队列清空了
@Override
public void onDestroy() {
mServiceLooper.quit();
}
IntentService与Service的区别总结:
- Service是依附于主线程的(远程服务除外),直接做耗时操作会阻塞主线程;而IntentService是工作在工作线程,对主线程没影响
- 使用IntentService不需要像在Service里自己手动开启线程去处理耗时请求
- Service是需要手动调用stopService来销毁,而IntentService是自动销毁,不用担心内存泄露
- IntentService内部采用了HandleThread和Handler的实现,IntentService又比线程优先级高,相对而言不容易被系统杀死来保证服务运行。
附加:
在Android 8.0 (API level 26) 也就是Android O上用JobIntentService替代IntentService
JobIntentService与IntentService都是可以用来执行后台任务,在Android O之前,使用起来效果没多大差别,但是Android O以后JobIntentService不会立即执行,等手机进入一定状态后才会执行任务,所以不能用来执行及时的后台任务,感兴趣的可以去看看,这里不做过多介绍。
结语:感谢各位大佬的分享,如有错误的需要改进的地方,请留言评论指出,谢谢!
参考文章
- https://blog.csdn.net/qq_30993595/article/details/78452064
- https://juejin.cn/post/6962407237784436750
- https://juejin.cn/post/6844903847027015693