原文链接:http://www.orlion.ga/674/

一、定义一个服务

创建一个项目ServiceDemo,然后在这个项目中新增一个名为 MyService的类,并让它继承自 Service,完成后的代码如下所示:

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

onBind()方法是Service中唯一的一个抽象方法,目前可以暂时将它忽略掉。既然是定义一个服务,自然应该在服务中去处理一些事情了,那处理事情的逻辑应该写在哪里呢?这时就可以重写 Service中的另外一些方法了,如下所示:

public class MyService extends Service {
    @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();
    }
}

可以看到,这里我们又重写了 onCreate()、onStartCommand()和 onDestroy()这三个方法,它们是每个服务中最常用到的三个方法了。其中 onCreate()方法会在服务创建的时候调用,onStartCommand()方法会在每次服务启动的时候调用,onDestroy()方法会在服务销毁的时候调用。

通常情况下,如果我们希望服务一旦启动就立刻去执行某个动作,就可以将逻辑写在onStartCommand()方法里。而当服务销毁时,我们又应该在 onDestroy()方法中去回收那些不再使用的资源。

另外需要注意,每一个服务都需要在AndroidManifest.xml文件中进行注册才能生效,不知道你有没有发现,这是Android四大组件共有的特点。于是我们还应该修改AndroidManifest.xml文件,代码如下所示:

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    ……
    <service android:name=".MyService" >
    </service>
</application>

二、启动和停止服务

定义好了服务之后,接下来就应该考虑如何去启动以及停止这个服务。启动和停止的方法当然你也不会陌生,主要是借助 Intent来实现的,下面就让我们在 ServiceTest项目中尝试去启动以及停止 MyService这个服务。首先修改 activity_main.xml中的代码,添加两个按钮如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

    <Button
        android:id="@+id/start_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Service" />
    
    <Button
        android:id="@+id/stop_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop Service" />
</LinearLayout>

然后修改 MainActivity中的代码,如下所示:

package ga.orlion.servicedemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener{

	private Button startService;

	private Button stopService;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		startService = (Button) findViewById(R.id.start_service);
		stopService = (Button) findViewById(R.id.stop_service);
		startService.setOnClickListener(this);
		stopService.setOnClickListener(this);
	}

	@Override
	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;
		}
	}
}

可以看到,这里在 onCreate()方法中分别获取到了 Start Service按钮和 Stop Service按钮的实例,并给它们注册了点击事件。然后在 Start Service按钮的点击事件里,我们构建出了一个 Intent对象,并调用 startService()方法来启动 MyService这个服务。在 Stop Serivce按钮的点击事件里, 我们同样构建出了一个Intent对象, 并调用stopService()方法来停止MyService这个服务。startService()和 stopService()方法都是定义在 Context类中的,所以我们在活动里可以直接调用这两个方法。注意,这里完全是由活动来决定服务何时停止的,如果没有点击Stop Service按钮, 服务就会一直处于运行状态。 那服务有没有什么办法让自已停止下来呢?当然可以, 只需要在 MyService 的任何一个位置调用 stopSelf()方法就能让这个服务停止下来了。

那么接下来又有一个问题需要思考了, 我们如何才能证实服务已经成功启动或者停止了呢?最简单的方法就是在 MyService的几个方法中加入打印日志,如下所示:

package ga.orlion.servicedemo;

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

public class MyService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		Log.d("MyService", "onCreate");
	}

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

	@Override
	public void onDestroy() {
		super.onDestroy();
		Log.d("MyService", "onDestroy");
	}

}

三、活动和服务通信

虽然服务是在活动里启动的,但在启动了服务之后,活动与服务基本就没有什么关系了。那么有没有什么办法能让活动和服务的关系更紧密一些呢?例如在活动中指挥服务去干什么,服务就去干什么。当然可以,这就需要借助我们刚刚忽略的 onBind()方法了。比如说目前我们希望在 MyService里提供一个下载功能,然后在活动中可以决定何时开始下载,以及随时查看下载进度。实现这个功能的思路是创建一个专门的 Binder对象来对下载功能进行管理,修改 MyService中的代码,如下所示:

...
public class MyService extends Service {

	private DownloadBinder mBinder = new DownloadBinder();

