转载请注明出处(万分感谢!):
http://blog.csdn.net/javazejian/article/details/52709857
出自【zejian的博客】html
Service所有内容基本会在本篇涉及到,咱们将围绕如下主要知识点进行分析:java
Service(服务)是一个一种能够在后台执行长时间运行操做而没有用户界面的应用组件。服务可由其余应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即便启动服务的组件(Activity)已销毁也不受影响。 此外,组件能够绑定到服务,以与之进行交互,甚至是执行进程间通讯 (IPC)。 例如,服务能够处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而全部这一切都可在后台进行,Service基本上分为两种形式:linux
当应用组件(如 Activity)经过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务便可在后台无限期运行,即便启动服务的组件已被销毁也不受影响,除非手动调用才能中止服务, 已启动的服务一般是执行单一操做,并且不会将结果返回给调用方。android
当应用组件经过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,容许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通讯 (IPC) 跨进程执行这些操做。 仅当与另外一个应用组件绑定时,绑定服务才会运行。 多个组件能够同时绑定到该服务,但所有取消绑定后,该服务即会被销毁。数据库
前面说过Service分为启动状态和绑定状态两种,但不管哪一种具体的Service启动类型,都是经过继承Service基类自定义而来,也都须要在AndroidManifest.xml中声明,那么在分析这两种状态以前,咱们先来了解一下Service在AndroidManifest.xml中的声明语法,其格式以下:编程
<service android:enabled=["true" | "false"] android:exported=["true" | "false"] android:icon="drawable resource" android:isolatedProcess=["true" | "false"] android:label="string resource" android:name="string" android:permission="string" android:process="string" > . . . </service>
android:exported:表明是否能被其余应用隐式调用,其默认值是由service中有无intent-filter决定的,若是有intent-filter,默认值为true,不然为false。为false的状况下,即便有intent-filter匹配,也没法打开,即没法被其余应用隐式调用。安全
android:name:对应Service类名服务器
android:permission:是权限声明网络
android:process:是否须要在单独的进程中运行,当设置为android:process=”:remote”时,表明Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,因此“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,然后者的进程名称为:App-packageName:remote。多线程
android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其余进程分开且没有本身的权限。与其通讯的惟一途径是经过服务的API(bind and start)。
android:enabled:是否能够被系统实例化,默认为 true由于父标签 也有 enable 属性,因此必须两个都为默认值 true 的状况下服务才会被激活,不然不会激活。
ok~,关于Service在清单文件的声明咱们先了解这些就行,接下来分别针对Service启动服务和绑定服务进行详细分析
首先要建立服务,必须建立 Service 的子类(或使用它的一个现有子类如IntentService)。在实现中,咱们须要重写一些回调方法,以处理服务生命周期的某些关键过程,下面咱们经过简单案例来分析须要重写的回调方法有哪些?
package com.zejian.ipctest.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.support.annotation.Nullable; /** * Created by zejian * Time 2016/9/29. * Description:service simple demo */ public class SimpleService extends Service { /** * 绑定服务时才会调用 * 必需要实现的方法 * @param intent * @return */ @Nullable @Override public IBinder onBind(Intent intent) { return null; } /** * 首次建立服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 以前)。 * 若是服务已在运行,则不会调用此方法。该方法只被调用一次 */ @Override public void onCreate() { System.out.println("onCreate invoke"); super.onCreate(); } /** * 每次经过startService()方法启动Service时都会被回调。 * @param intent * @param flags * @param startId * @return */ @Override public int onStartCommand(Intent intent, int flags, int startId) { System.out.println("onStartCommand invoke"); return super.onStartCommand(intent, flags, startId); } /** * 服务销毁时的回调 */ @Override public void onDestroy() { System.out.println("onDestroy invoke"); super.onDestroy(); } }
从上面的代码咱们能够看出SimpleService继承了Service类,并重写了onBind方法,该方法是必须重写的,可是因为此时是启动状态的服务,则该方法无须实现,返回null便可,只有在绑定状态的状况下才须要实现该方法并返回一个IBinder的实现类(这个后面会详细说),接着重写了onCreate、onStartCommand、onDestroy三个主要的生命周期方法,关于这几个方法说明以下:
当另外一个组件想经过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通讯。不管是启动状态仍是绑定状态,此方法必须重写,但在启动状态的状况下直接返回 null。
首次建立服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 以前)。若是服务已在运行,则不会调用此方法,该方法只调用一次
当另外一个组件(如 Activity)经过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 若是本身实现此方法,则须要在服务工做完成后,经过调用 stopSelf() 或 stopService() 来中止服务。(在绑定状态下,无需实现此方法。)
当服务再也不使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理全部资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
咱们经过Demo测试一下Service启动状态方法的调用顺序,MainActivity代码以下:
package com.zejian.ipctest; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import com.zejian.ipctest.service.SimpleService; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button startBtn; private Button stopBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startBtn= (Button) findViewById(R.id.startService); stopBtn= (Button) findViewById(R.id.stopService); startBtn.setOnClickListener(this); assert stopBtn != null; stopBtn.setOnClickListener(this); } @Override public void onClick(View v) { Intent it=new Intent(this, SimpleService.class); switch (v.getId()){ case R.id.startService: startService(it); break; case R.id.stopService: stopService(it); break; } } }
记得在清单配置文件中声明Service(声明方式跟Activity类似):
<manifest ... > ... <application ... > <service android:name=".service.SimpleService" /> ... </application> </manifest>
从代码看出,启动服务使用startService(Intent intent)方法,仅须要传递一个Intent对象便可,在Intent对象中指定须要启动的服务。而使用startService()方法启动的服务,在服务的外部,必须使用stopService()方法中止,在服务的内部能够调用stopSelf()方法中止当前服务。若是使用startService()或者stopSelf()方法请求中止服务,系统会就会尽快销毁这个服务。值得注意的是对于启动服务,一旦启动将与访问它的组件无任何关联,即便访问它的组件被销毁了,这个服务也一直运行下去,直到手动调用中止服务才被销毁,至于onBind方法,只有在绑定服务时才会起做用,在启动状态下,无需关注此方法,ok~,咱们运行程序并屡次调用startService方法,最后调用stopService方法。Log截图以下:
从Log能够看出,第一次调用startService方法时,onCreate方法、onStartCommand方法将依次被调用,而屡次调用startService时,只有onStartCommand方法被调用,最后咱们调用stopService方法中止服务时onDestory方法被回调,这就是启动状态下Service的执行周期。接着咱们从新回过头来进一步分析onStartCommand(Intent intent, int flags, int startId),这个方法有3个传入参数,它们的含义以下:
onStartCommand(Intent intent, int flags, int startId)
intent :启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所须要的参数并传递给Service
flags:表示启动请求时是否有额外数据,可选值有 0,START_FLAG_REDELIVERY,START_FLAG_RETRY,0表明没有,它们具体含义以下:
START_FLAG_REDELIVERY
这个值表明了onStartCommand方法的返回值为
START_REDELIVER_INTENT,并且在上一次服务被杀死前会去调用stopSelf方法中止服务。其中START_REDELIVER_INTENT意味着当Service因内存不足而被系统kill后,则会重建服务,并经过传递给服务的最后一个 Intent 调用 onStartCommand(),此时Intent时有值的。
START_FLAG_RETRY
该flag表明当onStartCommand调用后一直没有返回值时,会尝试从新去调用onStartCommand()。
startId : 指明当前服务的惟一ID,与stopSelfResult (int startId)配合使用,stopSelfResult 能够更安全地根据ID中止服务。
实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值, START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义以下:
START_STICKY
当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试从新建立此Service,一旦建立成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待做业的媒体播放器或相似服务。
START_NOT_STICKY
当Service因内存不足而被系统kill后,即便系统内存再次空闲时,系统也不会尝试从新建立此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,能够避免在没必要要时以及应用可以轻松重启全部未完成的做业时运行服务。
START_REDELIVER_INTENT
当Service因内存不足而被系统kill后,则会重建服务,并经过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不一样的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该当即恢复的做业(例以下载文件)的服务。
因为每次启动服务(调用startService)时,onStartCommand方法都会被调用,所以咱们能够经过该方法使用Intent给Service传递所须要的参数,而后在onStartCommand方法中处理的事件,最后根据需求选择不一样的Flag返回值,以达到对程序更友好的控制。好~,以上即是Service在启动状态下的分析,接着咱们在来看看绑定状态的Service又是如何处理的?
绑定服务是Service的另外一种变形,当Service处于绑定状态时,其表明着客户端-服务器接口中的服务器。当其余组件(如 Activity)绑定到服务时(有时咱们可能须要从Activity组建中去调用Service中的方法,此时Activity以绑定的方式挂靠到Service后,咱们就能够轻松地方法到Service中的指定方法),组件(如Activity)能够向Service(也就是服务端)发送请求,或者调用Service(服务端)的方法,此时被绑定的Service(服务端)会接收信息并响应,甚至能够经过绑定服务进行执行进程间通讯 (即IPC,这个后面再单独分析)。与启动服务不一样的是绑定服务的生命周期一般只在为其余应用组件(如Activity)服务时处于活动状态,不会无限期在后台运行,也就是说宿主(如Activity)解除绑定后,绑定服务就会被销毁。那么在提供绑定的服务时,该如何实现呢?实际上咱们必须提供一个 IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口能够经过三种方法定义接口:
扩展 Binder 类
若是服务是提供给自有应用专用的,而且Service(服务端)与客户端相同的进程中运行(常见状况),则应经过扩展 Binder 类并从 onBind() 返回它的一个实例来建立接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现中以及Service 中可用的公共方法。若是咱们的服务只是自有应用的后台工做线程,则优先采用这种方法。 不采用该方式建立接口的惟一缘由是,服务被其余应用或不一样的进程调用。
使用 Messenger
Messenger能够翻译为信使,经过它能够在不一样的进程中共传递Message对象(Handler中的Messager,所以 Handler 是 Messenger 的基础),在Message中能够存放咱们须要传递的数据,而后在进程间传递。若是须要让接口跨不一样的进程工做,则可以使用 Messenger 为服务建立接口,客户端就可利用 Message 对象向服务发送命令。同时客户端也可定义自有 Messenger,以便服务回传消息。这是执行进程间通讯 (IPC) 的最简单方法,由于 Messenger 会在单一线程中建立包含全部请求的队列,也就是说Messenger是以串行的方式处理客户端发来的消息,这样咱们就没必要对服务进行线程安全设计了。
以上3种实现方式,咱们能够根据需求自由的选择,但须要注意的是大多数应用“都不会”使用 AIDL 来建立绑定服务,由于它可能要求具有多线程处理能力,并可能致使实现的复杂性增长。所以,AIDL 并不适合大多数应用,本篇中也不打算阐述如何使用AIDL(后面会另开一篇分析AIDL),接下来咱们分别针对扩展 Binder 类和Messenger的使用进行分析。
前面描述过,若是咱们的服务仅供本地应用使用,不须要跨进程工做,则能够实现自有 Binder 类,让客户端经过该类直接访问服务中的公共方法。其使用开发步骤以下
注意:此方式只有在客户端和服务位于同一应用和进程内才有效,如对于须要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方式很是有效。另外一点之因此要求服务和客户端必须在同一应用内,是为了便于客户端转换返回的对象和正确调用其 API。服务和客户端还必须在同一进程内,由于此方式不执行任何跨进程编组。
如下是一个扩展 Binder 类的实例,先看看Service端的实现BindService.java
package com.zejian.ipctest.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.support.annotation.Nullable; import android.util.Log; /** * Created by zejian * Time 2016/10/2. * Description:绑定服务简单实例--服务端 */ public class LocalService extends Service{ private final static String TAG = "wzj"; private int count; private boolean quit; private Thread thread; private LocalBinder binder = new LocalBinder(); /** * 建立Binder对象,返回给客户端即Activity使用,提供数据交换的接口 */ public class LocalBinder extends Binder { // 声明一个方法,getService。(提供给客户端调用) LocalService getService() { // 返回当前对象LocalService,这样咱们就可在客户端端调用Service的公共方法了 return LocalService.this; } } /** * 把Binder类返回给客户端 */ @Nullable @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Service is invoke Created"); thread = new Thread(new Runnable() { @Override public void run() { // 每间隔一秒count加1 ,直到quit为true。 while (!quit) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count++; } } }); thread.start(); } /** * 公共方法 * @return */ public int getCount(){ return count; } /** * 解除绑定时调用 * @return */ @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "Service is invoke onUnbind"); return super.onUnbind(intent); } @Override public void onDestroy() { Log.i(TAG, "Service is invoke Destroyed"); this.quit = true; super.onDestroy(); } }
BindService类继承自Service,在该类中建立了一个LocalBinder继承自Binder类,LocalBinder中声明了一个getService方法,客户端可访问该方法获取LocalService对象的实例,只要客户端获取到LocalService对象的实例就可调用LocalService服务端的公共方法,如getCount方法,值得注意的是,咱们在onBind方法中返回了binder对象,该对象即是LocalBinder的具体实例,而binder对象最终会返回给客户端,客户端经过返回的binder对象即可以与服务端实现交互。接着看看客户端BindActivity的实现:
package com.zejian.ipctest.service; import android.app.Activity; import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.view.View; import android.widget.Button; import com.zejian.ipctest.R; /** * Created by zejian * Time 2016/10/2. * Description:绑定服务实例--客户端 */ public class BindActivity extends Activity { protected static final String TAG = "wzj"; Button btnBind; Button btnUnBind; Button btnGetDatas; /** * ServiceConnection表明与服务的链接,它只有两个方法, * onServiceConnected和onServiceDisconnected, * 前者是在操做者在链接一个服务成功时被调用,然后者是在服务崩溃或被杀死致使的链接中断时被调用 */ private ServiceConnection conn; private LocalService mService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bind); btnBind = (Button) findViewById(R.id.BindService); btnUnBind = (Button) findViewById(R.id.unBindService); btnGetDatas = (Button) findViewById(R.id.getServiceDatas); //建立绑定对象 final Intent intent = new Intent(this, LocalService.class); // 开启绑定 btnBind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "绑定调用:bindService"); //调用绑定方法 bindService(intent, conn, Service.BIND_AUTO_CREATE); } }); // 解除绑定 btnUnBind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "解除绑定调用:unbindService"); // 解除绑定 if(mService!=null) { mService = null; unbindService(conn); } } }); // 获取数据 btnGetDatas.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mService != null) { // 经过绑定服务传递的Binder对象,获取Service暴露出来的数据 Log.d(TAG, "从服务端获取数据:" + mService.getCount()); } else { Log.d(TAG, "还没绑定呢,先绑定,没法从服务端获取数据"); } } }); conn = new ServiceConnection() { /** * 与服务器端交互的接口方法 绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象, * 经过这个IBinder对象,实现宿主和Service的交互。 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "绑定成功调用:onServiceConnected"); // 获取Binder LocalService.LocalBinder binder = (LocalService.LocalBinder) service; mService = binder.getService(); } /** * 当取消绑定的时候被回调。但正常状况下是不被调用的,它的调用时机是当Service服务被意外销毁时, * 例如内存的资源不足时这个方法才被自动调用。 */ @Override public void onServiceDisconnected(ComponentName name) { mService=null; } }; } }
在客户端中咱们建立了一个ServiceConnection对象,该表明与服务的链接,它只有两个方法, onServiceConnected和onServiceDisconnected,其含义以下:
onServiceConnected(ComponentName name, IBinder service)
系统会调用该方法以传递服务的 onBind() 方法返回的 IBinder。其中service即是服务端返回的IBinder实现类对象,经过该对象咱们即可以调用获取LocalService实例对象,进而调用服务端的公共方法。而ComponentName是一个封装了组件(Activity, Service, BroadcastReceiver, or ContentProvider)信息的类,如包名,组件描述等信息,较少使用该参数。
onServiceDisconnected(ComponentName name)
Android 系统会在与服务的链接意外中断时(例如当服务崩溃或被终止时)调用该方法。注意:当客户端取消绑定时,系统“绝对不会”调用该方法。
conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "绑定成功调用:onServiceConnected"); // 获取Binder LocalService.LocalBinder binder = (LocalService.LocalBinder) service; mService = binder.getService(); } @Override public void onServiceDisconnected(ComponentName name) { mService=null; } };
在onServiceConnected()被回调前,咱们还需先把当前Activity绑定到服务LocalService上,绑定服务是经过经过bindService()方法,解绑服务则使用unbindService()方法,这两个方法解析以下:
bindService(Intent service, ServiceConnection conn, int flags)
该方法执行绑定服务操做,其中Intent是咱们要绑定的服务(也就是LocalService)的意图,而ServiceConnection表明与服务的链接,它只有两个方法,前面已分析过,flags则是指定绑定时是否自动建立Service。0表明不自动建立、BIND_AUTO_CREATE则表明自动建立。
unbindService(ServiceConnection conn)
该方法执行解除绑定的操做,其中ServiceConnection表明与服务的链接,它只有两个方法,前面已分析过。
Activity经过bindService()绑定到LocalService后,ServiceConnection#onServiceConnected()便会被回调并能够获取到LocalService实例对象mService,以后咱们就能够调用LocalService服务端的公共方法了,最后还须要在清单文件中声明该Service。而客户端布局文件实现以下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/BindService" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="绑定服务器" /> <Button android:id="@+id/unBindService" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="解除绑定" /> <Button android:id="@+id/getServiceDatas" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="获取服务方数据" /> </LinearLayout>
咱们运行程序,点击绑定服务并屡次点击绑定服务接着屡次调用LocalService中的getCount()获取数据,最后调用解除绑定的方法移除服务,其结果以下:
经过Log可知,当咱们第一次点击绑定服务时,LocalService服务端的onCreate()、onBind方法会依次被调用,此时客户端的ServiceConnection#onServiceConnected()被调用并返回LocalBinder对象,接着调用LocalBinder#getService方法返回LocalService实例对象,此时客户端便持有了LocalService的实例对象,也就能够任意调用LocalService类中的声明公共方法了。更值得注意的是,咱们屡次调用bindService方法绑定LocalService服务端,而LocalService得onBind方法只调用了一次,那就是在第一次调用bindService时才会回调onBind方法。接着咱们点击获取服务端的数据,从Log中看出咱们点击了3次经过getCount()获取了服务端的3个不一样数据,最后点击解除绑定,此时LocalService的onUnBind、onDestroy方法依次被回调,而且屡次绑定只需一次解绑便可。此情景也就说明了绑定状态下的Service生命周期方法的调用依次为onCreate()、onBind、onUnBind、onDestroy。ok~,以上即是同一应用同一进程中客户端与服务端的绑定回调方式。
前面了解了如何使用IBinder应用内同一进程的通讯后,咱们接着来了解服务与远程进程(即不一样进程间)通讯,而不一样进程间的通讯,最简单的方式就是使用 Messenger 服务提供通讯接口,利用此方式,咱们无需使用 AIDL 即可执行进程间通讯 (IPC)。如下是 Messenger 使用的主要步骤:
1.服务实现一个 Handler,由其接收来自客户端的每一个调用的回调
2.Handler 用于建立 Messenger 对象(对 Handler 的引用)
3.Messenger 建立一个 IBinder,服务经过 onBind() 使其返回客户端
4.客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,而后使用Messenger将 Message 对象发送给服务
如下是一个使用 Messenger 接口的简单服务示例,服务端进程实现以下:
package com.zejian.ipctest.messenger; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.util.Log; /** * Created by zejian * Time 2016/10/3. * Description:Messenger服务端简单实例,服务端进程 */ public class MessengerService extends Service { /** Command to the service to display a message */ static final int MSG_SAY_HELLO = 1; private static final String TAG ="wzj" ; /** * 用于接收从客户端传递过来的数据 */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Log.i(TAG, "thanks,Service had receiver message from client!"); break; default: super.handleMessage(msg); } } } /** * 建立Messenger并传入Handler实例对象 */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * 当绑定Service时,该方法被调用,将经过mMessenger返回一个实现 * IBinder接口的实例对象 */ @Override public IBinder onBind(Intent intent) { Log.i(TAG, "Service is invoke onBind"); return mMessenger.getBinder(); } }
首先咱们一样须要建立一个服务类MessengerService继承自Service,同时建立一个继承自Handler的IncomingHandler对象来接收客户端进程发送过来的消息并经过其handleMessage(Message msg)进行消息处理。接着经过IncomingHandler对象建立一个Messenger对象,该对象是与客户端交互的特殊对象,而后在Service的onBind中返回这个Messenger对象的底层Binder便可。下面看看客户端进程的实现:
package com.zejian.ipctest.messenger; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.widget.Button; import com.zejian.ipctest.R; /** * Created by zejian * Time 2016/10/3. * Description: 与服务器交互的客户端 */ public class ActivityMessenger extends Activity { /** * 与服务端交互的Messenger */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean mBound; /** * 实现与服务端连接的对象 */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { /** * 经过服务端传递的IBinder对象,建立相应的Messenger * 经过该Messenger对象与服务端进行交互 */ mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mBound = false; } }; public void sayHello(View v) { if (!mBound) return; // 建立与服务交互的消息实体Message Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { //发送消息 mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenager); Button bindService= (Button) findViewById(R.id.bindService); Button unbindService= (Button) findViewById(R.id.unbindService); Button sendMsg= (Button) findViewById(R.id.sendMsgToService); bindService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d("zj","onClick-->bindService"); //当前Activity绑定服务端 bindService(new Intent(ActivityMessenger.this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } }); //发送消息给服务端 sendMsg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sayHello(v); } }); unbindService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Unbind from the service if (mBound) { Log.d("zj","onClick-->unbindService"); unbindService(mConnection); mBound = false; } } }); } }
在客户端进程中,咱们须要建立一个ServiceConnection对象,该对象表明与服务端的连接,当调用bindService方法将当前Activity绑定到MessengerService时,onServiceConnected方法被调用,利用服务端传递给来的底层Binder对象构造出与服务端交互的Messenger对象,接着建立与服务交互的消息实体Message,将要发生的信息封装在Message中并经过Messenger实例对象发送给服务端。关于ServiceConnection、bindService方法、unbindService方法,前面已分析过,这里就不重复了,最后咱们须要在清单文件声明Service和Activity,因为要测试不一样进程的交互,则须要将Service放在单独的进程中,所以Service声明以下:
<service android:name=".messenger.MessengerService" android:process=":remote" />
其中android:process=":remote"
表明该Service在单独的进程中建立,最后咱们运行程序,结果以下:
接着屡次点击绑定服务,而后发送信息给服务端,最后解除绑定,Log打印以下:
经过上述例子可知Service服务端确实收到了客户端发送的信息,并且在Messenger中进行数据传递必须将数据封装到Message中,由于Message和Messenger都实现了Parcelable接口,能够轻松跨进程传递数据(关于Parcelable接口能够看博主的另外一篇文章:序列化与反序列化之Parcelable和Serializable浅析),而Message能够传递的信息载体有,what,arg1,arg2,Bundle以及replyTo,至于object字段,对于同一进程中的数据传递确实很实用,但对于进程间的通讯,则显得至关尴尬,在android2.2前,object不支持跨进程传输,但即使是android2.2以后也只能传递android系统提供的实现了Parcelable接口的对象,也就是说咱们经过自定义实现Parcelable接口的对象没法经过object字段来传递,所以object字段的实用性在跨进程中也变得至关低了。不过所幸咱们还有Bundle对象,Bundle能够支持大量的数据类型。接着从Log咱们也看出不管是使用拓展Binder类的实现方式仍是使用Messenger的实现方式,它们的生命周期方法的调用顺序基本是同样的,即onCreate()、onBind、onUnBind、onDestroy,并且屡次绑定中也只有第一次时才调用onBind()。好~,以上的例子演示了如何在服务端解释客户端发送的消息,但有时候咱们可能还须要服务端能回应客户端,这时便须要提供双向消息传递了,下面就来实现一个简单服务端与客户端双向消息传递的简单例子。
先来看看服务端的修改,在服务端,咱们只需修改IncomingHandler,收到消息后,给客户端回复一条信息。
/** * 用于接收从客户端传递过来的数据 */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Log.i(TAG, "thanks,Service had receiver message from client!"); //回复客户端信息,该对象由客户端传递过来 Messenger client=msg.replyTo; //获取回复信息的消息实体 Message replyMsg=Message.obtain(null,MessengerService.MSG_SAY_HELLO); Bundle bundle=new Bundle(); bundle.putString("reply","ok~,I had receiver message from you! "); replyMsg.setData(bundle); //向客户端发送消息 try { client.send(replyMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } }
接着修改客户端,为了接收服务端的回复,客户端也须要一个接收消息的Messenger和Handler,其实现以下:
/** * 用于接收服务器返回的信息 */ private Messenger mRecevierReplyMsg= new Messenger(new ReceiverReplyMsgHandler()); private static class ReceiverReplyMsgHandler extends Handler{ private static final String TAG = "zj"; @Override public void handleMessage(Message msg) { switch (msg.what) { //接收服务端回复 case MessengerService.MSG_SAY_HELLO: Log.i(TAG, "receiver message from service:"+msg.getData().getString("reply")); break; default: super.handleMessage(msg); } } }
除了添加以上代码,还须要在发送信息时把接收服务器端的回复的Messenger经过Message的replyTo参数传递给服务端,以便做为同窗桥梁,代码以下:
public void sayHello(View v) { if (!mBound) return; // 建立与服务交互的消息实体Message Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); //把接收服务器端的回复的Messenger经过Message的replyTo参数传递给服务端 msg.replyTo=mRecevierReplyMsg; try { //发送消息 mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } }
ok~,到此服务端与客户端双向消息传递的简单例子修改完成,咱们运行一下代码,看看Log打印,以下:
由Log可知,服务端和客户端确实各自收到了信息,到此咱们就把采用Messenge进行跨进程通讯的方式分析完了,最后为了辅助你们理解,这里提供一张经过Messenge方式进行进程间通讯的原理图:
1.多个客户端可同时链接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),即可将同一 IBinder 传递至任何其余绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。
2.一般状况下咱们应该在客户端生命周期(如Activity的生命周期)的引入 (bring-up) 和退出 (tear-down) 时刻设置绑定和取消绑定操做,以便控制绑定状态下的Service,通常有如下两种状况:
若是只须要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
若是但愿 Activity 在后台中止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。须要注意的是,这意味着 Activity 在其整个运行过程当中(甚至包括后台运行期间)都须要使用服务,所以若是服务位于其余进程内,那么当提升该进程的权重时,系统极可能会终止该进程。
3.一般状况下(注意),切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,由于每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。此外,若是应用内的多个 Activity 绑定到同一服务,而且其中两个 Activity 之间发生了转换,则若是当前 Activity 在下一次绑定(恢复期间)以前取消绑定(暂停期间),系统可能会销毁服务并重建服务,所以服务的绑定不该该发生在 Activity 的 onResume() 和 onPause()中。
4.咱们应该始终捕获 DeadObjectException DeadObjectException 异常,该异常是在链接中断时引起的,表示调用的对象已死亡,也就是Service对象已销毁,这是远程方法引起的惟一异常,DeadObjectException继承自RemoteException,所以咱们也能够捕获RemoteException异常。
5.应用组件(客户端)可经过调用 bindService() 绑定到服务,Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder,而该绑定是异步执行的。
经过前面对两种服务状态的分析,相信你们已对Service的两种状态有了比较清晰的了解,那么如今咱们就来分析一下当启动状态和绑定状态同时存在时,又会是怎么的场景?
虽然服务的状态有启动和绑定两种,但实际上一个服务能够同时是这两种状态,也就是说,它既能够是启动服务(以无限期运行),也能够是绑定服务。有点须要注意的是Android系统仅会为一个Service建立一个实例对象,因此无论是启动服务仍是绑定服务,操做的是同一个Service实例,并且因为绑定服务或者启动服务执行顺序问题将会出现如下两种状况:
先绑定服务后启动服务
若是当前Service实例先以绑定状态运行,而后再以启动状态运行,那么绑定服务将会转为启动服务运行,这时若是以前绑定的宿主(Activity)被销毁了,也不会影响服务的运行,服务仍是会一直运行下去,指定收到调用中止服务或者内存不足时才会销毁该服务。
先启动服务后绑定服务
若是当前Service实例先以启动状态运行,而后再以绑定状态运行,当前启动服务并不会转为绑定服务,可是仍是会与宿主绑定,只是即便宿主解除绑定后,服务依然按启动服务的生命周期在后台运行,直到有Context调用了stopService()或是服务自己调用了stopSelf()方法抑或内存不足时才会销毁服务。
以上两种状况显示出启动服务的优先级确实比绑定服务高一些。不过不管Service是处于启动状态仍是绑定状态,或处于启动而且绑定状态,咱们均可以像使用Activity那样经过调用 Intent 来使用服务(即便此服务来自另外一应用)。 固然,咱们也能够经过清单文件将服务声明为私有服务,阻止其余应用访问。最后这里有点须要特殊说明一下的,因为服务在其托管进程的主线程中运行(UI线程),它既不建立本身的线程,也不在单独的进程中运行(除非另行指定)。 这意味着,若是服务将执行任何耗时事件或阻止性操做(例如 MP3 播放或联网)时,则应在服务内建立新线程来完成这项工做,简而言之,耗时操做应该另起线程执行。只有经过使用单独的线程,才能够下降发生“应用无响应”(ANR) 错误的风险,这样应用的主线程才能专一于用户与 Activity 之间的交互, 以达到更好的用户体验。
前台服务被认为是用户主动意识到的一种服务,所以在内存不足时,系统也不会考虑将其终止。 前台服务必须为状态栏提供通知,状态栏位于“正在进行”标题下方,这意味着除非服务中止或从前台删除,不然不能清除通知。例如将从服务播放音乐的音乐播放器设置为在前台运行,这是由于用户明确意识到其操做。 状态栏中的通知可能表示正在播放的歌曲,并容许用户启动 Activity 来与音乐播放器进行交互。若是须要设置服务运行于前台, 咱们该如何才能实现呢?Android官方给咱们提供了两个方法,分别是startForeground()和stopForeground(),这两个方式解析以下:
startForeground(int id, Notification notification)
该方法的做用是把当前服务设置为前台服务,其中id参数表明惟一标识通知的整型数,须要注意的是提供给 startForeground() 的整型 ID 不得为 0,而notification是一个状态栏的通知。
stopForeground(boolean removeNotification)
该方法是用来从前台删除服务,此方法传入一个布尔值,指示是否也删除状态栏通知,true为删除。 注意该方法并不会中止服务。 可是,若是在服务正在前台运行时将其中止,则通知也会被删除。
下面咱们结合一个简单案例来使用以上两个方法,ForegroundService代码以下:
package com.zejian.ipctest.foregroundService; import android.app.Notification; import android.app.Service; import android.content.Intent; import android.graphics.BitmapFactory; import android.os.IBinder; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import com.zejian.ipctest.R; /** * Created by zejian * Time 2016/10/4. * Description:启动前台服务Demo */ public class ForegroundService extends Service { /** * id不可设置为0,不然不能设置为前台service */ private static final int NOTIFICATION_DOWNLOAD_PROGRESS_ID = 0x0001; private boolean isRemove=false;//是否须要移除 /** * Notification */ public void createNotification(){ //使用兼容版本 NotificationCompat.Builder builder=new NotificationCompat.Builder(this); //设置状态栏的通知图标 builder.setSmallIcon(R.mipmap.ic_launcher); //设置通知栏横条的图标 builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.screenflash_logo)); //禁止用户点击删除按钮删除 builder.setAutoCancel(false); //禁止滑动删除 builder.setOngoing(true); //右上角的时间显示 builder.setShowWhen(true); //设置通知栏的标题内容 builder.setContentTitle("I am Foreground Service!!!"); //建立通知 Notification notification = builder.build(); //设置为前台服务 startForeground(NOTIFICATION_DOWNLOAD_PROGRESS_ID,notification); } @Override public int onStartCommand(Intent intent, int flags, int startId) { int i=intent.getExtras().getInt("cmd"); if(i==0){ if(!isRemove) { createNotification(); } isRemove=true; }else { //移除前台服务 if (isRemove) { stopForeground(true); } isRemove=false; } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { //移除前台服务 if (isRemove) { stopForeground(true); } isRemove=false; super.onDestroy(); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
在ForegroundService类中,建立了一个notification的通知,并经过启动Service时传递过来的参数判断是启动前台服务仍是关闭前台服务,最后在onDestroy方法被调用时,也应该移除前台服务。如下是ForegroundActivity的实现:
package com.zejian.ipctest.foregroundService; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import com.zejian.ipctest.R; /** * Created by zejian * Time 2016/10/4. * Description: */ public class ForegroundActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_foreground); Button btnStart= (Button) findViewById(R.id.startForeground); Button btnStop= (Button) findViewById(R.id.stopForeground); final Intent intent = new Intent(this,ForegroundService.class); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { intent.putExtra("cmd",0);//0,开启前台服务,1,关闭前台服务 startService(intent); } }); btnStop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { intent.putExtra("cmd",1);//0,开启前台服务,1,关闭前台服务 startService(intent); } }); } }
代码比较简单,咱们直接运行程序看看结果:
ok~,以上即是有关于Service前台服务的内容,接下来再聊聊服务与线程的区别
二者概念的迥异
Thread 是程序执行的最小单元,它是分配CPU的基本单位,android系统中UI线程也是线程的一种,固然Thread还能够用于执行一些耗时异步的操做。
Service是Android的一种机制,服务是运行在主线程上的,它是由系统进程托管。它与其余组件之间的通讯相似于client和server,是一种轻量级的IPC通讯,这种通讯的载体是binder,它是在linux层交换信息的一种IPC,而所谓的Service后台任务只不过是指没有UI的组件罢了。
二者的执行任务迥异
在android系统中,线程通常指的是工做线程(即后台线程),而主线程是一种特殊的工做线程,它负责将事件分派给相应的用户界面小工具,如绘图事件及事件响应,所以为了保证应用 UI 的响应能力主线程上不可执行耗时操做。若是执行的操做不能很快完成,则应确保它们在单独的工做线程执行。
Service 则是android系统中的组件,通常状况下它运行于主线程中,所以在Service中是不能够执行耗时操做的,不然系统会报ANR异常,之因此称Service为后台服务,大部分缘由是它自己没有UI,用户没法感知(固然也能够利用某些手段让用户知道),但若是须要让Service执行耗时任务,可在Service中开启单独线程去执行。
二者使用场景
当要执行耗时的网络或者数据库查询以及其余阻塞UI线程或密集使用CPU的任务时,都应该使用工做线程(Thread),这样才能保证UI线程不被占用而影响用户体验。
在应用程序中,若是须要长时间的在后台运行,并且不须要交互的状况下,使用服务。好比播放音乐,经过Service+Notification方式在后台执行同时在通知栏显示着。
二者的最佳使用方式
在大部分状况下,Thread和Service都会结合着使用,好比下载文件,通常会经过Service在后台执行+Notification在通知栏显示+Thread异步下载,再如应用程序会维持一个Service来从网络中获取推送服务。在Android官方看来也是如此,因此官网提供了一个Thread与Service的结合来方便咱们执行后台耗时任务,它就是IntentService,(若是想更深刻了解IntentService,能够看博主的另外一篇文章:Android 多线程之IntentService 彻底详解),固然 IntentService并不适用于全部的场景,但它的优势是使用方便、代码简洁,不须要咱们建立Service实例并同时也建立线程,某些场景下仍是很是赞的!因为IntentService是单个worker thread,因此任务须要排队,所以不适合大多数的多任务状况。
二者的真正关系
关于Service生命周期方法的执行顺序,前面咱们已分析得差很少了,这里从新给出一张执行的流程图(出自Android官网)
其中左图显示了使用 startService() 所建立的服务的生命周期,右图显示了使用 bindService() 所建立的服务的生命周期。经过图中的生命周期方法,咱们能够监控Service的总体执行过程,包括建立,运行,销毁,关于Service不一样状态下的方法回调在前面的分析中已描述得很清楚,这里就不重复了,下面给出官网对生命周期的原文描述:
服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 相似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放全部剩余资源。例如,音乐播放服务能够在 onCreate() 中建立用于播放音乐的线程,而后在 onDestroy() 中中止该线程。
不管服务是经过 startService() 仍是 bindService() 建立,都会为全部服务调用 onCreate() 和 onDestroy() 方法。
服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。
对于启动服务,有效生命周期与整个生命周期同时结束(即使是在 onStartCommand() 返回以后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在 onUnbind() 返回时结束。
从执行流程图来看,服务的生命周期比 Activity 的生命周期要简单得多。可是,咱们必须密切关注如何建立和销毁服务,由于服务能够在用户没有意识到的状况下运行于后台。管理服务的生命周期(从建立到销毁)有如下两种状况:
启动服务
该服务在其余组件调用 startService() 时建立,而后无限期运行,且必须经过调用 stopSelf() 来自行中止运行。此外,其余组件也能够经过调用 stopService() 来中止服务。服务中止后,系统会将其销毁。
绑定服务
该服务在另外一个组件(客户端)调用 bindService() 时建立。而后,客户端经过 IBinder 接口与服务进行通讯。客户端能够经过调用 unbindService() 关闭链接。多个客户端能够绑定到相同服务,并且当全部绑定所有取消后,系统即会销毁该服务。 (服务没必要自行中止运行)
虽然能够经过以上两种状况管理服务的生命周期,可是咱们还必须考虑另一种状况,也就是启动服务与绑定服务的结合体,也就是说,咱们能够绑定到已经使用 startService() 启动的服务。例如,能够经过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户须要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 能够经过调用 bindService() 绑定到服务。在这种状况下,除非全部客户端均取消绑定,不然 stopService() 或 stopSelf() 不会真正中止服务。所以在这种状况下咱们须要特别注意。
既然有隐式启动,那么就会有显示启动,那就先来了解一下什么是隐式启动和显示启动。
//显示启动 Intent intent = new Intent(this,ForegroundService.class); startService(intent);
final Intent serviceIntent=new Intent(); serviceIntent.setAction("com.android.ForegroundService"); startService(serviceIntent);
存在的意义
若是在同一个应用中,二者均可以用。在不一样应用时,只能用隐式启动。
Android 5.0以上的隐式启动问题
Android 5.0以后google出于安全的角度禁止了隐式声明Intent来启动Service。若是使用隐式启动Service,会出没有指明Intent的错误,以下:
主要缘由咱们能够从源码中找到,这里看看Android 4.4的ContextImpl源码中的validateServiceIntent(Intent service),可知若是启动service的intent的component和package都为空而且版本大于KITKAT的时候只是报出一个警报,告诉开发者隐式声明intent去启动Service是不安全的.
而在android5.0以后呢?咱们这里看的是android6.0的源码以下(sublime text查android各个版本源码就是爽呀!!):
从源码能够看出若是启动service的intent的component和package都为空而且版本大于LOLLIPOP(5.0)的时候,直接抛出异常,该异常与以前隐式启动所报的异常时一致的。那么该如何解决呢?
解决方式
final Intent serviceIntent=new Intent(); serviceIntent.setAction("com.android.ForegroundService"); serviceIntent.setPackage(getPackageName());//设置应用的包名 startService(serviceIntent);
public static Intent getExplicitIntent(Context context, Intent implicitIntent) { // Retrieve all services that can match the given intent PackageManager pm = context.getPackageManager(); List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0); // Make sure only one match was found if (resolveInfo == null || resolveInfo.size() != 1) { return null; } // Get component info and create ComponentName ResolveInfo serviceInfo = resolveInfo.get(0); String packageName = serviceInfo.serviceInfo.packageName; String className = serviceInfo.serviceInfo.name; ComponentName component = new ComponentName(packageName, className); // Create a new intent. Use the old one for extras and such reuse Intent explicitIntent = new Intent(implicitIntent); // Set the component to be explicit explicitIntent.setComponent(component); return explicitIntent; }
调用方式以下:
Intent mIntent=new Intent();//辅助Intent mIntent.setAction("com.android.ForegroundService"); final Intent serviceIntent=new Intent(getExplicitIntent(this,mIntent)); startService(serviceIntent);
到此问题完美解决。
实际上这种作法并不推荐,可是既然谈到了,咱们这里就给出一些实现思路吧。主要分如下3种状况
/** * 返回 START_STICKY或START_REDELIVER_INTENT * @param intent * @param flags * @param startId * @return */ @Override public int onStartCommand(Intent intent, int flags, int startId) { // return super.onStartCommand(intent, flags, startId); return START_STICKY; }
package com.zejian.ipctest.neverKilledService; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.support.annotation.Nullable; /** * Created by zejian * Time 2016/10/4. * Description:用户经过 settings -> Apps -> Running -> Stop 方式杀死Service */ public class ServiceKilledByAppStop extends Service{ private BroadcastReceiver mReceiver; private IntentFilter mIF; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Intent a = new Intent(ServiceKilledByAppStop.this, ServiceKilledByAppStop.class); startService(a); } }; mIF = new IntentFilter(); //自定义action mIF.addAction("com.restart.service"); //注册广播接者 registerReceiver(mReceiver, mIF); } @Override public void onDestroy() { super.onDestroy(); Intent intent = new Intent(); intent.setAction("com.restart.service"); //发送广播 sendBroadcast(intent); unregisterReceiver(mReceiver); } }
ok~,以上即是保证服务在必定场景下不被杀死的解决思路,关于第3种状况,若是有解决方案,请留言哈。好,关于Service的所有介绍就此完结。
主要参考资料:
https://developer.android.com/guide/components/services.html#Notifications
https://developer.android.com/guide/components/processes-and-threads.html
https://developer.android.com/guide/components/bound-services.html#Lifecycle
http://blog.csdn.net/vrix/article/details/45289207 android 开发艺术探索