Service
Service是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要长期运行的任务。
但是不要被“后台”所迷惑,Service 并不在子线程运行,同时也不运行在一个独立的进程,它同样在UI线程中执行,所以不要在Service进行耗时操作,除非你在Service中创建了单独的线程来完成耗时操作。
Service的运行不依赖任何用户界面,即使程序切换到后台或者打开另一个应用Service都会保持正常运行。但是如果整个应用进程被杀掉时所有依赖于该进程的Service也会停止运行。
Service基本用法
关于Service最基本的用法自然就是如何启动一个Service了,启动Service的方法和启动Activity很类似,都需要借助Intent来实现。
注:Service启动方式有两种:显式启动、隐式启动,在同一应用中两种都可用,但在不同应用之间只能用隐式方式。需要注意的是在5.0上采用隐式启动时,会出现java.lang.IllegalArgumentException: Service Intent must be explicit异常,也就是说Service的Intent必须明确, 如下需要指明Package:
final Intent intent = new Intent();
intent.setAction("com.bsoft.messengerserver");
intent.setPackage("com.bsoft.messengerserver");
bindService(intent, mConnect, Service.BIND_AUTO_CREATE);
这里用个混淆点:
service启动方法有两种startService和bindService。
service启动方式有两种显式和隐式。
下面我们就通过一个具体的例子来看一下。
新建一个Android项目,项目名就叫ServiceTest(注:这里的KLog是一个日志打印库)
compile 'com.github.zhaokaiqiang.klog:library:+'
/**
* Created by 泅渡者
* Created on 2017/9/20.
*/
public class APP extends Application {
@Override
public void onCreate() {
super.onCreate();
KLog.init(true);//日志打印初始化
}
}
/**
* Created by 泅渡者
* Created on 2017/9/20.
*/
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
KLog.d("onCreate() 被执行");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
KLog.d("onStartCommand() 被执行");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
KLog.d("onDestroy() 被执行");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
KLog.d("onBind() 被执行");
return null;
}
}
我们在MainActivity中新建布局如下
image.png
接下来我们看下如何启动Service:
/**
* Created by 泅渡者
* Created on 2017/9/20.
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_start, btn_stop, btn_bind, btn_unbind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start = (Button) findViewById(R.id.btn_start);
btn_stop = (Button) findViewById(R.id.btn_stop);
btn_bind = (Button) findViewById(R.id.btn_bind);
btn_unbind = (Button) findViewById(R.id.btn_unbind);
btn_start.setOnClickListener(this);
btn_stop.setOnClickListener(this);
btn_bind.setOnClickListener(this);
btn_unbind.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.btn_stop:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.btn_bind:
break;
case R.id.btn_unbind:
break;
}
}
}
别忘了在Manifest.Xml中进行注册。
<service android:name=".MyService"/>
接下来我们看下运行结果:点击启动Service
/MyService.java: [ (MyService.java:21)#onCreate ] onCreate() 被执行
/MyService.java: [ (MyService.java:26)#onStartCommand ] onStartCommand() 被执行
在启动Service时执行了OnCreat()、onStartCommand ()这两个方法,如果我们再次点击启动呢?
/MyService.java: [ (MyService.java:21)#onCreate ] onCreate() 被执行
/MyService.java: [ (MyService.java:26)#onStartCommand ] onStartCommand() 被执行
/MyService.java: [ (MyService.java:26)#onStartCommand ] onStartCommand() 被执行
可见再次点击时只会执行onStartCommand(),那是因为Service已经创建好了实例再次点击只会复用当前实例。
此时我们去验证下如果把应用切到后台运行,我们的 Service会不会停掉。
这是我的截图
ServiceTest确实是正在运行的,即使它的内部并没有执行任何的逻辑。
回到ServiceTest程序,然后点击一下Stop Service按钮就可以将MyService停止掉了。
Service和Activity通信
上面的MyService只是简单的演示了创建过程,并没有执行任何事务。也没有和Activity做关联,只是Activity通知了MyService“你可以启动了“的指令。怎么才能将Service 和Activity进行关联呢?
我们可以看到上面的MyService中有个onBind()我们一直没有用,这个方法其实就是用于和Activity建立关联的,修改MyService中的代码,如下所示:
/**
* Created by 泅渡者
* Created on 2017/9/20.
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_start, btn_stop, btn_bind, btn_unbind;
private MyService.MyBinder myBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start = (Button) findViewById(R.id.btn_start);
btn_stop = (Button) findViewById(R.id.btn_stop);
btn_bind = (Button) findViewById(R.id.btn_bind);
btn_unbind = (Button) findViewById(R.id.btn_unbind);
btn_start.setOnClickListener(this);
btn_stop.setOnClickListener(this);
btn_bind.setOnClickListener(this);
btn_unbind.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.btn_stop:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.btn_bind:
/**
* 1).Context.BIND_AUTO_CREATE
*说明:表示收到绑定请求的时候,如果服务尚未创建,则即刻创建,在系统内存不足需要先摧毁优先级组件来释放内存,且只有驻留该服务的进程成为被摧毁对象时,服务才被摧毁
*2).Context.BIND_DEBUG_UNBIND
*说明:通常用于调试场景中判断绑定的服务是否正确,但容易引起内存泄漏,因此非调试目的的时候不建议使用
*3).Context.BIND_NOT_FOREGROUND
*说明:表示系统将阻止驻留该服务的进程具有前台优先级,仅在后台运行,该标志位位于Froyo中引入。
*/
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.btn_unbind:
unbindService(connection);
break;
}
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.Download();
}
};
}
接下来我们运行程序看看我们的效果如何:
/MyService.java: [ (MyService.java:24)#onCreate ] onCreate() 被执行
/MyService.java: [ (MyService.java:42)#onBind ] onBind() 被执行
/MyService.java: [ (MyService.java:49)#Download ] Download() 下载任务被执行
##点击解绑后的效果
/MyService.java: [ (MyService.java:36)#onDestroy ] onDestroy() 被执行
那如果我们先点击启动Service再点击绑定呢?
##点击创建后
/MyService.java: [ (MyService.java:24)#onCreate ] onCreate() 被执行
/MyService.java: [ (MyService.java:29)#onStartCommand ] onStartCommand() 被执行
##点击绑定后
/MyService.java: [ (MyService.java:42)#onBind ] onBind() 被执行
/MyService.java: [ (MyService.java:49)#Download ] Download() 下载任务被执行
此时当我们点击 “解除绑定”会发现没有任何效果,但是如果我们再次点击 就会发现报错了!(错误信息是我们的Service没有注册)
java.lang.IllegalArgumentException: Service not registered: com.bsoft.servicetest.MainActivity$1@7079a45
Sevice.png一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。(这两个步骤不论谁先谁后均可)
Service和Thread
大部分人会觉得Service和Thread一样都是运行在后台处理耗时操作的!可真正的答案往往不是如此,Service和Thread完全没有关系,从概念来说Service是Android中实现程序后台运行 的解决方案,它非常适合去执行那些不需要和用户交互而且还要长期运行的任务。Thread是开辟子线程处理耗时操作。但是千万别被Service“后台”所迷惑,Service也是运行在UI线程。
前台Service
Service几乎都是在后台(是指与用户没有直接的操作交互)运行的,一直以来它都是默默地做着辛苦的工作,但是Service的系统优先级别还是比较低。当出现内存不足的情况就会回收掉正在后台运行的Service。如果我们想让Service可以一直保持运行,而不会被系统在内存不足时回收掉,那么可以将Service运行在前台。比如墨迹天气在前台运行了一个Service,并且在Service中定时更新通知栏上的天气信息。
下面我们来创建一个前台Service:
/**
* Created by 泅渡者
* Created on 2017/9/21.
*/
public class WeatherService extends Service {
private static final int NOTIFY_KEY = 0x11;
@Override
public void onCreate() {
super.onCreate();
showNotifycation();
KLog.d("被执行");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
KLog.d("被执行");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Into the Senate :
* Return:
* 显示通知
*/
private void showNotifycation() {
NotificationCompat.Builder mBilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.icon_weather)
.setContentTitle(new Date().toString())
.setContentText("今天天气晴");
Intent resultIntent = new Intent(this, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent requestPending = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBilder.setContentIntent(requestPending);
NotificationManager mNotify = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
final Notification notify = mBilder.build();
mNotify.notify(NOTIFY_KEY, notify);
//启动前台服务
startForeground(NOTIFY_KEY, notify);
KLog.d("被执行");
}
@Override
public void onDestroy() {
super.onDestroy();
KLog.d("被执行");
}
}
在Activity中我们修改如下
Intent startIntent = new Intent(this, WeatherService.class);
startService(startIntent);
运行效果如下:
前台服务.png下一章我会介绍AIDL 和Messenger ,这两个可是很有必要去看看的。
所有代码上传至Git:https://gitee.com/13102169005/Android_Projects.git
网友评论