	class DownloadBinder extends Binder {

		public void startDownload() {
			Log.d("MyService", "start download");
		}

		public int getProgress() {
			Log.d("MyService", "get Progress");
			return 0;
		}
	}
	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	...

}

可以看到,这里我们新建了一个 DownloadBinder类,并让它继承自 Binder,然后在它的内部提供了开始下载以及查看下载进度的方法。当然这只是两个模拟方法,并没有实现真正的功能,我们在这两个方法中分别打印了一行日志。接着,在MyService中创建了 DownloadBinder的实例,然后在 onBind()方法里返回了这个实例,这样 MyService中的工作就全部完成了。下面就要看一看,在活动中如何去调用服务里的这些方法了。首先需要在布局文件里新增两个按钮,修改 activity_main.xml中的代码,如下所示:

    <Button
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Bind Service" />
    
    <Button
        android:id="@+id/unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Unbind Service" />

这两个按钮分别是用于绑定服务和取消绑定服务的, 那到底谁需要去和服务绑定呢?当然就是活动了。 当一个活动和服务绑定了之后, 就可以调用该服务里的 Binder提供的方法了。修改 MainActivity中的代码,如下所示:

package ga.orlion.servicedemo;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener{

	private Button startService;

	private Button stopService;

	private Button bindService;

	private Button unbindService;

	private MyService.DownloadBinder downloadBinder;

	private ServiceConnection connection = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			downloadBinder = (MyService.DownloadBinder) service;
			downloadBinder.startDownload();
			downloadBinder.getProgress();
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {

		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		startService = (Button) findViewById(R.id.start_service);
		stopService = (Button) findViewById(R.id.stop_service);
		startService.setOnClickListener(this);
		stopService.setOnClickListener(this);

		bindService = (Button) findViewById(R.id.bind_service);
		unbindService = (Button) findViewById(R.id.unbind_service);
		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);
			startService(startIntent);
			break;
		case R.id.stop_service:
			Intent stopIntent = new Intent(this , MyService.class);
			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;
		}
	}
}

可以看到,这里我们首先创建了一个 ServiceConnection 的匿名类,在里面重写了onServiceConnected()方法和 onServiceDisconnected()方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。在 onServiceConnected()方法中,我们又通过向下转型得到了 DownloadBinder的实例,有了这个实例,活动和服务之间的关系就变得非常紧密了。现在我们可以在活动中根据具体的场景来调用 DownloadBinder中的任何 public方法,即实现了指挥服务干什么,服务就去干什么的功能。这里仍然只是做了个简单的测试,在onServiceConnected()方法中调用了 DownloadBinder的 startDownload()和 getProgress()方法。

当然,现在活动和服务其实还没进行绑定呢,这个功能是在 Bind Service按钮的点击事件里完成的。可以看到,这里我们仍然是构建出了一个 Intent对象,然后调用 bindService()方法将 MainActivity和 MyService进行绑定。bindService()方法接收三个参数,第一个参数就是刚刚构建出的 Intent对象,第二个参数是前面创建出的 ServiceConnection的实例,第三个参数则是一个标志位,这里传入 BIND_AUTO_CREATE表示在活动和服务进行绑定后自动创建服务。这会使得 MyService中的 onCreate()方法得到执行,但 onStartCommand()方法不会执行。然后如果我们想解除活动和服务之间的绑定该怎么办呢?调用一下 unbindService()方法就可以了,这也是 Unbind Service按钮的点击事件里实现的功能。

四、服务的生命周期

一旦在项目的任何位置调用了 Context的 startService()方法,相应的服务就会启动起来,并回调 onStartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动了之后会一直保持运行状态,直到 stopService()或stopSelf()方法被调用。注意虽然每调用一次 startService()方法,onStartCommand()就会执行

一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次 startService()方法,只需调用一次 stopService()或 stopSelf()方法,服务就会停止下来了。另外,还可以调用 Context的 bindService()来获取一个服务的持久连接,这时就会回调服务中的 onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到 onBind()方法里返回的 IBinder对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。

当调用了 startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了 startService()方法,又调用了 bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据 Android系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用 stopService()和 unbindService()方法,onDestroy()方法才会执行。

 

