原文链接: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. 通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存

    很久没有更新dapr系列了.今天带来的是一个小的组件集成,通过多级缓存框架来实现对服务的缓存保护,依旧是一个简易的演示以及对其设计原理思路的讲解,欢迎大家转发留言和star 目录:一.通过Dapr实现 ...

  4. Android入门(十五)通知

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

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

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

  6. Android入门(十九)WebView

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

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

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

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

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

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

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

随机推荐

  1. 大数据组件原理总结-Hadoop、Hbase、Kafka、Zookeeper、Spark

    Hadoop原理 分为HDFS与Yarn两个部分.HDFS有Namenode和Datanode两个部分.每个节点占用一个电脑.Datanode定时向Namenode发送心跳包,心跳包中包含Datano ...

  2. TCL:读取表格(xls)中的数据

    intToChar.tcl # input a number : 1 to 32 , you will get a char A to Z #A-Z:1-32 proc intToChar {int} ...

  3. 浅谈Swift集合类型

    Swift 的集合表现形式由数组和字典组成.它可以完美的存储任何呢想存储的东西. 数组是一个同类型的序列化列表集合,它用来存储相同类型的不同值.字典也是一个数组,但它的存值方式类似于Map,通过一对一 ...

  4. js学习笔记之一

    一.Javascript 中的对象 1. 建立自定义对象 方法1:对象={属性1:属性值1,属性2:属性值2……属性n:属性值n} 方法2:先定义构造函数,再new创建对象实例. 如: functio ...

  5. Happy Programming Contest(ZOJ3703)(01背包+路径储存)

    Happy Programming Contest  ZOJ3703 老实说:题目意思没看懂...(希望路过的大神指点) 最后那个the total penalty time是什么意思啊!!! 还是学 ...

  6. caffe 在window下编译(windows7, cuda8.0,matlab接口编译)

    1. 环境:Windows7,Cuda8.0,显卡GTX1080,Matlab2016a,VS2013 (ps:老板说服务器要装windows系统,没办法,又要折腾一番,在VS下编译好像在cuda8. ...

  7. php学习笔记2016.1

    基本类型    PHP是一种弱类型语言.      PHP类型检查函数   is_bool()    is_integer()  is_double()  is_string()   is_objec ...

  8. nginx.conf配置(支持thinkphp)

    error_log /home/wwwlogs/nginx_error.log crit; pid /usr/local/nginx/logs/nginx.pid; #Specifies the va ...

  9. pthread——pthread_cleanup

    Pthread_cleanup用于注册线程清理函数,注册的清理函数将在线程被取消或者主动调用pthread_exit时被调用:     一个简单的示例: #include <pthread.h& ...

  10. Redis中Value使用hash类型的效率是普通String的两倍

    什么Redis? 点击这里 最近要开发的一个项目是分布式缓存组件,解决参数缓存高效获取的问题.参数达到了500万级别,刚刚开始了解Redis.做设计的时候考虑到Value使用哪种类型的问题? 主要面临 ...