Android基础——Service介绍

本文讲到一下几点:
1,Service的基本概念;

2,Service的生命周期;

3,进程生命周期;





Service概述:

Service是Android中非常重要的组件之一。它和Activity很相似,都代表可执行的程序。但是他们的区别在于,Service一直运行在后台,它没有用户界面,无法与用户交互。Service启动后,和Activity一样有自己的生命周期。使用Service的方法和Activity也相似,每创建一个Service的子类就需要在Manifest.xml文件中有对其的声明(<service>标签),当然它也能够使用<intent-filter>标签设置intent的过滤条件。


需要注意的是:Service和其他应用组件一样运行在它宿主的主线程中(也叫UI线程)。当我们要在Service中执行耗时的操作时,我们应该将这些操作放在另外的线程中。


关于Service我们需要明确几点:
1,Service组件并不是运行在自己的进程中,除非我们另外指定,它运行在和应用相同的进程中;
2,一个Service开启并不是说它就开启了一个线程,它没有脱离主线程(但是我们通常在做耗时操作时这样做,为了防止发生Android Not Responsing);


Service实际上是一个很简便的组件,提供了一些很重要的特性:
1,使用Service,相当于告诉系统我们要做一些后台的操作,这些操作不需要和用户直接进行交互。
2,Service也提供了和其他应用程序通信的途径




Service的生命周期:

Service可以有两种方式启动,通过startService()或者bindService()。不同的启动方式他们的生命周期也有一些不同。通过startService()启动时生命周期如左图(这个时候会调用onStartCommand()方法,如果希望Service组件做些什么事情的话可以在onCreate()、onStartCommand()中定义相关的业务代码就可以了),通过bindService()启动时生命周期如右图(这种方式提供了Service和本地通信/和其他应用程序通信的途径,以后会涉及到)。


图中的onStart方法就是onStartCommand()方法。


   startService()启动指定的Service:第一次启动Service时会调用onCreate()方法,接着调用onStartCommand(),这时service开始运行,直到调用stopService()或者stopSelf()方法。这里如果多次的调用startService()来启动service,并不会重新调用onCreate()方法多次,但是onStartCommand()方法会调用多次,同样也只需要一次stopService()或者stopSelf()就可以停止Service。这种方式启动的Service时,根据onStartCommand()的返回值不同,有着不同的工作模式:

START_STICKY:表示Service在任何时候都是明确的开启和停止,例如:在后台播放音乐;(如果这个Service的进程在onStartCommand()返回后被杀死,这个Service会停留在启动状态,但是系统并不会保留那个Intent,而是会在稍后的时间内尝试重新创建这个Service,并且会调用onStartCommand。如果没有任何启动的命令传递到这个Service,那么就会传递一个为null的Intent。)

START_NOT_STICKY:表示这个Service不需要一直保持运行状态,只是在接收到运行命令后才开始工作,例如:数据库的查询;(如果这个Service的进程在onStartCommand()返回后被杀死,并且没有新的启动Intent发送过来,这时Service就会离开启动状态,系统不会重新创建它,直到有明确的启动操作startService()出现。)

START_REDELIVER_INTENT:和START_NOT_STICKY类似。(如果这个Service的进程在onStartCommand()返回后被杀死,它会重新启动并且最后一次通过onStartCommand()传递过来的Intent会被重新传递过来,并且这个Intent会一直重传直到服务被stop)

    

    bindService()启动Service:可以建立客户端和Service的连接,但是不会调用 onStartCommand()方法。客户端会接收到一个onBind()方法返回的IBunder对象,这样就允许客户端回调到Service。Service会一直保持运状态,只要这个连接没有断。(通常这个IBinder会比较复杂,在实现进程间通信的时候,它可以是一个AIDL的接口)这些知识会在后面涉及到,这里不做详解。



Service生命周期还有一种较特殊的情形:如果Service已经由一个Activity通过startService()启动,接下来其他Activity又通过bindService()来绑定该Service,再调用unbindService()来解除绑定,最后又调用bindService()再次绑定它,这个时候出发的生命周期是:

onCreate()——>onStartCommand()——>onBind()——>onUnBind()[重写该方法时返回true]——>onReBind()

可看到Service的onReBind()方法被回调,这是不仅需要Service是由startService()启动的,还需要重写onUnBind()时返回true。


