服务(Service)是 Android 中实现程序后台运行的解决方案,它很是适合去执行那些不须要和用户交互并且还须要长期进行的任务。服务的运行不依赖于任何用户界面,即便程序被切换到后台,或者用户打开了另一个应用程序,服务仍然可以保持正常运行。java
不过须要注意的是,服务并非运行在一个独立的进程当中的,而是依赖于建立服务时所在的应用程序进程。当某个应用程序进程被杀掉时,全部依赖于该进程的服务也会中止运行。android
首先,咱们定义一个 MyService.java 类,固然做为一个服务类,必需要继承 Service(android.app.Service),看代码:编程
// 源码路径:frameworks/base/core/java/android/app/Service.java public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { private static final String TAG = "Service"; ... ... @Nullable public abstract IBinder onBind(Intent intent); ... ... }
Service 定义了一个抽象方法 onBind,子类继承它,必须复写此方法。多线程
public class MyService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { // 这是一个抽象方法,那么子类是必需要重写的 return null; } }
服务既然已经定义好了,天然应该在服务中去处理一些事情,那处理事情的逻辑应该写在哪里?咱们须要在服务里重写 Service 中的另外一些经常使用的方法:app
public class MyService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { // 这是一个抽象方法,那么子类是必需要重写的 return null; } @Override public void onCreate() { // 服务建立时调用 super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 服务启动时调用 return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { // 服务销毁时调用 super.onDestroy(); } }
和添加 Activity 同样,咱们添加了一个服务,那么在 AndroidManifest.xml 文件中必须进行注册才能生效!ide
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.xin02ma.myapplication"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" ...... <activity android:name=".MainActivity"> ...... </activity> <service android:name=".MyService" android:enabled="true" android:exported="true" /> </application> </manifest>
服务定义好了,接下来就应该考虑如何去启动以及中止这个服务了。函数
(1)先添加两个Button(activity_main.xml)布局
<Button android:id="@+id/start_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/start_service" /> <Button android:id="@+id/stop_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/stop_service" />
<center></center>测试
(2)接下来,修改主函数 MainActivity 的代码:ui
public class MainActivity extends Activity implements View.OnClickListener{ private Button startService; private Button stopService; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 采用布局 startService = (Button) super.findViewById(R.id.start_service); // 取得Button实例 stopService = (Button) super.findViewById(R.id.stop_service); // 取得Button实例 startService.setOnClickListener(this); // 监控Button,注册按钮事件 stopService.setOnClickListener(this); // 监控Button,注册按钮时间 } 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; default: break; } } }
上面的代码很简单,主要做了如下工做:
(1)取得 startService 和 stopService 两个按钮实例,而且注册了点击事件;
(2)经过 Intent 对象,调用 Activity 的 startService() 和 stopService() 方法来启动和中止服务。
【Notice】
这里的活动的启动和中止彻底是由活动自己控制的,若是咱们 start 了服务,可是没有点击 stop,那么服务会一直处于运行状态,此时服务如何让本身中止下来?
只须要在 MyService 的任何一个位置调用 stopSelf() 这个方法就能让服务停下来!
添加Log,查看Service是如何运做的:
public class MyService extends Service{ private static final String TAG = "MyService"; @Nullable @Override public IBinder onBind(Intent intent) { // 这是一个抽象方法,那么子类是必需要重写的 return null; } public void onCreate() { // 服务建立时调用 super.onCreate(); Log.d(TAG, "onCreate executed"); } public int onStartCommand(Intent intent, int flags, int startId) { // 服务启动时调用 Log.d(TAG, "onStartCommand executed"); return super.onStartCommand(intent, flags, startId); } public void onDestroy() { // 服务销毁时调用 super.onDestroy(); Log.d(TAG, "onDestroy executed"); } }
添加了3行Log,目的就是看:在咱们点击两个按钮的时候,整个Service何时建立,何时启动,何时毁灭!
咱们来看一下执行结果,运行程序,查看Logcat中的打印日志:
(1)第一次点击 StartService按钮 后,MyService中的 onCreate() 和 onStartCommand() 方法都执行了,图中黄色箭头所示!
此时,咱们能够在 手机 --> 设置 --> 应用 --> 运行中 能够看到这个服务:
(2)而后咱们点击 stopService按钮 后,MyService中的 onDestory() 方法被执行,图中蓝色箭头所示!
(3)此时可能你会有一个疑问?当咱们点击了 startService按钮 之后, onCreate() 和 onStartCommand() 方法同时被执行,这两个方法有什么区别?
图中的 红色箭头 给了咱们答案: onCreat() 方法是在服务第一次建立的时候调用的,而 onStartCommand() 方法则在每次启动服务的时候都会被调用。
当咱们在 服务未启动 的时候,点击 startService 按钮 ,则此时会 执行两个方法 ;
可是 服务启动完成 以后,再次点击(随便你点几回) startService按钮 ,你会发现 只有onStartCommand()方法被执行 。
上面介绍完 Service 的使用方法,接下来看看 Service 的 生命周期 :跟Activity相比,Service的生命周期很简单: onCreate()->onStart()->onDestroy()
咱们以以下的方式展开这章节的讨论工做!
【主题】 :Activity 与 Service之间的 Communication
【问题】 :由上贴咱们知道,当咱们点击 START SERVICE 按钮后,服务的 onCreate() 和 onStartCommand() 方法会获得执行,此后 Service 是一直存在于后台运行的,Activity 没法控制 Service 中具体的逻辑运行,那么这样 Activity 只至关于起到一个通知的做用,除了告诉 Service 你能够开始工做了。那么这样显然会分离二者之间的关联性,这也不是咱们须要的结果!
【后果】 :若是出现以上的问题,那么在咱们平时的项目开发过程当中,一直存在的 Service 颇有可能会引发功耗的问题,可能影响手机的运行效率!
【要求】 :咱们可否将 Activity 与 Service 创建一种联系,当 Activity 终结之时,Service 也销毁,也就是有没有办法让 Activity 和 Service 可以 “不求同生,但求共死” ?
答案是确定的! 这就涉及到 Service 的另外一个重要知识点: 绑定 与 解绑 !
仍是以代码为例:
MyService.java
public class MyService extends Service{ private static final String TAG = "MyService"; private DownloadBinder mBinder = new DownloadBinder(); // 定义一个 DownloadBinder 类 class DownloadBinder extends Binder { // 让 DownloadBinder 成为 Binder 的子类 public void startDownload() { // 定义开始下载的方法 Log.d(TAG, "startDownload executed"); } public int getProgress() { // 定义一个查看下载进度的方法 Log.d(TAG, "getProgress executed"); return 0; } } @Nullable @Override public IBinder onBind(Intent intent) { // onBind()方法,这个方法将在绑定后调用 return mBinder; // 返回 IBinder 的实例 --> DownloadBinder 类的实例 } public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate executed"); } public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand executed"); return super.onStartCommand(intent, flags, startId); } public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy executed"); } }
咱们在Layout中添加两个按钮 BIND SERVICE 和 UNBIND SERVICE :
MainActivity.java
public class MainActivity extends Activity implements View.OnClickListener{ private ServiceConnection connection = new ServiceConnection() { // 建立一个 ServiceConnection 的匿名内部类 @Override public void onServiceConnected(ComponentName name, IBinder service) { // 重写 onServiceConnected() 方法 MyService.DownloadBinder downloadBinder = (MyService.DownloadBinder) service; // 向下转型取得 downloadBinder 实例 downloadBinder.startDownload(); // 在 Activity 中调用 Service 的方法 downloadBinder.getProgress(); // 在 Activity 中调用 Service 的方法 } @Override public void onServiceDisconnected(ComponentName name) { // 重写onServiceDisconnected()方法 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startService = (Button) super.findViewById(R.id.start_service); Button stopService = (Button) super.findViewById(R.id.stop_service); Button bindService = (Button) super.findViewById(R.id.bind_service); Button unbindService = (Button) super.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); // START 服务 --> onCreate() --> onStartCommand() startService(startIntent); break; case R.id.stop_service: Intent stopIntent = new Intent(this, MyService.class); // STOP 服务 --> onDestroy() stopService(stopIntent); break; case R.id.bind_service: // 绑定 --> ? Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE); // 💥 💥 💥 💥 重点分析 break; case R.id.unbind_service: // 解绑 --> ? unbindService(connection); break; default: break; } } }
看一下 bindService(bindIntent, connection, BIND_AUTO_CREATE) 这个方法:
bindService 接收了 3 个参数:
bindIntent :这个参数传入的就是咱们的 intent,目的就是调用 MyService 这个服务。
connection :这个参数传入的就是建立好的 ServiceConnection 的实例,这个参数表明着咱们的 Activity 是要和 Service 绑定在一块儿的!
BIND_AUTO_CREATE :这是一个 FLAG,表示在活动和服务进行绑定后 自动建立服务 。注意!是自动建立服务,也就是说 MyService 会执行 onCreate() 方法,可是不会执行 onStartCommand() 方法!
接下来,直接看代码最终的效果:
经过排列组合,对按钮进行点击,Log分 3 种状况:
START SERVICE + STOP SERVICE:
一、当咱们先点击 START SERVICE :此时服务启动,调用 onCreat() 和 onStartCommand() 方法;
二、当咱们后点击 STOP SERVICE :此时,服务被销毁,调用 onDestroy() 方法。
BIND SERVICE + UNBIND SERVICE:
一、当咱们先点击 BIND SERVICE :此时服务仅仅是建立,并未启动!因此调用的只是 onCreate() 方法。此时 Activity 与 Service 绑定,会同时调用 onBind() 方法,此时 onServiceConnected() 方法会被执行,还记的 onBind() 方法的返回类型不?咱们经过 Log 能够很明显发现,Activity 调用了服务内部的两个自定义方法。
二、当咱们后点击 UNBIND SERVICE :因为服务还未启动,而 BIND SERVICE 只是将服务建立好并与活动进行绑定,那么解绑后,势必会销毁这个 Service,因此 onDestroy() 被执行!
START SERVICE + BIND SERVICE + UNBIND SERVICE + STOP SERVICE:
一、咱们先点击 START SERVICE :onCreat() 和 onStartCommand() 方法被执行,这个就不用多说了;
二、而后点击 BIND SERVICE :这个时候其实活动已经在后台运行了,咱们此时将活动和服务绑定,那么 onCreate() 不会再执行,只会执行 onServiceConnected() 方法,Log 里面打出来看的很清楚。
三、此时你若是手贱,想 STOP SERVICE:那么恭喜你,毫无反应!为何?由于你都没解绑,你怎么销毁?
四、OK,那咱们先解绑,咱们点击 UNBIND SERVICE :此时一个奇怪的现象发生了,LOG 日志没有打印出 Destroy() 这个方法啊?没有被执行啊!不是说 bind 了 Service 以后,unbind 就会销毁这个服务吗?这跟咱们以前分析的不符合啊。
五、好吧,咱们来看看为何。其实缘由很简单:咱们先 start 了 Service,那么此时服务已经在后台运行了,这个时候你 bind,让 Service 和 Activity 绑定,实际上是没有什么意义的。可是既然绑定了,你若是不解绑,那么 Destroy() 毫无用武,因此,这种状况和(2)中分析的仍是有区别的,此是解绑完后,服务仍是舒舒服服的在后台运行,因此,要想干掉这个服务,你必需要 STOP SERVICE。
六、那咱们解绑后,再 STOP SERVICE :这个时候 Service 就被枪毙了!
服务几乎都是在后台运行的,一直一来它都是默默地作着辛苦的工做。可是服务的系统优先级仍是比较低的,当系统出现内存不足的状况时,就有可能会回收掉正在后台运行的服务。若是你但愿服务能够一直保持运行状态,而不是因为系统内存不足的缘由致使被回收掉,就能够考虑使用前台服务。前台服务和普通服务最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后能够看到更加详细的信息,很是相似于通知的效果。固然有时候你也可能不只仅是为了防止服务被回收掉才使用前台服务,有些项目因为特殊的需求会要求必须使用前台服务,好比说天气类软件,它的服务在后台更新天气数据的同时,还会在系统状态栏一直显示当前的天气信息。
【问题】 :咱们都知道服务是运行在后台的,若是系统出现内存不足的状况,那么此时,系统就可能回收后台的服务,那么咱们如何保证服务能够一直运行?
【解决】 :在服务中,有一个 前台服务 的概念,调用 startForground() 方法能够实现。
如何建立一个前台服务,看代码:
public class MyService extends Service{ ...... @Override public void onCreate() { super.onCreate(); Log.d("MyService", "onCreate executed"); Intent intent = new Intent(this, MainActivity.class); PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0); Notification notification = new Notification.Builder(this) // 启动服务后,在前台添加一个Notification .setContentTitle("This is a content title") .setContentText("This is content text") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) .setContentIntent(pi) .build(); startForeground(1, notification); } ... ... }
以上的代码是在 Service 的建立中添加了一个 Notification,调用 startForground() 就能够保证:只要服务一直存在,那么在前台就会一直显示这个 Notification。
若是咱们在 onDestroy() 中调用 stopForground() 方法,会销毁这个 Notification,可是 Service 仍是存活的,此时 Service 就会面临被 System 干掉的风险。
若是直接 STOP SERVICE,那么 Notification 和 Service 都会销毁。
【问题】 :咱们知道服务的代码逻辑是在主线程中执行的,若是咱们在主线程中须要执行一些耗时的操做,那么颇有可能出现ANR(程序暂无响应)的情况。
这个时候,咱们能够采用 Android 的多线程编程的方式,咱们应该在服务的每一个具体的方法里开启一个子线程,而后在这里去处理那些耗时的逻辑。因此,一个比较标准的服务就能够写成以下形式:
public class MyService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { new Thread(new Runnable() { // 开启一个线程处理耗时操做 @Override public void run() { // 处理具体的逻辑 } }).start(); return super.onStartCommand(intent, flags, startId); } }
如今,服务能够启动起来了。可是若是不调用 StopService() 或 stopSelf() 方法,服务会一直运行,因此咱们须要修改一下代码:
public class MyService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { new Thread(new Runnable() { @Override public void run() { // 处理具体的逻辑 // 开启一个线程处理耗时操做 stopSelf(); // 让服务执行完逻辑后自行中止 } }).start(); return super.onStartCommand(intent, flags, startId); } }
上面的代码就是一个标准的 Service 的书写形式,主要包含两个知识点:Thread子线程的建立 和 stopSelf() 方法的调用。
虽然说这种写法并不复杂,可是总会有人忘记开启线程,或者忘记调用 stopSelf(),那么有没有更好的办法可以实现上面两个需求呢?
【解决】: 在 Android 中,专门提供了一个 IntentService 类(android.app.IntentService),这个类就能很好的知足咱们的需求!咱们直接经过代码来看:
新建一个 MyIntentService 类继承自 IntentService,代码以下:
public class MyIntentService extends IntentService{ public MyIntentService() { super("MyIntentService"); // 调用父类的有参构造函数 } @Override protected void onHandleIntent(Intent intent) { // 打印当前线程的 id Log.d("MyIntentService", "MyIntentServiceThread id is " + Thread.currentThread().getId()); } @Override public void onDestroy() { super.onDestroy(); Log.d("MyIntentService", "onDestroy executed"); } }
以上代码作了几件事:
一、提供了一个无参的构造方法,而且调用了父类的有参构造函数;
二、子类实现父类的 onHandleIntent() 抽象方法,这个方法好就好在,它是一个已经运行在子线程中的方法。也就是说,服务调用了它,那么执行的逻辑就如同 Thread 子线程;
三、根据 IntentService 的特性,这个服务在运行结束后应该是会自动中止的,因此咱们又重写了 onDestroy()方法,在这里也打印一行日志,以证明服务是否是中止掉了。
咱们在 xml 文件中,建立一个 MyIntentService 服务按钮:
<Button android:id="@+id/start_intent_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/intent_service"/>
而后修改 MainActivity 中的代码:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ... Button startIntentService = (Button) super.findViewById(R.id.start_intent_service); startIntentService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d("MyIntentService", "MainActivity Thread id is " + Thread.currentThread().getId()); // 查看主线程的id Intent intentService = new Intent(getBaseContext(), MyIntentService.class); startService(intentService); } }); } }
最后,在AndroidMainfest中注册服务:
<service android:name=".MyIntentService" />
【结果】 :
从打出的LOG能够看出:
一、MyIntentService 和 MainActivity 所在进程的 id是不同的 ;
二、onHandleIntent() 方法在执行完逻辑后确实销毁了服务,效果等同于 stopSelf()。
从上面的分析能够看出 onHandleIntent() 方法确实至关的好用!