完整版代码在最下面,直接查看请跳过介绍
ScheduledExecutorService的创建就不用多说,直接上代码
executorService = Executors.newSingleThreadScheduledExecutor();
ScheduledExecutorService的停止有shutdown和shutdownNow之分,可以看下它们之间的区别,但多数情况下都不能结束在线程池中的任务。不过有个小技巧可以让真正的逻辑不执行,就是在run方法中添加条件判断。如下:
@Override
public void run() {
if (mFrag.mWifi != null) { //添加条件判断,在shutdown之前把mWifi置为空
mFrag.search(); //这样虽不能取消run方法的执行,但可以让真正的searchWifi逻辑不执行
}
}
但是如果我们有业务需要,要求定时任务可以取消,再创建,该如何操作呢?
常规的写法就是
executorService.shutdownNow(); //取消
executorService.scheduleAtFixedRate(new Executor(),30,30, TimeUnit.SECONDS); //开始
注意了!
坑1:
这样的话,熟悉的同学就知道会发生RejectedExecutionException异常。因为executorService已经关闭了。如果需要继续开始任务,则需要重新new 对象Executors.newSingleThreadScheduledExecutor()。
坑2:
在坑1的基础上,我们想想会发生什么。坑1取消事件调用的是shutdownNow方法,开始事件是newSingleThreadScheduledExecutor创建对象后,再scheduleAtFixedRate。这样的话,我们从log分析就可以看出来,任务可能多次执行,就是上一次任务并没有销毁。我们每开始一个任务前,就需要把上一次任务关闭、置空。像这样:
private void beginExecutorScan() {
//先取消上一个任务,防止重复的任务
endExecutorScan();
executorService = Executors.newSingleThreadScheduledExecutor();
if (executorService != null) {
Lg.e("ScheduledExecutorService beginExecutorScan");
executorService.scheduleAtFixedRate(new ScanScheduledExecutor(Frag.this),30,30, TimeUnit.SECONDS);
}
}
private void endExecutorScan() {
if(executorService != null) {
Lg.e("ScheduledExecutorService endExecutorScan");
executorService.shutdownNow();
}
executorService = null;//非单例模式,置空防止重复的任务
}
坑3:
如果定时任务执行过程中遇到发生异常,则后面的任务将不再执行。处理方式是在run中加try catch,这样当一个任务发生异常时,后续的任务还会继续执行。
解决完以上3种坑后,完整的可以重复开始、停止的ScheduledExecutorService周期性任务像这样:
private void beginExecutorScan() {
//先取消上一个任务,防止重复的任务
endExecutorScan();
executorService = Executors.newSingleThreadScheduledExecutor();
if (executorService != null) {
Lg.e("ScheduledExecutorService beginExecutorScan");
executorService.scheduleAtFixedRate(new ScanScheduledExecutor(Frag.this),30,30, TimeUnit.SECONDS);
}
}
private void endExecutorScan() {
if(executorService != null) {
Lg.e("ScheduledExecutorService endExecutorScan");
executorService.shutdownNow();
}
executorService = null;//非单例模式,置空防止重复的任务
}
//静态弱引用方式,防止内存泄露
static class ScanScheduledExecutor implements Runnable {
WeakReference<Frag> mFragReference;
ScanScheduledExecutor(Frag frag) {
mFragReference = new WeakReference<>(frag);
}
@Override
public void run() {
try {
final Frag mFrag = mFragReference.get();
if (mFrag == null)
return;
if (mFrag.mWifi != null) { //添加条件判断,在shutdown之前把mWifi置为空
mFrag.search(); //这样虽不能取消run方法的执行,但可以让真正的searchWifi逻辑不执行
}
} catch (Exception e) {
Lg.e("ScheduledExecutorService e:"+e.toString());
}
}
}
附:
scheduleAtFixedRate 与 scheduleWithFixedDelay 的区别:
scheduleAtFixedRate ,是以上一个任务开始的时间计时,N秒过去后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
scheduleWithFixedDelay,是以上一个任务结束时开始计时,N秒过去后,立即执行。
完整使用说明
/**
* Author : 马占柱
* E-mail : mazhanzhu_3351@163.com
* Time : 2021/9/22 9:24
* Desc : 服务进程【不需要进行双进程保活设置】
*/
public class LocalService_NoKip extends Service {
private AMapLocationClient mClient = null;
public static final String TAG = "LocalService_NoKip";
private MediaPlayer mediaPlayer;
private ScheduledExecutorService threadPool;
private static int mTime = 0;
private String mFlag = "mzz";
private ScreenReceiverUtil.SreenStateListener mScreenListenerer = new ScreenReceiverUtil.SreenStateListener() {
@Override
public void onSreenOn() {
Log_Ma.e(TAG, "开屏 -------------------");
}
@Override
public void onSreenOff() {
Log_Ma.e(TAG, "锁屏 打开了1像素Activity");
mScreenManager.startActivity();
}
@Override
public void onUserPresent() {
Log_Ma.e(TAG, "解锁 关闭了1像素Activity");
mScreenManager.finishActivity();
}
};
private ScreenReceiverUtil mScreenListener;
private ScreenManager mScreenManager;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//注册锁屏广播监听器
mScreenListener = new ScreenReceiverUtil(this);
mScreenManager = ScreenManager.getInstance(this);
mScreenListener.setScreenReceiverListener(mScreenListenerer);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log_Ma.e(TAG, "LocalService_NoKip 启动");
//播放无声音乐
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
mediaPlayer = MediaPlayer.create(this, R.raw.aa);
//声音设置为0
mediaPlayer.setVolume(0f, 0f);
mediaPlayer.setLooping(true); //循环播放
mediaPlayer.start();
//启用前台服务,提升优先级
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(13695, NotifyManager.getInstance().getNotification(this));
} else {
startService(new Intent(this, HideForegroundService.class));
}
initTimer();
return Service.START_STICKY;
}
private void initTimer() {
endExecutorScan();
threadPool = Executors.newSingleThreadScheduledExecutor();//启动定时线程池
if (threadPool != null) {
/*保证period>initialDelay,以period为准。以period的时长为一个周期*/
mFlag = "mzz";
threadPool.scheduleWithFixedDelay(new ScanScheduledExecutor(LocalService_NoKip.this),
5, 10, TimeUnit.SECONDS);
}
}
private void endExecutorScan() {
if (threadPool != null) {
Log_Ma.e(TAG, "ScheduledExecutorService endExecutorScan");
mFlag = null;
threadPool.shutdownNow();
}
threadPool = null;//非单例模式,置空防止重复的任务
}
//静态弱引用方式,防止内存泄露
private static class ScanScheduledExecutor implements Runnable {
private WeakReference<LocalService_NoKip> reference;
ScanScheduledExecutor(LocalService_NoKip kip) {
reference = new WeakReference<>(kip);
}
@Override
public void run() {
try {
LocalService_NoKip kip = reference.get();
if (kip == null) return;
if (!TextUtils.isEmpty(kip.mFlag)) {
kip.startWoker();
}
} catch (Exception e) {
Log_Ma.e("ScheduledExecutorService e:" + e.toString());
}
}
}
private synchronized void startWoker() {
//do thing 开始你的表演吧
}
@Override
public void onDestroy() {
super.onDestroy();
Log_Ma.e(TAG, "onDestroy() called");
mClient.stopLocation();
mClient.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
mScreenListener.stopScreenReceiverListener();
endExecutorScan();
}
}