@(读书笔记)
Service主要用于在后台处理一些耗时逻辑,或者去执行某些需要长期运行的任务。有时候在程序退出的情况下,Service也会在后台继续保持运行状态。
1.新建一个MyService继承Service,并重写onCreate()、onStartCommand()和onDestroy()方法:
例:
public class MyService extends Service {
public static final String TAG="MyService";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG,"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy");
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,"onBind");
return null;
}
}
2.启动Service和停止Service的方法
//启动Service
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
//停止Service
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent)
3.项目中所有使用的Service都需要在AndroidManifest文件中配置:
<service android:name="com.example.servicetest.MyService" > service>
第一次点击执行启动Service的方法时,会打印:
08-10 21:52:33.257 14033-14033/com.simon.activity I/MyService: onCreate
08-10 21:52:33.257 14033-14033/com.simon.activity I/MyService: onStartCommand
也就是说,当启动一个Service的时候,会调用该Service中的onCreate和onStartCommand方法。
再次点击执行启动Service的方法,会打印:
08-10 21:54:22.576 14033-14033/com.simon.activity I/MyService: onStartCommand
可以看到只执行了onStartCommand方法,因为onCreate方法只会在Service第一次创建的时候调用。如果Service已经创建过来,不管怎么调用startService方法,onCreate方法都不会执行。只会执行onStartCommand方法。
然后我们就可以在应用管理界面看到我们启动的Service了,这里需要注意一下Service一旦启动,就不会自动停止,即时里面的方法执行完成或者没有方法。
在service的基本用法中,Activity只是启动了Service,然后Service就自己执行onCreate和onStartCommand了。这种方式下Service和Activity的关系不太。如果想在Activity中去通知Service去做什么事情,那就需要将Activity和Service建立关联。我们通过Binder对象来实现这个操作。
1.Service是通过onBinder对象和Activity建立关联的,例:
public class MyService extends Service {
public static final String TAG="MyService";
private MyBinder binder=new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG,"onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy");
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,"onBind");
return binder;
}
public class MyBinder extends Binder{
public void startDownload(){
Log.i(TAG,"startDownload");
Log.i(TAG,"Thread-name"+Thread.currentThread().getName());
}
}
}
可以看到我们在Service中添加了一个自定义的MyBinder对象,然后给他添加了StartDownload方法。 这个方法中我们输出了日志,并打印了当前线程的名字。然后构建了一个myBinder对象,并把它作为onBinder方法的返回值。之后我们在Activity中会拿到这个binder对象,这样Activty和Service就建立关联了。
2.修改Activity的代码,添加绑定Service的方法
public class MainActivity extends AppCompatActivity {
public static final String TAG = "tag";
private MyService.MyBinder myBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "MainActivity-onCreate");
findViewById(R.id.bind_service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
}
});
findViewById(R.id.unbind_service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);
}
});
}
}
可以看到我们这里先建立了一个匿名的ServiceConnection的匿名类,在里面重写了onServiceConnection()和onServiceDisconnected()方法,这两个方法分别会在Activity和Service建立关联和解除关联的时候调用。在onServiceConnected()方法中,我们又通过向下转型得到了MyBinder的实例,有了这个实例,我们就可以在Activity中调用Service中的相关方法了。
然后,我们执行下面这段代码,Service就会绑定到Activity中,bindService传递的第三个参数BIND_AUTO_CREATE表示在Service和Activity建立关联后自动创建Service. 就会使得Service的onCreate方法执行,onStartCommand方法不执行。
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
我们点击一下,绑定Service的按钮,打印日志如下
08-11 15:45:30.040 15608-15608/com.simon.activity I/MyService: onCreate
08-11 15:45:30.041 15608-15608/com.simon.activity I/MyService: onBind
08-11 15:45:30.048 15608-15608/com.simon.activity I/MyService: startDownload
08-11 15:45:30.048 15608-15608/com.simon.activity I/MyService: startDownload -Threadmain
可以看到,点击绑定Service后,先执行了onCreate和onBind方法。然后在ServiceConnection的onServiceConnection中执行了binder对象的startDownload方法。 我们看到打印的日志中的线程名可以确定startDownload是执行在主线程中的。
现在我们就可以在Activity中任意调用绑定的Service对象了。
需要注意的是,Service在整个应用范围内是通用的,即MyService不仅可以和MainActivity建立关联,也可以可以其他Acitivity建立关联。 建立关联的时候拿到的binder对象是同一个实例。并且再次建立关联的时候Service的onCreate方法不会执行。
1.在Service的基本用法中,我们介绍了销毁Service最简单的一种情况,点击Start Service按钮启动Service,再点击Stop Service按钮停止service.这样MyService就销毁了。 打印日志如下:
08-11 16:10:52.529 22596-22596/com.simon.activity I/MyService: onCreate
08-11 16:10:52.565 22596-22596/com.simon.activity I/MyService: onStartCommand
08-11 16:10:54.619 22596-22596/com.simon.activity I/MyService: onDestroy
2.如果是点击binderService按钮呢? 由于在绑定Service的时候指定的标志位是BIND_AUTO_CREATE. 说明点击bind service按钮的时候Service也会创建,这时应该怎么销毁Service呢? 其实很简单,点击一下 Unbind Service按钮将Service解除关联就可以了。
日志如下:
08-11 16:14:15.137 22596-22596/com.simon.activity I/MyService: onCreate
08-11 16:14:15.139 22596-22596/com.simon.activity I/MyService: onBind
08-11 16:14:16.655 22596-22596/com.simon.activity I/MyService: onDestroy
3.如果是即点击start Service又点击了bind service. 要怎么销毁呢?
这样情况下,单独stop service和unbindService都不能销毁Service。 需要stop service和unbindService都点击一下,才能正常销毁。
08-11 16:19:57.506 22596-22596/com.simon.activity I/MyService: onCreate
08-11 16:19:57.506 22596-22596/com.simon.activity I/MyService: onStartCommand
08-11 16:19:59.960 22596-22596/com.simon.activity I/MyService: onBind
08-11 16:20:06.824 22596-22596/com.simon.activity I/MyService: onDestroy
这里我们要正确的理解Service和Thread的关系,Service有后台处理的概念,所以很容易和Thread的混淆在一起。其实Service是运行在主线程中的,Service所谓的后台,指定的是不需要UI的。即使Activity被销毁或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用需要始终和服务器保持心跳连接,就可以使用Service来实现。我们在Service中执行耗时任务,还是需要启动线程去执行。
Service中启动线程去执行耗时任务和Activity中启动线程又什么区别呢? 原因是在Activity中启动线程,如果Activity被销毁,就没有办法可以再重新获取到之前创建的实例。 而且一个Activity创建的子线程,另一个Activity也无法对其操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便的操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就可以重新获得Service中的Binder的实例。
Service几乎都是在后台运行的,一直以来都是在后台默默工作。但是Service的系统优先级还是比较低,当系统出现内存不足情况下,就有可能会回收正在后台运行的Service.如果你希望Service可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台Service. 前台Service和普通Service最大的区别就在于,它会一直有一个正在运行的图标在系统状态栏显示。下面示例如何创建前台Service:
public class FrontService extends Service {
@Override
public void onCreate() {
super.onCreate();
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("这是通知的标题")
.setContentText("这是通知的内容")
.setContentIntent(PendingIntent.getService(this, 0, new Intent(this, FrontService.class), 0))
.build();
startForeground(1, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
可以看到我们在Service的onCreate方法中先创建了一个Notification对象,然后调用了startForeground方法。将这个Service变成前台Service。