【android学习】四大组件-Service(服务)

1,概念

Service是android系统中的四大组件之一,它是一种长生命周期的、没有可视化界面、运行于后台的一种服务程序。主要负责更新数据源和可见的Activity,以及触发通知。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行

与activity、application一样继承了Context抽象类。
Service本身就是Context的实现,所以只需要调用this.

2,场景

1)不依赖于用户可视的UI界面(前台Service除外)

2)具有较长时间的运行的特性。(心跳连接、音乐播放、数据下载)

服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

3)如何保证service不被杀死

进程拉活

3,分类

1)启动

不管哪种启动方式,都需要在xml里注册Service:

<service
        android:name=".packnameName.youServiceName"
        android:enabled="true" />

①启动状态(Started Service)

通过public ComponentName startService(Intent service)方法去启动一个Service,此时Service的生命周期与启动它的Context无关。

开启Service,调用者退出后Service仍然存在,需手动调用才可以停止服务。
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。
已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。

②绑定状态(Bound Service)

通过public boolean bindService(Intent service,ServiceConnection conn,int flags) 方法来进行Service与Context的关联并启动,并且Service的生命周期依附于Context。
开启Service,调用者退出后Service也随即退出。
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

通过bindService的方式,可是实现Activity如与Service通信:
先在Activity里实现一个ServiceConnection接口,并将该接口传递给bindService()方法,在ServiceConnection接口的onServiceConnected()方法里执行相关操作。

2)process

通过在AndroidManifest.xml中可以申明android:process是否remote Service。

①Local Service

对应的Service运行在主进程的main线程上的。如:onCreate,onStart这些函数在被系统调用的时候都是在主进程的main线程上运行的。

②Remote Service

对应的Service运行在独立进程的main线程上。单独在进程中运行。

3,实现

1)在AndroidManifest.xml中声明Service

①原因

为了在使用Service时,系统能够找到此自定义Service。

②属性

<service android:enabled=["true" | "false"] //是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活
    android:exported=["true" | "false"]//代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。
    android:icon="drawable resource"
    android:isolatedProcess=["true" | "false"]//设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
    android:label="string resource"
    android:name="string"//对应Sevice类么:包名.服务类名
    android:permission="string"//是权限声明
    android:process="string" >//是否需要在单独的进程中运行,当设置为android:process=":remote"时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
    . . .
</service>

③如果未申明,也不会让程序崩溃,会报出警告。

2)新建一个继承Service的类

①启动状态

public class HttpService extends Service{  

    /** 
     * Service唯一抽象方法 
     * 返回值:针对Bound Service类型的Service才有用,此处直接返回null即可
     */  
    @Override  
    public IBinder onBind(Intent intent) {            
        return null;  
    }  
    /**
     * 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。
     * 如果服务已在运行,则不会调用此方法。该方法只被调用一次
     */
    @Override  
    public void onCreate(){  
        super.onCreate();  
    }  
     /**
     * 每次通过startService()方法启动Service时都会被回调。
     */
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        String name = intent.getStringExtra("name");  
        return START_STICKY;  
    }  
    /**
     * 服务销毁时的回调
     */
    @Override  
    public void onDestroy() {  
        super.onDestroy();   
    }     
}

3)调用

private Intent serviceIntent;   
serviceIntent = new Intent(this, HttpService.class);  
             startService(serviceIntent);  
             stopService(serviceIntent);

4,生命周期

1)方法

生命周期

①onCreate()

首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。如果服务已在运行,则不会调用此方法,该方法只调用一次。

②onStart()和onStartCommand()

onStart已经deprecated,在Android2.0以下版本使用,建议使用onStartCommand替代。
onStart和onStartCommand两个方法放在一起使用时,不会产生冲突。

当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。(在绑定状态下,无需实现此方法。)

onStartCommand(Intent intent, int flags, int startId)参数

--intent 

--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 

--返回值(int)
    ----START_STICKY
    如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
    ----START_NOT_STICKY    “非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
    ----TART_REDELIVER_INTENT    Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
    ----START_STICKY_COMPATIBILITY    START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

③onDestroy()

当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
在服务的外部,必须使用stopService()方法停止,在服务的内部可以调用stopSelf()方法停止当前服务。

④onBind()

当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。

2)调用次序

两种启动类型,都是通过继承Service基类自定义而来。

①Started Service生命周期

开启Service,调用者退出后Service仍然存在。
context.startService() –> onCreate() –> onStart() (可多次调用) –> Service running
context.stopService() –> onDestroy() –> Service stop

i>创建:

开启的service通过其他组件调用 context.startService(Intent serviceIntent)。

ii>停止:

这种service可以无限地运行下去,必须调用stopSelf()方法(在Service内部)或者其他组件调用context.stopService(Intent serviceIntent)方法来停止它。

iii>销毁:

当service被停止时,系统会销毁它

②Bound Service生命周期

