【Android的从零单排开发日记】之入门篇(五)——Android四大组件之Service

这几天忙着驾校考试,连电脑都碰不到了,今天总算告一段落了~~
Service作为Android的服务组件,默默地在后台为整个程序服务,辅助应用与系统中的其他组件或系统服务进行沟通。它跟Activity的级别差不多,但不能自己运行只能后台运行。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等, 总之服务总是藏在后台的。
ps:Service运行在主线程中的,所以用它做耗时工作时,同样会阻塞进程,所以最好是在service里开个子线程。

一、注册Service

service的注册跟activity的注册类似,同样是要在AndroidManifest.xml的文件里注册。

        <service android:name=".MinaService"><!-- 你自定义的service文件   (在<application></application>里面加)-->
        <intent-filter>
                <action android:name="com.MinaService" /><!-- 用intent启动时的快捷名(也可以用常规的方式启动) -->
                <category android:name="android.intent.category.default" />
        </intent-filter>
        </service>

二、Service的两种模式

service有两种模式,本地服务和远程服务。我们一般开发应用都是用的本地服务,而远程服务经常在做系统开发时被用到。所以今天我会主要讲本地的服务,远程服务放着以后再讲吧(其实UP主也没用过远程服务就是了==)

类别 区别  优点 缺点   应用
本地服务(Local) 该服务依附在主进程上,  服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。  主进程被Kill后,服务便会终止。  非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务。
远程服务(Remote) 该服务是独立的进程,  服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。  该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。  一些提供系统服务的Service,这种Service是常驻的。

三、Service的生命周期及两种启动方式

service的生命周期比activity简单多了,原因是service是在后台运行的,它是一直运行的,所以不需要那么多的状态判断。它只继承了onCreate()、onStart()或者说是onStartCommand()
// 2.0 API level之后,实现onStart等同于重写onStartCommand并返回  关于onStartCommon()的详解:http://blog.csdn.net/lizzy115/article/details/7001731

onDestroy()三个方法。
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。

  1. 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。    如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。 采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
  2. 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。

四、Service实例

