Android入门(十八)服务

原文链接: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()方法才会执行。

时间: 2024-10-13 01:36:13

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

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

原文链接:http://www.orlion.ga/610/ 一.事务 SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可以先使用delete()方法将Book表中的数据删除, 然后再使用insert()方法将新的数据添加到表中.我们要保证的是,删除旧数据和添加新数据的操作必须一起完成,否则就还要继续保留原来的旧数据.                 Button replaceData = (Button) find

[WebGL入门]十八,利用索引缓存来绘图

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正. 本次的demo的运行结果 对应复杂的模型 上次,通过操作模型坐标变换矩阵,实现了多个模型的移动,旋转和放大缩小.但是,渲染的依然是简单的三角形,是个构造及其简单的模型.但是实际中,用WebGL来绘制一个简单的三角形的机会是很少见的.至少是个四角多边形吧,通常会是更复杂的模型.伴随着模型的复杂化

android学习十八(Service服务的基本用法)

定义一个服务 在项目中定义一个服务,新建一个ServiceTest项目,然后在这个项目中新增一个名为MyService的类,并让它继承自Service,完成后的代码如下所示: package com.jack.servicetest; import android.app.Service; import android.content.Intent; import android.os.IBinder; public class MyService extends Service { @Over

Android入门(十五)通知

原文链接:http://www.orlion.ga/663/ 1.通知的基本用法 创建通知的步骤,首先需要一个NotificationManager来对通知进行管理,可以调用Context的getSystemService()方法获取到.getSystemService()方法接收到一个字符串参数用于确定获取系统的哪个服务,这里我们传入Context.NOTIFICATION_SERVICE即可,因此获取NotificationManager的实例就可以写成: NotificationManag

Android第十八期 - 智能机器人

这回比较复杂,所以就一步步的教学弟们怎么写出来一个可以和你聊天的智能机器人Android版. 首先我们找一个第三方的api接口,我选择的是图灵机器人,地址是http://www.tuling123.com/openapi/,  如图:     然后是找到api文档接入口:     然后是json的解析部分: 然后我们挑一个做一个demo - 一问一答的例子: 那么调用的方式url = http://www.tuling123.com/openapi/api?key=ad5f0729523118c

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

原文链接:http://www.orlion.ga/603/ 一.创建数据库 Android为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper帮助类, 借助这个类就可以非常简单地对数据库进行创建和升级. SQLiteOpenHelper是一个抽象类,其中有两个抽象方法分别是onCreate()和onUpgrade(),分别在这两个方法中实现创建和升级数据库的逻辑 SQLiteOpenHelper还有两个十分重要的实例方法,getReadableDatabase(

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

原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的提示去除掉,因为跨程序访问时我们不能直接使用 Toast.然后添加一个 DatabaseProvider类,代码如下所示: package ga.orlion.databasedemo; import android.content.ContentProvider; import android.c

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

原文链接:http://www.orlion.ga/665/ 一.调用摄像头 创建一个项目ChoosePicDemo,修改activity_main.xml: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent&qu

Android(十八)横竖屏的切换

1.强制横竖屏. 这就需要通过在AndroidManifest.xml中设置activity中的android:screenOrientation属性值来实现. 该android:screenOrientation属性,他有以下几个参数: "unspecified":默认值 由系统来判断显示方向.判定的策略是和设备相关的,所以不同的设备会有不同的显示方向. "landscape":横屏显示(宽比高要长) "portrait":竖屏显示(高比宽要长