Service分为本地服务(LocalService)和远程服务(RemoteService),本地服务就是运行在同一个进程,记住service运行的是主线程,所以在开service里做耗时任务一定要线程。远程服务就是跟app不同个进程,app关了,服务进程还开着,需要另外关闭服务进程。
首先先讲下service与线程区别。
service与线程没有半毛钱关系,很多时候,你可能会问,为什么要用 Service,因为service是独立的,也只有一个实例,如果你开线程,你的活动关了,其他活动就很难控制线程去事情,其实服务最重要一点就是它方便与很多活动去控制它,且它只有一个,所有活动控制的对象都是一个实例。
而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。
service的使用有两种
第一种 ,用startService和stopService
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.d("zjs", "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("zjs", "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("zjs", "onDestroy: ");
//释放掉资源,防止内存泄漏
}
public void startDownload() {
}
}
创建个类去继承服务,再重写方法去实现逻辑,然后再需要的地方去开启停止活动。
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
非常注意,以第一种去开始服务,一旦服务开启就跟调用者(开启者)没有任何关系了,开启者退出了,开启者挂了,服务还在后台长期的运行,也就是活动一旦开启,着服务就一起运行,除非有人去调用它停止,不然活动销毁了,它也会一直运行,当然,你把app关了,服务肯定停止,因为它是本地服务,不是远程服务。还有,以第一种去开启服务,开启者不能调用服务里面的方法,即你在活动开启服务,那么上面的startDownload() 你是不可以调用的,你不可以说获得上面MyService 实例然后去调用MyService 实例的startDownload(),记住,不管是第一还是第二种,service是不存在实例的,因为就只有一个服务。
第一次使用startService(startIntent)后的生命周期是,
servier: onCreate()>onStartCommand,之后而不管再什么活动什么地方,重复了多少次开启服务, onCreate()方法都不会被调用,只会调用onStartComman方法。,所有活动所有地方都是只有一个同样的服务,当stopService(stopIntent);调用后,就会调用 onDestroy()方法。
第二种方法
原理就是,活动们想要跟Service连接,然后拿到Service,(注意,在第二张service也是只有一个的。)去调用Service里面的方法。上面那种做法就是不可以调用Service里面的方法
我们知道进程间通信是通过Binder,而四大组件的之间的通信也就是 四大组件与 AMS 之间的通信 其实也就是用binder
那么当 activity 去绑定这个server 的时候能够 获取到 这个服务的binder 不就可以在 activity 中利用这个 binder 与 server通信。
那么 server要做的就是提供一个binder 给绑定它的人
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.d("zjs", "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("zjs", "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("zjs", "onDestroy: ");
//释放掉资源,防止内存泄漏
}
@Override
public IBinder onBind(Intent intent) {
Log.d("zjs", "onBind: ");
return mBinder;//提供Binder 给活动们,让他们可以与server 通信
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("zjs", "onUnbind: ");
return super.onUnbind(intent);
}
class MyBinder extends Binder { //这里创建了一个binder,里面包含着当前server实例
public MyService getService() {
return MyService.this;
}
}
//服务的方法,活动们要调用服务的方法
public void startDownload() {
}
}
public class MainActivity extends Activity implements OnClickListener {
private Button startService;
private Button stopService;
private Button bindService;
private Button unbindService;
private MyService myService= null;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService .MyBinder myBinder = (MyService .MyBinder) binder;//获取server提供的binder
myService= myBinder.getService();//拿到了服务实例
myService.startDownload()
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = (Button) findViewById(R.id.start_service);
stopService = (Button) findViewById(R.id.stop_service);
bindService = (Button) findViewById(R.id.bind_service);
unbindService = (Button) findViewById(R.id.unbind_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE); //绑定一个服务,需要有 ServiceConnection
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}
非常注意:
以第二种方式,bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。不过这肯定是只有一个绑定服务的情况,多个绑定就只有调用者挂了,服务依旧运行着,只有最后一个绑定服务的挂了,服务才挂,第二种是可以调用服务里面的方法,即上面的startDownload,而第一种是不可以的,但是不管是第一还是第二种,service 永远只有一个,第二种不过是利用了ibinder去让多个连接着共同享有一个service,然后去调用service的方法,并不是创建了n了service实例。当然,比如你再服务的方法这么做
//服务的方法,活动们要调用服务的方法
public void startDownload() {
Log.d("zjs", "startDownload: ");
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
}
});
thread.start();
}
那么肯定是开启了n个线程。
当只且第一次第一个活动点击绑定服务:server:oncread>onbind.,
活动们就会各自执行ServiceConnection 的onServiceConnected()方法。之后任何包括第一个绑定服务的活动的绑定服务,,只有活动们就会各自执行ServiceConnection 的onServiceConnected(),server的oncread和onbind.不会再调用
调用unbindService之后发生的事情,client与Service解除绑定连接状态,注意调用unbindService后,活动们里面的ServiceConnection 的onServiceConnected() 方法是不会被调用的,onServiceDisconnected() 在正常关闭的情况下是不会被调用的.该方法只在Service 被破坏了或者被杀死的时候调用. 例如, 系统资源不足, 要关闭一些Services, 刚好连接绑定的 Service 是被关闭者之一, 这个时候onServiceDisconnected() 就会被调用,也就是正常操作就不会调用,要是出现异常或者系统资源不足才会。
如果多个连接service,那么当其中一个调用unbindService,service的onUnbind()和onDestroy()是不会执行的,只有最后一个unbindService,service的onUnbind()和onDestroy()才会执行的。
注意:使用bindService开启服务以后,与activity存在关联,退出activity时必须调用unbindService方法,否则会报ServiceConnection泄漏的错误。
通过上面我们知道开启一个服务就两种方式,不需要开启服务再去绑定服务,他们是独立的,看的是生命周期的方法。当第一第二种结合的时候:
注意,同一个服务可以用两种方式一同开启,没有先后顺序的要求,MyService的onCreate只会执行一次。
关闭服务需要stopService和unbindService都被调用,也没有先后顺序的影响,MyService的onDestroy也只执行一次。但是如果只用一种方式关闭服务,不论是哪种关闭方式,onDestroy都不会被执行,服务也不会被关闭。这一点需要注意。
接下来讲intentserver
IntentService是Service的子类,比普通的Service增加了额外的功能。先看Service本身存在两个问题:
Service不会自己开启一个线程去,而是需要人为的开启;
如果需要让服务运行的代码运行结束后,自动关闭服务我们得需要:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
//逻辑代码
stopSelf();//用这句话去停止服务
}
});
thread.start();
return super.onStartCommand(intent, flags, startId);
}
而IntentService解决了上面的问题,它会自己内部开启一个线程 ,运行完自动结束。
IntentService也是只有一个,其实就是里面自己用了HandlerThread,然后,实现的效果就是,只弄了个线程去执行耗时任务,把所有的intent以队列形式一个个执行。执行完一个才会继续执行下一个。在IntentService中,onCreate() 方法只会调用一次,所以只会创建一个工作线程,多次调用 startService(Intent) 时(onStartCommand也会调用多次)其实并不会创建新的工作线程,只是把消息即各种Intent加入消息队列中等待执行,所以,多次启动 IntentService 会按顺序执行事件,在IntentService中,不要用bindservier方法去开启,因为在源码里面的onBind()是默认返回null的,而采用bindService() 启动 IntentService的生命周期是:onCreate() —>onBind()—>onunbind()—>onDestory()
并不会调用onstart()或者onstartcommand()方法,所以不会将消息发送到消息队列,那么onHandleIntent()将不会回调。
IntentService使用:
在Manifest.xml中注册服务
<service android:name="com.example.administrator.module.MyIntentService" />
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}//必须这么做传给父类不然报错
//在onHandleIntent做耗时io操作
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//复写onHandleIntent方法
//实现耗时任务操作
//打印当前的线程id
String taskName = intent.getExtras().getString("taskName");
Log.d("MyIntentService", " Thread Id is : " + Thread.currentThread()
.getId());
switch (taskName) {
case "task1":
Log.i("MyIntentService", "do task1");
break;
case "task2":
Log.i("MyIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("MyIntentService", "onCreate");
super.onCreate();
}
/*复写onStartCommand()方法*/
//默认调用了父类的onStartCommand方法去把每个intent加入队列中
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy executed");
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button= findViewById(R.id.aa);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(),MyIntentService.class);
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
}
});
Button button2= findViewById(R.id.bb);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i2 = new Intent(getApplicationContext(),MyIntentService.class);
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
}
});
}
}
此外还要注意的是,IntentService 中除了 onHandleIntent 方法其他都是运行在主线程的。
接下来讲前台服务
讲前台服务我们先讲通知的内容:
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Intent intent = new Intent(MyService.this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);//点击通知要跳转的界面
Notification notification=null;
//Android8.0以上必须添加 渠道 才能显示通知栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//创建渠道
String id = "my_channel_01";
String name="渠道名字";
NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(mChannel);
//设置图片,通知标题,发送时间,提示方式等属性
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), id);
builder.setChannelId(id)//设置渠道
.setContentTitle("北极熊也有微笑") //标题
.setContentText("北极熊是本人认为最棒的动物,没有之一") //内容
.setSubText("--北极熊很Happy~") //内容下面的一小段文字
.setTicker("收到北极熊也有微笑发来的的消息~") //收到信息后状态栏显示的文字信息
.setWhen(System.currentTimeMillis()) //系统显示时间
.setSmallIcon(R.mipmap.ic_launcher) //收到信息后状态栏显示的小图标
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE) //设置默认的三色灯与振动器
.setDefaults(Notification.DEFAULT_SOUND) //设置系统的提示音
.setAutoCancel(true); //设置点击后取消Notification
builder.setContentIntent(pendingIntent); //绑定PendingIntent对象
notification = builder.build();
} else {
//设置图片,通知标题,发送时间,提示方式等属性
Notification.Builder builder = new Notification.Builder(getApplicationContext());
builder.setContentTitle("北极熊也有微笑") //标题
.setContentText("北极熊是本人认为最棒的动物,没有之一") //内容
.setSubText("--北极熊很Happy~") //内容下面的一小段文字
.setTicker("收到北极熊也有微笑发来的的消息~") //收到信息后状态栏显示的文字信息
.setWhen(System.currentTimeMillis()) //系统显示时间
.setSmallIcon(R.mipmap.ic_launcher) //收到信息后状态栏显示的小图标
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE) //设置默认的三色灯与振动器
.setDefaults(Notification.DEFAULT_SOUND) //设置系统的提示音
.setAutoCancel(true); //设置点击后取消Notification
builder.setContentIntent(pendingIntent); //绑定PendingIntent对象
notification = builder.build();
}
notificationManager.notify(1, notification);
通知的内容没什么好讲的,无非就是点击按钮或者需要的时候就弹出了一条通知,然后点击这条通知就跳转相应的画面。不懂得百度看一下就明白了,就是一堆设置功能属性而已,上面的代码就是开启一条通知的代码,你可以在里面修改自己需要的属性功能。
好继续讲前台服务
上面说的都是后台服务,后台服务在系统内存不足的时候,可能会被系统杀死,如果想要让服务尽量不被杀死,就可以使用前台服务。它与普通服务的区别就是,其实就是如果你把这个服务弄为前台服务,那么就会一直在状态栏显示一条通知,并且,这条通知不会去除,直到这个服务结束或者被杀死,这条通知一直放在那,使用前提服务,那么这个服务是被用户认可的,可以一直运行着,从而也不容易被系统杀死。
使用:
public class MyService extends Service {
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Intent intent = new Intent(MyService.this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);//点击通知要跳转的界面
Notification notification=null;
//Android8.0以上必须添加 渠道 才能显示通知栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//创建渠道
String id = "my_channel_01";
String name="渠道名字";
NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(mChannel);
//设置图片,通知标题,发送时间,提示方式等属性
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), id);
builder.setChannelId(id)//设置渠道
.setContentTitle("北极熊也有微笑") //标题
.setContentText("北极熊是本人认为最棒的动物,没有之一") //内容
.setSubText("--北极熊很Happy~") //内容下面的一小段文字
.setTicker("收到北极熊也有微笑发来的的消息~") //收到信息后状态栏显示的文字信息
.setWhen(System.currentTimeMillis()) //系统显示时间
.setSmallIcon(R.mipmap.ic_launcher) //收到信息后状态栏显示的小图标
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE) //设置默认的三色灯与振动器
.setDefaults(Notification.DEFAULT_SOUND) //设置系统的提示音
.setAutoCancel(true); //设置点击后取消Notification
builder.setContentIntent(pendingIntent); //绑定PendingIntent对象
notification = builder.build();
} else {
//设置图片,通知标题,发送时间,提示方式等属性
Notification.Builder builder = new Notification.Builder(getApplicationContext());
builder.setContentTitle("北极熊也有微笑") //标题
.setContentText("北极熊是本人认为最棒的动物,没有之一") //内容
.setSubText("--北极熊很Happy~") //内容下面的一小段文字
.setTicker("收到北极熊也有微笑发来的的消息~") //收到信息后状态栏显示的文字信息
.setWhen(System.currentTimeMillis()) //系统显示时间
.setSmallIcon(R.mipmap.ic_launcher) //收到信息后状态栏显示的小图标
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE) //设置默认的三色灯与振动器
.setDefaults(Notification.DEFAULT_SOUND) //设置系统的提示音
.setAutoCancel(true); //设置点击后取消Notification
builder.setContentIntent(pendingIntent); //绑定PendingIntent对象
notification = builder.build();
}
startForeground(1, notification);//把这个服务当前台服务。
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
使用跟普通服务一样,就只不过在普通服务里加了个通知信息要长什么样子并且通过
startForeground(1, notification);把这个服务当前台服务。