【转】 Pro Android学习笔记(七七):服务(2):Local Service
- Local service代码
- 调用Local ServiceLocal Service client代码
- AndroidManifestxml定义Serviceacitivty的launchMode
- 一些思考
文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处:http://blog.csdn.net/flowingflying/
Local Service的目的是更容易实现后台任务。下面是一个简单的小例子,通过一个activity请求服务和关闭服务。实际上应用中任何的component都可以使用服务,如activity,service,普通的java类。
Client是一个activity,UI很简单,由两个button组成,按“Start Service”将触发Local Service,按“Stop Service”将强制终止Local Service。Local Service创建后,会在通知栏显示信息,Service被终止时,通知栏信息消失,我们可以据此观察local service是否正在运行。下拉通知栏,我们可以看到具体的通知信息。点击通知,通常会打开某个相关的activity,本例打开client。Local Service每收到请求(通过intent来传递),都会开启一个线程执行相应的任务,本例的任务采用Sleep()来模拟。
Local service代码
在给出代码之前,先看看执行的流程,以更好地了解local service的生命周期。
//【1】自定义的LocalBackgroundService是android.app.Service的继承
public class LocalBackgroundService extends Service{
private static final String TAG = "LocalBackgroundService";
public static final String COUNTER_STR = "counter";
private NotificationManager notifyMgr = null;
private ThreadGroup myThreads = new ThreadGroup(TAG);
private static final int MY_NOFIGY_ID = 100;
@Override //【2】Service创建时回调函数,用于service的初始化,本例将在通知栏中给出信息
public void onCreate() {
super.onCreate();
Log.v(TAG,"onCreate() is called. service is " + this.toString());
//在通知栏显示信息,相关代码也可参考Android学习笔记(五四):通知Notification(上)。service通常(包括本例)会在后台开启线程,在无UI情况下运行,如果用户需要干预运行,例如打开邮件,又例如关闭音乐播放,可通过通知的方式,用户点击通知,创建某activity来实现。
notifyMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
displayNotificationMessage("Backgound service is running...");
}
@Override //【3】当收到client的intent时,触发onStartCommand()。 每次client的请求都有一个对应的startId,对应的可以用stopSelfResult(int startId),在实际应用中,可以利用startId进行一一识别和数据保存及对应,当然在本例中也可以用intent传递过来的counter作为识别码。当一个请求触发了onStartCommand(),正在运行之际,第二个请求来了,会在队列中,等待前面请求的代码运行完。本例在onStartCommand()中开启后台线程,可以触发产生多个后台线程。
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
//【3.1】通过intent读取client传递的信息,本例简单,仅进行验证
int counter = intent.getExtras().getInt(COUNTER_STR);
Log.v(TAG,"onStartCommand() is called. counter = " + counter + " startId = " + startId);
//【3.2】Service运行在主线程,如果我们要进行后台处理,必须创建自己的后台线程,在本例中,我们使用了使用java.lang.Thread.Thread(ThreadGroup group, Runnable runnable, String threadName) ,采用ThreadGroup方式,是为了在Service结束,即onDestroy()中通过group.interrupt(),终止所有正在运行的线程。
new Thread(myThreads,new ServiceWorker(counter),TAG+counter).start();
return START_STICKY;
}
//【3.3】线程代码,本例简单地sleep十秒
private class ServiceWorker implements Runnable{
private int counter = -1;
public ServiceWorker(int counter){
this.counter = counter;
}
@Override
public void run() {
final String tag = "ServiceWorker-" + Thread.currentThread().getId();
try{
Log.v(tag,"sleep after 10s, counter = " + counter);
Thread.sleep(10000);
Log.v(tag,"waking up, counter = " + counter);
}catch (InterruptedException e){ //当线程被中断,出现InterruptedException异常
Log.v(tag,"sleep Interrupted... counter = " + counter);
}catch (Exception ex){
Log.e(tag,ex.toString());
}
}
}
@Override //【4】在Service结束时调用,进行cleanup。根据本例设计,此前要终止所有线程,清除通知栏的显示。在实际运行中,我们可能不会这样简单粗暴的中断,例如正在发送的邮件,我们仍希望将邮件成功发送出去。
public void onDestroy() {
Log.v(TAG,"onDestroy() is called... Interrupt threads and cannceling notifications");
myThreads.interrupt(); //对于Thread发送interrupt请求,本例是在sleep,因此会抛出InterruptedExcetpion异常,如果在进行I/O处理,会收到ClosedByInterruptException异常,
notifyMgr.cancelAll(); //清除所有的通知栏显示
super.onDestroy();
}
@Override //【1】作为Local Service,client通过startService()来进行触发,不需要通过bindService(),但是onBind()是Service类的abstract函数必须重写,看简单地return null即可。
public IBinder onBind(Intent arg0) {
Log.v(TAG,"onBind() is called.");
return null;
}
//【2】相关代码的理解,可以参考Android学习笔记(五四):通知Notification(上),本例采用Notification.Builder().build(),要求sdk的最小版本是16
private void displayNotificationMessage(String message){
PendingIntent contentIntent = PendingIntent.getActivity
(this, 0, new Intent(this,LocalServiceClientActivity.class), 0);//点击通知将创建client activity,注意这是一个新的activity,而不是返回原来的activity。
Notification notify = new Notification.Builder(this)
.setSmallIcon(R.drawable.emo_im_winking)
.setTicker(message)
.setContentText(message)
.setContentIntent(contentIntent)
.build();
notify.flags |= Notification.FLAG_NO_CLEAR; //本例,我们希望只要service存在,通知就存在,即不能人手清除该通知的显示,所以设置FLAG_NO_CLEAR。
notifyMgr.notify(MY_NOFIGY_ID,notify);
}
}
调用Local Service:Local Service client代码
public class LocalServiceClientActivity extends Activity{
private static final String TAG="LocalServiceClientActivity";
private int counter = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.localservice_client);
Log.v(TAG,"client: onCreate() is called. client is " + this.toString());
}
@Override
protected void onDestroy() {
// 本例在activity退出时关闭local service,当然在实际案例中,不一定要如此,可能是基于app
stopLocalService();
super.onDestroy();
}
// 用户按button触发,对应layout xml文件的<Button … android:onClient="onClickAct" …/>
public void onClickAct(View v){
switch(v.getId()){
case R.id.startButton:
startLocalService();
break;
case R.id.stopButton:
stopLocalService();
break;
default:
break;
}
}
//请求local service,通过startService()请求本地服务
private void startLocalService(){
Log.v(TAG,"client: Start service... counter = " + counter);
Intent intent = new Intent(this,LocalBackgroundService.class);
intent.putExtra(LocalBackgroundService.COUNTER_STR, counter ++ );//添加传递的数据
startService(intent); //请求本地服务
}
//终止local service,通过stopService()请求本地服务,注意stopService()和startService()并不是一一对应的,可以请求多次本地服务,而一旦stopService()就会关闭本地服务,本例中所有的线程将被终止。
private void stopLocalService(){
Log.v(TAG,"client: Stop service... ");
Intent intent = new Intent(this,LocalBackgroundService.class);
if(stopService(intent)){ //如果service存在并关闭,即触发service的onDestroy(),返回true,如果不存在,返回false
Log.v(TAG,"client: stopService was successful");
}else{
Log.v(TAG,"client: stopService was unsuccessful");
}
}
}
AndroidManifest.xml:定义Service、acitivty的launchMode
和acitivty一样,service也需要在AndroidManifest.xml中定义。
<?xml version="1.0" encoding="utf-8"?>
<manifest … >
<application ... ... >
… …
<!-- 我们在按了Start Service的button后,按Home键返回系统主界面,这时如果我们下拉通知栏,按通知,将会开启一个新的activity。但是原有的activity只是看不见,仍然存在,我们希望显示的仍是原来的activity,通过设置launchMode为signleTop解决这个问题,将invisible的acitivity放在最前显示。 -->
<activity android:name=".LocalServiceClientActivity" android:label="@string/demo_local_service" android:launchMode="singleTop"/>
... ...
<!-- 本例是local service,只需要给出类名作为name属性即可 -->
<service android:name="LocalBackgroundService" />
</application>
… …
</manifest>
一些思考
有时我们只希望运行一个且是唯一的后台服务线程(如播放音乐),可以考虑在onCreate()中启动线程,但这种方式不能获得通过intent中传递的数据,如果那样只好在onStartService()中进行逻辑判断:是否已经有后台线程在运行。
本笔记涉及的例子代码,可以在Pro Android学习:Android service小例子中下载。
相关链接: 我的Android开发相关文章
【转】 Pro Android学习笔记(七七):服务(2):Local Service的更多相关文章
- 【转】 Pro Android学习笔记(六七):HTTP服务(1):HTTP GET
目录(?)[-] HTTP GET小例子 简单小例子 出现异常NetworkOnMainThreadException 通过StrictMode进行处理 URL带键值对 Andriod应用可利用ser ...
- Pro Android学习笔记 ActionBar(1):Home图标区
Pro Android学习笔记(四八):ActionBar(1):Home图标区 2013年03月10日 ⁄ 综合 ⁄ 共 3256字 ⁄ 字号 小 中 大 ⁄ 评论关闭 ActionBar在A ...
- 【转】 Pro Android学习笔记(八二):了解Package(1):包和进程
文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在之前,我们已经学习了如何签发apk,见P ...
- 【转】 Pro Android学习笔记(五五):调试和分析(3):adb命令、模拟器控制台和StrictMode
目录(?)[-] adb命令 模拟器Console StrictMode adb命令 我们在学习SQLite的使用,介绍过部分adb命令的使用,见Pro Android学习笔记(五):了解Conten ...
- 【转】 Pro Android学习笔记(五六):配置变化
目录(?)[-] Activity的destorycreate过程 Fragment的destorycreate过程 onSaveInstanceState saveFragmentInstanceS ...
- 【转】 Pro Android学习笔记(五二):ActionBar(5):list模式
可以在action bar中加入spinner的下来菜单,有关spinner,可以参考Pro Android学习笔记(二十):用户界面和控制(8):GridView和Spinner. list的样式和 ...
- 【转】 Pro Android学习笔记(四二):Fragment(7):切换效果
目录(?)[-] 利用setTransition 利用setCustomAnimations 通过ObjectAnimator自定义动态效果 程序代码的编写 利用fragment transactio ...
- 【转】 Pro Android学习笔记(四十):Fragment(5):适应不同屏幕或排版
目录(?)[-] 设置横排和竖排的不同排版风格 改写代码 对于fragment,经常涉及不同屏幕尺寸和不同的排版风格.我们在基础小例子上做一下改动,在横排的时候,仍是现实左右两个fragment,在竖 ...
- 【转】 Pro Android学习笔记(三五):Menu(6):XML方式 & PopUp菜单
目录(?)[-] 利用XML创建菜单 XML的有关属性 onClick事件 Pop-up菜单 利用XML创建菜单 在代码中对每个菜单项进行设置,繁琐且修改不灵活,不能适配多国语言的要求,可以利用资源进 ...
- 【转】 Pro Android学习笔记(三三):Menu(4):Alternative菜单
目录(?)[-] 什么是Alternative menu替代菜单 小例子说明 Alternative menu代码 关于Category和规范代码写法 关于flags 多个匹配的itemId等参数 什 ...
随机推荐
- 深入 JavaScript 中的对象以及继承原理
ES6引入了一个很甜的语法糖就是 class, class 可以帮助开发者回归到 Java 时代的面向对象编程而不是 ES5 中被诟病的面向原型编程. 我也在工作的业务代码中大量的使用 class, ...
- 【学习】Spring 的 AOP :基于Annotation 的“零配置”方式
转自:http://www.cnblogs.com/jbelial/archive/2012/07/20/2539123.html AOP(Aspect Orient Programming ) , ...
- Apache Shiro 使用手册(二)Shiro 认证(转发:http://kdboy.iteye.com/blog/1154652)
认证就是验证用户身份的过程.在认证过程中,用户需要提交实体信息(Principals)和凭据信息(Credentials)以检验用户是否合法.最常见的“实体/凭证”组合便是“用户名/密码”组合. 一. ...
- Python基础(3)_可变对象与不可变对象、列表、元祖和字典
可变对象与不可变对象 实例被创建后,身份和类型是不可变的, 如果值是不可以被修改的,则是不可变对象 如果值是可以被修改的,则是可变对象 #在id不动,type也不动,value被修改了,则称为可变 # ...
- Android FrameLayout单帧布局
FrameLayout:所有控件位于左上角,并且直接覆盖前面的子元素. 在最上方显示的层加上: android:clickable="true" 可以避免点击上层触发底层. 实例: ...
- 【八】MongoDB管理之分片集群实践
MongoDB中集群有三种:主从复制.副本集.分片集群.目前副本集已经替代主从复制架构,成为官方建议采用的架构,而分片集群相较于前两种,更加复杂. 下面是生产环境中常用的分片集群架构: 我们知道,分片 ...
- Spark MLlib框架详解
1. 概述 1.1 功能 MLlib是Spark的机器学习(machine learing)库,其目标是使得机器学习的使用更加方便和简单,其具有如下功能: ML算法:常用的学习算法,包括分类.回归.聚 ...
- python安装包的方式
easy_install 老版python只有easy_install pip install 说明: 是easy_install的改进版,提供更好的信息提示,添加删除package等功能 安装方式: ...
- (转)Windows下面安装和配置MySQL(5.6.20)
原文地址:http://www.cnblogs.com/qiyebao/p/3887055.html 1.首先到http://dev.mysql.com/ 上下载windows版mysql5.6免安装 ...
- 处理 javax.el.ELException: Failed to parse the expression 报错
在JSP的表达式语言中,使用了 <h3>是否新Session:${pageContext.session.new}</h3> 输出Session是否是新的,此时遇到了 j ...