接下来我会写一个关于service的实例demo,希望看了之后会对大家有所帮助。

  1. 先新建一个项目,并新建一个class文件,命名为MinaService,继承于service(不要忘记在AndroidManifest.xml文件里注册),其中代码如下:

     1 public class MinaService extends Service{
     2     private String msg = "service bind success";
     3
     4     private final IBinder mBinder = new LocalBinder();
     5
     6     @Override
     7     public IBinder onBind(Intent intent) {
     8         Log.d("TEST", "onbind");
     9         //onBind service时会调用此方法,用于绑定Activity
    10         return mBinder;
    11     }
    12
    13     @Override
    14     public void onCreate() {
    15         //开启服务时会首先调用该方法
    16         Log.d("TEST", "onCreate");
    17         super.onCreate();
    18     }
    19
    20     @Override
    21     public int onStartCommand(Intent intent, int flags, int startId) {
    22         //start service时会在onCreate后调用该方法
    23         Log.d("TEST", "start");
    24         return START_STICKY;
    25     }
    26
    27     @Override
    28     public void onDestroy() {
    29         // 停止service后会调用此方法
    30         Log.d("TEST", "destroy");
    31         super.onDestroy();
    32     }
    33
    34     @Override
    35     public boolean onUnbind(Intent intent) {
    36         //取消Activity与service绑定时要调用此方法
    37         Log.d("TEST", "onunbind");
    38         return super.onUnbind(intent);
    39     }
    40
    41     @Override
    42     public void onRebind(Intent intent) {
    43         // 重新绑定时调用此方法
    44         Log.d("TEST", "onrebind");
    45         super.onRebind(intent);
    46     }
    47
    48
    49     /**
    50      * @author cpacm
    51      * 通过Binder返回service的引用到Activity中
    52      */
    53     public class LocalBinder extends Binder {
    54         MinaService getService() {
    55             return MinaService.this;
    56         }
    57     }
    58
    59     //普通方法,证明service在后台运行
    60     public String getMsg(){
    61         return msg;
    62     }
    63     public void setMsg(String m){
    64         this.msg = m;
    65     }
    66
    67 }
  2. 回到MainActivity中,来看一下我们需要做的处理

      1 public class MainActivity extends Activity implements OnClickListener{
      2
      3     private MinaService ms = null;
      4     private Button b1,b2,b3,b4,b5,b6;
      5     private TextView textView;
      6     private EditText editText;
      7     @Override
      8     protected void onCreate(Bundle savedInstanceState) {
      9         super.onCreate(savedInstanceState);
     10         setContentView(R.layout.activity_main);
     11         //获得界面的控件
     12         textView = (TextView) findViewById(R.id.textView1);
     13         editText = (EditText) findViewById(R.id.editText1);
     14         b1 = (Button) findViewById(R.id.button1);
     15         b1.setOnClickListener(this);
     16         b2 = (Button) findViewById(R.id.button2);
     17         b2.setOnClickListener(this);
     18         b3 = (Button) findViewById(R.id.button3);
     19         b3.setOnClickListener(this);
     20         b4 = (Button) findViewById(R.id.button4);
     21         b4.setOnClickListener(this);
     22         b5 = (Button) findViewById(R.id.button5);
     23         b5.setOnClickListener(this);
     24         b6 = (Button) findViewById(R.id.button6);
     25         b6.setOnClickListener(this);
     26         Log.d("TEST","===初始化完成===");
     27     }
     28
     29     @Override
     30     public void onClick(View v) {
     31         // TODO Auto-generated method stub
     32         switch(v.getId()){
     33             case R.id.button1:{//按钮一:用startService开启服务
     34                 Intent i  = new Intent();
     35                 i.setClass(MainActivity.this, MinaService.class);
     36                 startService(i);
     37                 break;
     38             }
     39             case R.id.button2:{//按钮二:停止服务
     40                 Intent i  = new Intent();
     41                 i.setClass(MainActivity.this, MinaService.class);
     42                 stopService(i);
     43                 break;
     44             }
     45             case R.id.button3:{//按钮三:用bindService来绑定Service和Activity
     46                 bindService(new Intent(MainActivity.this,
     47                         MinaService.class), mConnection, Context.BIND_AUTO_CREATE);
     48                 break;
     49             }
     50             case R.id.button4:{//取消绑定
     51                 unbindService(mConnection);
     52                 break;
     53             }
     54             case R.id.button5:{//跳转到下一个Activity
     55                 Intent i  = new Intent();
     56                 i.setClass(MainActivity.this, SecondActivity.class);
     57                 startActivity(i);
     58
     59                 break;
     60             }
     61             case R.id.button6:{//显示Service里面的信息
     62                 textView.setText(ms.getMsg());
     63                 ms.setMsg(editText.getText().toString());
     64                 break;
     65             }
     66         }
     67     }
     68
     69
     70     public void show(String str){
     71         Toast.makeText(this, str, Toast.LENGTH_LONG).show();
     72     }
     73
     74     private ServiceConnection mConnection = new ServiceConnection(){
     75
     76
     77         /**
     78          * 绑定Service和Activity时会用到这个函数,所以可以在这里获取到Service的引用对象
     79          * @see android.content.ServiceConnection#onServiceConnected(android.content.ComponentName, android.os.IBinder)
     80          **/
     81         @Override
     82         public void onServiceConnected(ComponentName name, IBinder service) {
     83             //获取Service的引用对象
     84              ms = ((MinaService.LocalBinder)service).getService();
     85              Toast.makeText(MainActivity.this, "connect",
     86                      Toast.LENGTH_SHORT).show();
     87         }
     88
     89         /**
     90          * 这个函数是在绑定异常时调用,平时不会使用到这个函数
     91          * @see android.content.ServiceConnection#onServiceDisconnected(android.content.ComponentName)
     92          **/
     93         @Override
     94         public void onServiceDisconnected(ComponentName name) {
     95             // TODO Auto-generated method stub
     96             ms = null;
     97             Toast.makeText(MainActivity.this, "cutdown",
     98                     Toast.LENGTH_SHORT).show();
     99         }
    100
    101     };
    102
    103 }

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
    
            <TextView
                android:id="@+id/textView1"
                android:layout_width="match_parent"
                android:layout_height="57dp"
                android:text="TextView" />
    
            <EditText
                android:id="@+id/editText1"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:ems="10" >
    
                <requestFocus />
            </EditText>
    
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
            <Button
                android:id="@+id/button1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="start service" />
            <Button
                android:id="@+id/button2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="stop service" />
    
            <Button
                android:id="@+id/button3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="bind service" />
    
            <Button
                android:id="@+id/button4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="unbind service" />
    
            <Button
                android:id="@+id/button5"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="next activity" />
    
            <Button
                android:id="@+id/button6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="set message" />
    
        </LinearLayout>
    
    </LinearLayout>

    activity_main

    再新建一个SecondActivity(不要忘记在AndroidManifest.xml里注册Activity),里面代码基本和MainActivity里一样,改一下这里

    case R.id.button5:{
        Intent i  = new Intent();
         i.setClass(SecondActivity.this, MainActivity.class);
        startActivity(i);
        break;
    }

    以上就是Demo里面的组成部分了。接下来我们来看一下运行结果。

  3. 首先我们先启动项目,按下第一个按钮(start service),我们可以看到在LogCat里打印出


    可以看出项目先后调用了onCreate()和onStartCommand()。接下来按下第二个按钮(stop service)

    发现直接将service给destroy掉了。
    接下来我们用BindService来启动Service
    依次按下第三个和第四个按钮,结果如图

    可以发现BindService不会调用onStartCommand()方法,它会使用onbind来代替。unbind后,Service也是会摧毁。

    public boolean bindService(Intent service, ServiceConnection conn, int flags)

    该方法的第1个参数表示与服务类相关联的Intent对象,第2个参数是一个ServiceConnection类型的变量,负责连接Intent对象指定的服务。通过ServiceConnection对象可以获得连接成功或失败的状态,并可以获得连接后的服务对象。第3个参数是一个标志位,一般设为 Context.BIND_AUTO_CREATE。

  4. 如果我们想保持和 Service 的通信,又不想让 Service 随着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService() 。当你不需要绑定的时候就执行 unbindService() 方法,执行这个方法只会触发 Service 的 onUnbind() 而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。(销毁Service还是要调用StopService)

    在应用中,我们先按下第一个按钮,再按下第三个按钮,这样activity就获得了service的对象。现在我们改变service里的值,在输入框输入“Activity与Service通信”,按下第六个按钮。
    然后按下第五个按钮跳转到下一个Activity。在跳转到的Activity中,我们按下第三个按钮(onBind),在按下第六个按钮,你会发现文本框会出现“Activity与Service通信”的字符串。

    从截图上看,第二个activity成功获得了service里面的值。(注意,跳转到下一个Activity前一定要unbind,不然会报错)

  5. 关于service的一些技巧:
    (1)可以在一个Activity启动Service,再在另一个Activity绑定。
    (2)可以多次start同一个Service(通过该方法,可以经由intent传递信息到Service中)
    (3)start->bind->stop,此时service还是存在的不会被销毁,但在调用unbind后,Service会被销毁。
    可能还会有更多的可能性组合,大家可以自己写个demo来好好研究。

