文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处: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相关推荐

  1. Pro Android学习笔记(二九):用户界面和控制(17):include和merge

    xml控件代码重用:include 如果我们定义一个控件,需要在不同的layout中重复使用,或者在同一个layout中重复使用,可以采用include的方式.例如定义my_button.xml如下 ...

  2. 【转】 Pro Android学习笔记(二九):用户界面和控制(17):include和merge

    目录(?)[-] xml控件代码重用include xml控件代码重用merge 横屏和竖屏landsacpe portrait xml控件代码重用:include 如果我们定义一个控件,需要在不同的 ...

  3. Pro Android学习笔记(三三):Menu(4):Alternative菜单

    什么是Alternative menu(替代菜单) 举个例子,Activity显示一个文本文件.如果用户想对文本文件进行编辑,Activity不提供编辑能力,但可由其他activity或者其他应用提供 ...

  4. 【转】Pro Android学习笔记(二五):用户界面和控制(13):LinearLayout和TableLayout...

    目录(?)[-] 布局Layout 线性布局LinearLayout 表格布局TableLayout 布局Layout Layout是容器,用于对所包含的view进行布局.layout是view的子类 ...

  5. 【转】 Pro Android学习笔记(二十):用户界面和控制(8):GridView和Spinner

    目录(?)[-] GridView Spinner GridView GridView是网格状布局,如图所示.在了解ListView后,很容易了解GridView.下面是例子的XML文件. <? ...

  6. Pro Android学习笔记(一五五) 传感器(5) 磁场传感器和方位(上)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 文章转载 ...

  7. Pro Android学习笔记(一五五):传感器(5): 磁场传感器和方位(上)

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei. 磁场传感器(Magne ...

  8. Pro Android学习笔记 四八 ActionBar 1 Home图标区

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! Acti ...

  9. Pro Android学习笔记(四八) ActionBar(1) Home图标区

    ActionBar在Android 3.0 SDK中为平板引入,在4.0中也可以在phone中使用.在title中提供类似tab和菜单的效果,有三种形式:Tabbed action bar,list ...

最新文章

  1. 测试中如何管理外包质量_如何从测试自动化中实现价值
  2. 转:秒杀系统架构分析与实战
  3. 无线节能组的充电问题
  4. Facebook全球6小时宕机原因已查明:一条指令所致,内部工程师所为
  5. 这几个技巧让你做出让导师眼前一亮的答辩 PPT
  6. HDU - 6749 Mosquito(二分+状态压缩+最大流)
  7. windows获取硬件设备的guid_Windows编程技术:提权技术(下)
  8. hadoop 开启防火墙_hadoop上搭建hive
  9. 【附源码】手写一个Ico生成器
  10. 北四环的杰西·利弗莫尔:一个中国投机客的传说
  11. 【BZOJ5470】[FJOI2018]所罗门王的宝藏()
  12. 高处的圣地 --读《消失的地平线》
  13. 漫漫Java学习路,第十四天
  14. restore,recover的区别
  15. Eclips注释模板的使用
  16. 【软考软件评测师】2019综合知识历年真题
  17. LNK2001: unresolved external symbol_WinMain@16
  18. pip install mysqlclient安装
  19. 小岳岳吐槽房子隔音差:买房小心隔墙有耳
  20. shell win7 乱码问题

热门文章

  1. 网络管理员常见错误集锦
  2. 上传失败 已保存至草稿_我告诉你抖音上传失败保存至草稿
  3. Unity Shader - URP Instancing
  4. 【CSDN】-长沙1024程序员节技术英雄会之行
  5. sumo之加入货运车辆
  6. 七夕版Python学习教程:用 Python 给女友准备甜蜜惊喜的七夕礼物
  7. Dynamic Web TWAIN构建兼容 HTML5
  8. 【华为OD机试真题 python】字符串分割
  9. 台湾新生产的电脑要兼容 Linux
  10. 用计算机都会听错数字怎么回事,由web程序出现乱码开始挖掘(Bom头、字符集与乱码)...