无法设置Service粘性。开启Service,调用者退出后Service也随即退出。

bindService()方法执行成功同时onBind()方法返回非空IBinder对象,触发ServiceConnection接口的onServiceConnected()方法。
context.bindService() –> onCreate() –> onBind() –> Service running
onUnbind() –> onDestroy() –> Service stop

i>创建:

被绑定的service是当其他组件(一个客户)调用bindService()来创建的。

ii>通信:

客户可以通过一个IBinder接口和service进行通信。

iii>关闭连接:

客户可以通过 unbindService()方法来关闭这种连接。

iv>销毁:

一个service可以同时和多个客户绑定,当多个客户都解除绑定之后,系统会销毁service。

③同时使用startService()启动服务与bindService()绑定服务

onCreate() -> onStartCommnad() -> onBind() -> onUnBind() -> onDestory

6,Service与Thread区别

首先,一定要说的是,两者没有半毛线关心。

1)概念上的不同

①Thread 是程序执行的最小单元,它是分配CPU的基本单位,android系统中UI线程也是线程的一种,当然Thread还可以用于执行一些耗时异步的操作。
②Service是Android的一种机制,服务是运行在主线程上的,它是由系统进程托管。它与其他组件之间的通信类似于client和server,是一种轻量级的IPC通信,这种通信的载体是binder,它是在linux层交换信息的一种IPC,而所谓的Service后台任务只不过是指没有UI的组件罢了。

2)与主线程关系不同

在android系统中,线程一般指的是工作线程(即后台线程),而主线程是一种特殊的工作线程,它负责将事件分派给相应的用户界面小工具,如绘图事件及事件响应,因此为了保证应用 UI 的响应能力主线程上不可执行耗时操作。如果执行的操作不能很快完成,则应确保它们在单独的工作线程执行。

Service 则是android系统中的组件,一般情况下它运行于主线程中,因此在Service中是不可以执行耗时操作的,否则系统会报ANR异常,之所以称Service为后台服务,大部分原因是它本身没有UI,用户无法感知(当然也可以利用某些手段让用户知道),但如果需要让Service执行耗时任务,可在Service中开启单独线程去执行。
所以在使用中,需要在Service中再创建一个子线程,然后在这里去处理耗时逻辑。

3)生命周期不同。

Service用于后台操作。
Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。
使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

4)使用场景选择

当要执行耗时的网络或者数据库查询以及其他阻塞UI线程或密集使用CPU的任务时,都应该使用工作线程(Thread),这样才能保证UI线程不被占用而影响用户体验。

在应用程序中,如果需要长时间的在后台运行,而且不需要交互的情况下,使用服务。比如播放音乐,通过Service+Notification方式在后台执行同时在通知栏显示着。

5)为什么在Service中创建子线程而不是Activity中

这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

7,前台Service

1)概念

Service的系统优先级比较低,当系统出现内存不足情况时,就有可能会回收掉正在后台运行的Service。
如果要Service一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台Service。

2)与普通Service区别

前台Service会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。当然有时候你也可能不仅仅是为了防止Service被回收才使用前台Service,有些项目由于特殊的需求会要求必须使用前台Service,比如说墨迹天气,它的Service在后台更新天气数据的同时,还会在系统状态栏一直显示当前天气的信息。

3)方法

---startForeground(int id, Notification notification) 
该方法的作用是把当前服务设置为前台服务,其中id参数代表唯一标识通知的整型数,需要注意的是提供给 startForeground() 的整型 ID 不得为 0,而notification是一个状态栏的通知。

---stopForeground(boolean removeNotification) 
该方法是用来从前台删除服务,此方法传入一个布尔值,指示是否也删除状态栏通知,true为删除。 注意该方法并不会停止服务。 但是,如果在服务正在前台运行时将其停止,则通知也会被删除。

4)实现

在ForegroundService类中,创建了一个notification的通知,并通过启动Service时传递过来的参数判断是启动前台服务还是关闭前台服务,最后在onDestroy方法被调用时,也应该移除前台服务。

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;
    }
}
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);
            }
        });
    }
}

8,IntentService

1)概念

IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题。

底层用到了线程。内部采用HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出。

IntentService是一种特殊的Service,是继承了Service的抽象类。

2)优点

①不需要自己去new Thread
②不需要考虑在什么时候关闭该Service
③IntentService是一种服务,不轻易被系统杀死。而HandlerThread是一个后台进程,优先级很低,容易被系统杀死。

3)场景

IntentService用于执行后台耗时任务,当任务执行后会自动停止。
适合执行优先级高的后台任务。

节制的使用Service,当启动一个Service时,系统总是倾向于保留这个Service依赖的进程,这样会造成系统资源的浪费,可以使用IntentService,执行完成任务后会自动停止。

4)方法

①onHandleIntent()

在子线程的handler中回调。

猜你喜欢

转载自blog.csdn.net/SunshineTan/article/details/77774417