例子:

布局只有简单的四个按钮:

Activity作为启动Service的客户端:

package com.flyingduck.servicedemo;

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.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class ClientActivity extends Activity {

	private Button startBtn;
	private Button bindBtn;
	private Button unBindBtn;
	private Button stopBtn;

	// bindServier()用到
	private ServiceConnection conn;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_client);

		startBtn = (Button) findViewById(R.id.start_service);
		bindBtn = (Button) findViewById(R.id.bind_service);
		unBindBtn = (Button) findViewById(R.id.unbind_service);
		stopBtn = (Button) findViewById(R.id.stop_service);

		final Intent intent = new Intent(getApplicationContext(),
				ServiceDemo.class);

		conn = new ServiceConnection() {
			@Override
			public void onServiceDisconnected(ComponentName arg0) {
			}

			@Override
			public void onServiceConnected(ComponentName arg0, IBinder arg1) {
			}
		};

		// 通过startService()启动Service
		startBtn.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View view) {
				startService(intent);
			}
		});
		// 通过bindService()绑定service
		bindBtn.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View view) {
				bindService(intent, conn, Service.BIND_AUTO_CREATE);
			}
		});
		// 解除绑定
		unBindBtn.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				unbindService(conn);
			}
		});
		// 停止service
		stopBtn.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				stopService(intent);
			}
		});

	}

}


service:

package com.flyingduck.servicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class ServiceDemo extends Service {
	private static String TAG = "service demo";

	@Override
	public IBinder onBind(Intent intent) {
		Log.i(TAG, "onBind...!");
		return null;
	}

	@Override
	public void onCreate() {
		Log.i(TAG, "onCreate...!");
		
		super.onCreate();
	}

	@Override
	public void onDestroy() {
		Log.i(TAG, "onDestroy...!");
		super.onDestroy();
	}

	@Override
	public void onRebind(Intent intent) {
		Log.i(TAG, "onRebind...!");
		super.onRebind(intent);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, "onStartCommand...!");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public boolean onUnbind(Intent intent) {
		Log.i(TAG, "onUnbind...!");
		return true;
	}

}

Manifest.xml千万不能忘记配置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.flyingduck.servicedemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.flyingduck.servicedemo.ClientActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.flyingduck.servicedemo.ServiceDemo" >
        </service>
    </application>

</manifest>

运行截图:


依次点击:StartService、BindService、UnBindService、BindService按钮:

结果如下:



这种情形下并不会回调Service的OnDestroy()方法,这是因为该方法并不是由Activity通过bindService()来启动的(是由startService()启动的),因此即使调用Service的unBindService()方法解除与Service的绑定,该Service也不会终止。

由此可见,当Activity调用bindService()绑定一个已经启动的Service时,系统只是把Service内部的IBinder对象传给了Activity,并不会把该Service生命周期完全绑定到Activity。因此,在调用unBindService()取消与该Service的绑定时,也只是切断了Activity与Service的联系,并不能停止Service组件。


进程生命周期

只要Service被启动或者被绑定到宿主进程上,Android系统就会让这个进程一直持有这个Service。当系统内存不足或者其他情况需要杀死一些存在的进程时,持有Service的进程在一下这些情况下会有较高的优先级:

1,当service正在执行声明周期中方法(onCreate()、onStartCommand()、onDestroy()),宿主进程会成为前台进程来保证service正常的执行;

2,当Service启动后,宿主进程会的优先级别会 < 当前正在和用户交互的进程,>其他不可见的进程;

3,如果有很多客户端绑定到了service,那么这个service的宿主进程的有限级别高于优先级最高的那个客户端。也就是说,如果客户端中的一个是对用户可见的,那么这个Service可以被看作是可见的;

4,当Service通过startForeground()启动,进入前台状态,那么在系统内存低的情况下,不会被杀死。

在Service运行的大部分时候,系统都可能在内存不足的情况下杀死service。Service被杀死后系统会在之后尝试重新启动这个Service。所以,在我们重写onStartCommand()去做一些事情,并且我们是在异步操作或者在一个新的线程中去做这些事情的话,我们做好使用START_FLAG_REDELIVERY让系统重传一个intent,以免造成不必要的损失。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值