五、结语

Service不仅可以与前端界面组件建立双向连接、提供数据和功能支持,也可以单向接受Intent对象的请求,进行数据的分析处理和功能调度。在不同的使用方式下,Service服务组件扮演的角色和开发模式完全不同。这种设计,也为理解Service带来了一定的难度。所以理解和习惯Service的使用显得非常重要。
 
 
参考文章:(1)Android Service学习之本地服务 http://android.blog.51cto.com/268543/527314/
DEMO下载(百度网盘)(过期的请CALL我)
 
作者:cpacm
联系方式:QQ 348515494(请注明博客园)
出处:(http://www.cnblogs.com/cpacm/p/3912578.html
版权声明:本文的版权归作者与博客园共有。欢迎转载阅读,转载时须注明本文的详细链接。

【Android的从零单排开发日记】之入门篇(五)——Android四大组件之Service

时间: 2024-08-02 07:01:19

【Android的从零单排开发日记】之入门篇(五)——Android四大组件之Service的相关文章

【Android的从零单排开发日记】之入门篇(四)——Android四大组件之Activity

在Android中,无论是开发者还是用户,接触最多的就算是Activity.它是Android中最复杂.最核心的组件.Activity组件是负责与用户进行交互的组件,它的设计理念在很多方面都和Web页面类似.当然,这种相似性主要体现在设计思想上.在具体实现方面,Android的Activity组件有自己的设计规范,同时,它能够更简便地使用线程.文件数据等本地资源. 一.Activity 的生命周期 Activity 的生命周期是被以下的函数控制的. 1 public class Activity

【Android的从零单排开发日记】之入门篇(六)——Android四大组件之Broadcast Receiver

广播接受者是作为系统的监听者存在着的,它可以监听系统或系统中其他应用发生的事件来做出响应.如设备开机时,应用要检查数据的变化状况,此时就可以通过广播来把消息通知给用户.又如网络状态改变时,电量变化时都可以通过广播来通知用户.要做比喻的话,广播就像是我们的感官,能够有效且快速的从外界获取信息来反馈给自身. 一.广播的功能和特征 广播的生命周期很短,经过 调用对象—实现onReceive—结束 整个过程就结束了.从实现的复杂度和代码量来看,广播无疑是最迷你的Android 组件,实现往往只需几行代码

【Android的从零单排开发日记】之入门篇(三)——Android目录结构

本来的话,这一章想要介绍的是Android的系统架构,毕竟有了这些知识的储备,再去看实际的项目时才会更清楚地理解为什么要这样设计,同时在开发中遇到难题,也可以凭借着对Android的了解,尽快找出哪些模块和设计能够帮助解决该问题.但想了一下,这毕竟是入门篇,若没有实际项目开发经验的人看了之后肯定是一头雾水,所以就决定将其搁浅到大家熟悉Android之后再为大家介绍. 那么今天的主题是Android的目录结构,将系统架构比作人的骨骼架构的话,目录结构就像是人的各个器官,彼此功能各不相同,却能有序地

【Android的从零单排开发日记】之入门篇(八)——Android数据存储(下)

废话不多说了,紧接着来讲数据库的操作吧.Come On! 提到数据存储问题,数据库是不得不提的.数据库是用来存储关系型数据的不二利器.Android为开发者提供了强大的数据库支持,可以用来轻松地构造基于数据库的应用.Android的数据库应用,依托于当下最流行的开源嵌入式数据库SQLite.在Android中,应用的数据库文件是该应用私有的,存储在应用数据目录下的databases子目录内.从代码结构来看,Android的数据库实现可以分成两个层次,在底层通过C++调用SQLite的接口来执行S

【Android的从零单排开发日记】之入门篇(七)——Android数据存储(上)

在讲解Android的数据源组件——ContentProvider之前我觉得很有必要先弄清楚Android的数据结构. 数据和程序是应用构成的两个核心要素,数据存储永远是应用开发中最重要的主题之一,也是开发平台必须提供的基础功能.不光是在Android平台上,在其他的平台上,数据的存储永远是不可缺少的一块.Android的数据存储是构建在Linux的文件系统上,它充分利用Linux的账号系统来限定应用对数据的访问,部署了一套安全和灵活并重的数据存储解决方案.Android的文件框架,以及各种数据

【Android的从零单排开发日记】之入门篇(一)——开发环境的搭建

写给自己的话:至此,大学的时光已经剩下一年的时光,下一年等毕业设计结束后就算是正式地踏入社会.自己学android也不过几个月的时间,为了更好管理文档,写点东西记录下自己曾经做过的点点滴滴是一个不错的选择,接下来都会将我自己所学所感一一记录下来,算是给后来的我一份复习的资料和还算不错的回忆. 开始正题吧,android环境的安装,网上很多教程,本来不想写这章的,后来帮同学配置时想想有个总结也是不错的,所以就写了这篇满是链接的文章. 一.需要准备的文件 JDK 官方网站 http://www.or

【Android的从零单排开发日记】之入门篇(十二)——Android组件间的数据传输

组件我们有了,那么我们缺少一个组件之间传递信息的渠道.利用Intent做载体,这是一个王道的做法.还有呢,可以利用文件系统来做数据共享.也可以使用Application设置全局数据,利用组件来进行控制数据. 一.Intent数据传递 那么首先是简单的跳转.我们可以借助bundle这个容器来存放我们想要传递的数据. Intent intent = new Intent(); intent.setClass(activity1.this, activity2.class); //描述起点和目标 Bu

【Android的从零单排开发日记】——Android四大组件之ContentProvider

数据源组件ContentProvider与其他组件不同,数据源组件并不包括特定的功能逻辑.它只是负责为应用提供数据访问的接口.Android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等).如果把第三方应用比作一个黑盒子的话,ContentProvider就像是从里面延伸出来的管道,从这个管道,应用可以把一些数据共享出来,我们也可以往里面输送数据.但是里面怎么处理数据我们看不到,也管不着.并且这个管道是有规范标准的,不是它规定的数据你塞不进

【Android的从零单排开发日记】之入门篇(九)——Android四大组件之ContentProvider

数据源组件ContentProvider与其他组件不同,数据源组件并不包括特定的功能逻辑.它只是负责为应用提供数据访问的接口.Android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等).如果把第三方应用比作一个黑盒子的话,ContentProvider就像是从里面延伸出来的管道,从这个管道,应用可以把一些数据共享出来,我们也可以往里面输送数据.但是里面怎么处理数据我们看不到,也管不着.并且这个管道是有规范标准的,不是它规定的数据你塞不进