1.intentservice
IntentService具有以下特点:
(1) IntentService自带一个工作线程,当我们的Service需要做一些可能会阻塞主线程的工作的时候可以考虑使用IntentService。
(2) 我们需要将要做的实际工作放入到IntentService的onHandleIntent回到方法中,当我们通过startService(intent)启动了IntentService之后,最终Android Framework会回调其onHandleIntent方法,并将intent传入该方法,这样我们就可以根据intent去做实际工作,并且onHandleIntent运行在IntentService所持有的工作线程中,而非主线程。
(3) 当我们通过startService多次启动了IntentService,这会产生多个job,由于IntentService只持有一个工作线程,所以每次onHandleIntent只能处理一个job。面多多个job,IntentService会如何处理?处理方式是one-by-one,也就是一个一个按照先后顺序处理,先将intent1传入onHandleIntent,让其完成job1,然后将intent2传入onHandleIntent,让其完成job2…这样直至所有job完成,所以我们IntentService不能并行的执行多个job,只能一个一个的按照先后顺序完成,当所有job完成的时候IntentService就销毁了,会执行onDestroy回调方法。
来自:https://blog.csdn.net/wjlsxl_whb/article/details/78036932
2.写法:
写一个类继承自intentservice,无参构造方法里面要调用super(tag),重写onHandleIntent(),在里面做耗时操作即可。
package com.ysl.myandroidbase;
import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyIntentService extends IntentService {
public static final String TAG = "MyIntentService";
public MyIntentService() {
super(TAG);
Log.d(TAG, "MyIntentService() is invoke "+Thread.currentThread().getId());
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate is invoke "+Thread.currentThread().getId());
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand is invoke "+Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//做耗时操作
Log.d(TAG, "onHandleIntent is invoke "+Thread.currentThread().getId());
int age = intent.getIntExtra("age",0);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "age = "+age);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy is invoke "+Thread.currentThread().getId());
}
}
activity启动service,和普通的方式一样:
intent = new Intent(ServiceActivity.this, MyIntentService.class);
intent.putExtra("age", 15);
startService(intent);
看一下结果:
onHandleIntent方法在一个子线程中调用,并且执行了耗时任务,在任务结束后,自动调用ondestroy销毁。
service本身是依附在主线程的,假如要在service中执行一些耗时操作。我们就可以使用intentservice来代替service。
3.foregroundService
service默认情况下是后台服务,这意味着service在系统回收内存(比如在浏览器里显示大图片)的时候可以被kill掉。如果你比较在意这个service的挂掉,比如像后台音乐播放器这种突然挂了会影响用户的情况,就可以使用ForegroundService来提示用户。
使用该方法可以让后台服务变成前台服务,使服务的优先级变低,不容易被回收掉。可以用来做进程保活。可以让当前进程持有这样一个服务,当当前进程进入后台之后,由于持有前台服务,会使得当前进程的优先级变低。一般情况下,若当前进程不持有前台进程,当前进程进入后台之后优先级为5,若持有前台进程,优先级变为2。使得不容易被回收。
(1)我的写法:
package com.ysl.myandroidbase;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
public class ForegroundService extends Service {
public static final String TAG = "ForegroundService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind is invoke");
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate is invoke");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand is invoke");
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, ServiceActivity.class), 0);
Notification notification = new Notification.Builder(this)
.setContentIntent(pendingIntent)
.setContentTitle("设置下拉列表里的标题")
.setContentText("设置要显示的内容")
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.mipmap.ic_launcher)
.setWhen(System.currentTimeMillis() - 24*60*60*1000)//设置通知上的时间,now,1d...
.setShowWhen(true)//设置是否显示时间
.build();
//使用startForeground方法开启前台服务
startForeground(1, notification);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
//关闭前台服务
stopForeground(true);
Log.d(TAG, "onDestroy is invoke");
}
}
从代码可以看到,在onStartCommand方法中构建了notification。然后调用startForeground()方法就可以启动前台服务了。并且此时会有一个通知栏消息弹出。
注:startForeground(1, notification);第一个参数不能为0,否则前台服务启动失败。
关于:
setWhen(System.currentTimeMillis())和setWhen(System.currentTimeMillis() - 24*60*60*1000)//设置通知上的时间,now,1d... .
setShowWhen(true)//设置是否显示时间,如上第一张图,就是设置为false,不显示时间的样子。
前台服务的通知栏消息是一直存在的。
在服务的onDestroy()中设置notification.setAutoCancel(true)或者不写这句话。只要是手动停止服务的,通知栏的消息都会消失。但如果是按菜单键直接杀死app,通知栏的消息图标不会消失。
4.虽然前台进程可以避免被杀死,但通知栏的图标对于强迫症来说也是一种折磨。怎么才能去掉它呢?
可以使用2个Service都用startForeground设置为前台进程,但他们使用相同的Notification ID,那么他们只会产生一个通知,然后把其中一个Service取消前台效果,那么就会把通知关闭,剩下的那个Service就是前台Service了,而且通知栏没有通知。
放置前台的service:
package com.ysl.myandroidbase;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.BitmapFactory;
import android.os.Build.VERSION;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
public class ForegroundService extends Service {
public static final String TAG = "ForegroundService";
public static final int NOTICE_ID = 100;
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind is invoke");
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate is invoke");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand is invoke");
//使用startForeground方法开启前台服务
startForeground(NOTICE_ID, getNotification());
startService(new Intent(this, CancelNoticeService.class));
return super.onStartCommand(intent, flags, startId);
}
private Notification getNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, ServiceActivity.class), 0);
return new Notification.Builder(this)
.setContentIntent(pendingIntent)
.setContentTitle("设置下拉列表里的标题")
.setContentText("设置要显示的内容")
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.mipmap.ic_launcher)
.setWhen(System.currentTimeMillis() - 24*60*60*1000)//设置通知上的时间,now,1d...
.setShowWhen(true)//设置是否显示时间
.setAutoCancel(true)
.build();
}
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
Log.d(TAG, "onDestroy is invoke");
}
}
用于取消通知栏图标的service:
package com.ysl.myandroidbase;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;
public class CancelNoticeService extends Service {
public static final String TAG = "CancelNoticeService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate is invoke");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand is invoke");
startForeground(ForegroundService.NOTICE_ID, new Notification());
stopForeground(true);
stopSelf();//停止此服务减少资源消耗
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy is invoke");
}
}
看上面的代码:在前台service中,startService(new Intent(this, CancelNoticeService.class));启动了取消图标的service。
在取消图标的service中,做了三件事:1.使用和前台service相同ID来使辅助服务变成前台服务,这时会和前台服务公用一个通知栏图标。2.再把此服务停止前台操作,调用stopForeground(true)。3.调用stopSelf();停止服务。减少资源消耗。
这种方式可以取消图标,但图标还是会在通知栏闪一下。使用这种方式是不可避免的。