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()方法才会执行。
Android入门(十八)服务的更多相关文章
- Android入门(十二)SQLite事务、升级数据库
原文链接:http://www.orlion.ga/610/ 一.事务 SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可 ...
- Android进阶(十八)AndroidAPP开发问题汇总(二)
Android进阶(十八)AndroidAPP开发问题汇总(二) 端口被占用解决措施: Android使用SimpleAdapter更新ListView里面的Drawable元素: http://ww ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存
很久没有更新dapr系列了.今天带来的是一个小的组件集成,通过多级缓存框架来实现对服务的缓存保护,依旧是一个简易的演示以及对其设计原理思路的讲解,欢迎大家转发留言和star 目录:一.通过Dapr实现 ...
- Android入门(十五)通知
原文链接:http://www.orlion.ga/663/ 1.通知的基本用法 创建通知的步骤,首先需要一个NotificationManager来对通知进行管理,可以调用Context的getSy ...
- Android入门(八):使用RadioGroup 和RadioButton组件建立单选清单
这一章,我们学习RadioGroup 和RadioButton组件,我们新建一个项目,编码过程与前几章的项目类似. 1.建立字符串资源文件strings.xml: <resources> ...
- Android入门(十九)WebView
原文链接:http://www.orlion.ga/676/ WebView可以在自己的应用程序中嵌入一个浏览器来展示网页. 创建一个项目WebViewDemo,修改activity_main.xml ...
- Android入门(十六)调用摄像头相册
原文链接:http://www.orlion.ga/665/ 一.调用摄像头 创建一个项目ChoosePicDemo,修改activity_main.xml: <LinearLayout xml ...
- Android入门(十四)内容提供器-实现跨程序共享实例
原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的 ...
- Android入门(十)SQLite创建升级数据库
原文链接:http://www.orlion.ga/603/ 一.创建数据库 Android为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper帮助类, 借助这个类就可 ...
随机推荐
- Eclipse配置tomcat后,启动tomcat,访问tomcat报404错误
当你在Eclipse中新建一个工程,配置好tomcat,然后测试tomcat是否配置成功的时候,报404错误异常. 解决方法: 1,把工程文件删除,重新建立一个新的工程, 2,新建一个工程. 3,Ne ...
- DSO动态加载PHP模块到Apache服务器
PHP在Linux/Unix平台上经常与Apache搭配使用,在安装PHP时,有三种安装方式可供选择:静态模式.动态模式(DSO).CGI二进制模式. 由于易于维护和升级,我强烈建议以DSO方式安装P ...
- Swift语言的基础详解
1.1.关于swift · Swift 是一种适用于 iOS 和 OS X 应用的全新编程语言,它建立在最好的 C 和 Objective-C 语言之上,并且没有 C 语言的兼容性限制.Swift 采 ...
- 我的ORM之十三 -- 性能参数
我的ORM索引 测试环境 台式机: 主板:映泰Z77 CPU:i5 3470(3.2GHz) 内存:DDR3 1600 8G(单条) 硬盘:创见 SSD 256G ORM从过程上,可以分两个大的部分: ...
- Web Essentials之样式表StyleSheets
返回Web Essentials功能目录 本篇目录 智能感知 视觉提示 验证 Web标准 转换器 Web Essentials中大多数的CSS功能也适用于LESS. 智能感知 生成供应商特定的属性 如 ...
- 【读书笔记】WebApi 和 SPA(单页应用)--knockout的使用
Web API从MVC4开始出现,可以服务于Asp.Net下的任何web应用,本文将介绍Web api在单页应用中的使用.什么是单页应用?Single-Page Application最常用的定义:一 ...
- Visual Studio 2012 Visual C++ 入门
改进的C++11标准的支持 标准模板库 为STL添加了新的库文件:<atomic>.<chrono>.<condition_variable>.<filesy ...
- HTML目录生成工具
目录 内容简介 工具代码 使用方法 工具改进 内容简介 园子里面很多博主都会为自己的博文创建目录,方便大家浏览.我很好奇大家是怎么做的,是不是有自动生成目录的工具可以推荐一下(我知道word可以,但是 ...
- 熟用js中的Date
js中的Date类型是使用UTC(国际协调时间)自1970年1月1日午夜(零时)开始,经过的毫秒数来保存日期. 1. 创建日期对象 ---> 获得当前日期和时间 var now = new ...
- 细说.NET中的多线程 (二 线程池)
上一章我们了解到,由于线程的创建,销毁都是需要耗费大量资源和时间的,开发者应该非常节约的使用线程资源.最好的办法是使用线程池,线程池能够避免当前进行中大量的线程导致操作系统不停的进行线程切换,当线程数 ...