Android入门(十八)服务的更多相关文章

  1. Android入门(十二)SQLite事务、升级数据库

    原文链接:http://www.orlion.ga/610/ 一.事务 SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可 ...

  2. Android进阶(十八)AndroidAPP开发问题汇总(二)

    Android进阶(十八)AndroidAPP开发问题汇总(二) 端口被占用解决措施: Android使用SimpleAdapter更新ListView里面的Drawable元素: http://ww ...

  3. Android入门(十五)通知

    原文链接:http://www.orlion.ga/663/ 1.通知的基本用法 创建通知的步骤,首先需要一个NotificationManager来对通知进行管理,可以调用Context的getSy ...

  4. Android入门(八):使用RadioGroup 和RadioButton组件建立单选清单

    这一章,我们学习RadioGroup 和RadioButton组件,我们新建一个项目,编码过程与前几章的项目类似. 1.建立字符串资源文件strings.xml: <resources> ...

  5. Android入门(十九)WebView

    原文链接:http://www.orlion.ga/676/ WebView可以在自己的应用程序中嵌入一个浏览器来展示网页. 创建一个项目WebViewDemo,修改activity_main.xml ...

  6. Android入门(十六)调用摄像头相册

    原文链接:http://www.orlion.ga/665/ 一.调用摄像头 创建一个项目ChoosePicDemo,修改activity_main.xml: <LinearLayout xml ...

  7. Android入门(十四)内容提供器-实现跨程序共享实例

    原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的 ...

  8. Android入门(十)SQLite创建升级数据库

    原文链接:http://www.orlion.ga/603/ 一.创建数据库 Android为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper帮助类, 借助这个类就可 ...

  9. [译]Kinect for Windows SDK开发入门(十八):Kinect Interaction交互控件

    本文译自 http://dotneteers.net/blogs/vbandi/archive/2013/03/25/kinect-interactions-with-wpf-part-i-getti ...

随机推荐

  1. Android-项目介绍

    一个.net开发人员 在了解android项目只能凭自己的理解慢慢来了! 新建项目 右击 New-JAVA Application Project 傻瓜似的下一步骤填写每一步 文件介绍 Android ...

  2. 用edtftpj实现Java FTP客户端工具

    edtftpj是一个java FTP工具包,使用非常方便,感觉比Apache的好用,但Apache更灵活.edtftpj有多种版本,分别是java..net和js版本.对于Java版的有一个免费版本. ...

  3. 整不明白Jquery的问题

    最近写个GridView,由于不是很熟悉javascript,用jquery实现后,发现下面设置左右表行高的代码如果不把行保存在$tr1,$tr2中再设置css,速度非常慢,300行50列左右的数据得 ...

  4. L9-1-安装Apache

    一.安装Apache服务器: 安装apr [root@OCP soft]# tar -zxf apr-1.5.1.tar.gz [root@OCP apr-1.5.1]# ./configure -- ...

  5. 20170506计划-----(基于python查询oracle语句)

    在日常的工作中,经常接到开发同事查询生产SQL的请求,公司又不允许对开发开放查询SQL的权限,并且查询的堡垒机又很慢,计划做一个可以自动查询SQL的小工具,一周内完成吧. 大概功能实现了,一些涉及敏感 ...

  6. python property的用法

    用法一: class Test(object): def __init__(self): # 私有化 self.__num = 100 #名字重整_Test__num def setNum(self, ...

  7. Linux打开TCP BBR拥塞控制算法

    要求内核为4.9以上,如果不是,请升级内核. modprobe tcp_bbr echo "tcp_bbr" >> /etc/modules-load.d/module ...

  8. IONIC 页面之间传递参数

    HomePage 定义goToMyPage方法,传递id和name MyPage接收参数

  9. [转]SQL Server如何启用xp_cmdshell组件

    本文转自:https://www.cnblogs.com/atree/p/SQL_SERVER_xp_cmdshell.html 错误描述]: SQL Server阻止了对组件‘xp_cmdshell ...

  10. Jmeter零起点学习

    什么是JMeter   Apache JMeter是一个开源的Java桌面软件.设计的目的就是进行C/S架构软件的负载测试.随着发展,有很多人也用来进行一些静态资源或者动态资源的性能测试.可以支持的测 ...