Android四大组件之 Service

一    Service简介php

        Service是运行在后台的,没有界面的,用来处理耗时比较长的。Service不是一个单独的进程,不是一个单独的线程。java

        Service有两种类型:android

  1. 本地服务(Local Service):用于应用程序内部
  2. 远程服务(Remote Sercie):用于android系统内部的应用程序之间
       

      前者用于实现应用程序本身的一些耗时任务,好比查询升级信息,并不占用应用程序好比Activity所属线程,而是单开线程后台执行,这样用户体验比较 好。windows

      后者可被其余应用程序复用,好比天气预报服务,其余应用程序不须要再写这样的服务,调用已有的便可。app

     

二  Service的生命周期eclipse

      Service有startService()和bindService()两种启动Service方法,每种方法Service的生命周期是不同的。ide

     1 经过startService()
       Service会经历 onCreate --> onStartCommand()
       stopService的时候直接onDestroy

       若是是 调用者 直接退出而没有调用stopService的话,Service会一直在后台运行。
       下次调用者再起来仍然能够stopService。this

    2 经过bindService()  
       Service只会运行onCreate()-->onBind()

      这个时候 调用者和Service绑定在一块儿
       unbindService的时候  onUnbind()-->onDestroyed()
      调用者退出了,Srevice就会调用onUnbind()-->onDestroyed()
      所谓绑定在一块儿就共存亡了。 spa

 

注意:Service的onCreate的方法只会被调用一次,
就是你不管多少次的startService又 bindService,Service只被建立一次。
若是先是bind了,那么start的时候就直接运行Service的onStart方法,
若是先是start,那么bind的时候就直接运行onBind方法。若是你先bind上了,就stop不掉了,
只能先UnbindService, 再StopService,因此是先start仍是先bind行为是有区别的。

Android中的服务和windows中的服务是相似的东西,服务通常没有用户操做界面,它运行于系统中不容易被用户发觉,可使用它开发如监控之类的 程序。

服务不能本身运行,须要经过调用Context.startService()或Context.bindService()方法启动服务。
这两个方法均可以启动Service,可是它们的使用场合有所不一样。使用startService()方法启用服务,调用者与服务之间没有关连,
即便调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一块儿,调用者一旦退出,服务也就终止,大有“不求同 时生,必须同时死”的特色。

若是打算采用Context.startService()方法启动服务,在服务未被建立时,系统会先调用服务的onCreate()方法,
接着调用onStart()方法。若是调用startService()方法前服务已经被建立,屡次调用startService()方法并不会致使屡次 建立服务,
但会致使屡次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结 束服务,服务结束时会调用onDestroy()方法。

若是打算采用Context.bindService()方法启动服务,在服务未被建立时,系统会先调用服务的onCreate()方法,
接着调用onBind()方法。这个时候调用者和服务绑定在一块儿,调用者退出了,系统就会先调用服务的onUnbind()方法,
接着调用onDestroy()方法。若是调用bindService()方法前服务已经被绑定,
屡次调用bindService()方法并不会致使屡次建立服务及绑定(也就是说onCreate()和onBind()方法并不会被屡次调用)。
若是调用者但愿与正在绑定的服务解除绑定,能够调用unbindService()方法,调用该方法也会致使系统调用服务的 onUnbind()-->onDestroy()方法.   线程

 

 

三    代码实例

编写不需和Activity交互的本地服务示例

本地服务编写比较简单。首先,要建立一个Service类,该类继承android的Service类。这里写了一个计数服务的类,每秒钟为计数器 加一。在服务类的内部,还建立了一个线程,用于实现后台执行上述业务逻辑。

 

 

package com.easymorse;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class CountService extends Service {

   
private boolean threadDisable;

   
privateint count;

    @Override
   
public IBinder onBind(Intent intent) {
       
returnnull;
    }

    @Override
   
public void onCreate() {
       
super.onCreate();
       
new Thread(new Runnable() {

            @Override
           
publicvoid run() {
               
while (!threadDisable) {
                   
try {
                        Thread.sleep(
1000);
                    }
catch (InterruptedException e) {
                    }
                    count
++;
                    Log.v(
"CountService","Count is " + count);
                }
            }
        }).start();
    }

    @Override
   
public void onDestroy() {
       
super.onDestroy();
       
this.threadDisable= true;
        Log.v(
"CountService","on destroy");
    }

   
public int getCount() {
       
return count;
    }

}

 须要将该服务注册到配置文件AndroidManifest.xml中,不然没法找到:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package
="com.easymorse" android:versionCode="1" android:versionName="1.0">
   
<applicationandroid:icon="@drawable/icon" android:label="@string/app_name">
       
<activityandroid:name=".LocalServiceDemoActivity"
            android:label
="@string/app_name">
           
<intent-filter>
               
<actionandroid:name="android.intent.action.MAIN"/>
               
<categoryandroid:name="android.intent.category.LAUNCHER"/>
           
</intent-filter>
       
</activity>
       
<serviceandroid:name="CountService"/>
   
</application>
   
<uses-sdkandroid:minSdkVersion="3"/>
</manifest/>

 

 

在Activity中启动和关闭本地服务。

package com.easymorse;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class LocalServiceDemoActivityextends Activity {
   
/** Called when the activity is first created.*/
    @Override
   
public void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

       
this.startService(new Intent(this, CountService.class));
    }

    @Override
   
protectedvoid onDestroy() {
       
super.onDestroy();
       
this.stopService(new Intent(this, CountService.class));
    }
}

 

可经过日志查看到后台线程打印的计数内容。

编写本地服务和Activity交互的示例

上面的示例是经过startService和stopService启动关闭服务的。适用于服务和activity之间没有调用交互的状况。若是之 间须要传递参数或者方法调用。须要使用bind和unbind方法。

具体作法是,服务类须要增长接口,好比ICountService,另外,服务类须要有一个内部类,这样能够方便访问外部类的封装数据,这个内部类 须要继承Binder类并实现ICountService接口。还有,就是要实现Service的onBind方法,不能只传回一个null了。

这是新创建的接口代码:

package com.easymorse;

public interface ICountService {
   
public abstract int getCount();
}

 

修改后的CountService代码:

package com.easymorse;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class CountServiceextends Serviceimplements ICountService {

   
privateboolean threadDisable;

   
privateint count;

   
private ServiceBinder serviceBinder=new ServiceBinder();

   
public class ServiceBinderextends Binderimplements ICountService{
        @Override
       
publicint getCount() {
           
return count;

        }

    }

    @Override

    public IBinder onBind(Intent intent) {
       
return serviceBinder;
    }

    @Override
   
public void onCreate() {
       
super.onCreate();
       
new Thread(new Runnable() {

            @Override
           
publicvoid run() {
               
while (!threadDisable) {
                   
try {
                        Thread.sleep(
1000);
                    }
catch (InterruptedException e) {
                    }
                    count
++;
                    Log.v(
"CountService","Count is " + count);
                }
            }
        }).start();
    }

    @Override
   
public void onDestroy() {
       
super.onDestroy();
       
this.threadDisable= true;
        Log.v(
"CountService","on destroy");
    }

   
/* (non-Javadoc)
     * @see com.easymorse.ICountService#getCount()
    
*/
   
public int getCount() {
       
return count;
    }

}

 

服务的注册也要作改动,AndroidManifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package
="com.easymorse" android:versionCode="1" android:versionName="1.0">
   
<applicationandroid:icon="@drawable/icon" android:label="@string/app_name">
       
<activityandroid:name=".LocalServiceDemoActivity"
            android:label
="@string/app_name">
           
<intent-filter>
               
<actionandroid:name="android.intent.action.MAIN"/>
               
<categoryandroid:name="android.intent.category.LAUNCHER"/>
           
</intent-filter>
       
</activity>
       
<serviceandroid:name="CountService">
           
<intent-filter>
               
<actionandroid:name="com.easymorse.CountService"/>
           
</intent-filter>
       
</service>
   
</application>
   
<uses-sdkandroid:minSdkVersion="3"/>
</manifest>

 

Acitity代码再也不经过startSerivce和stopService启动关闭服务,另外,须要经过ServiceConnection的 内部类实现来链接Service和Activity。

package com.easymorse;

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.util.Log;

public class LocalServiceDemoActivity extends Activity {

   
private ServiceConnection serviceConnection= new ServiceConnection() {

        @Override
       
publicvoid onServiceConnected(ComponentName name, IBinder service) {
            countService
= (ICountService) service;
            Log.v(
"CountService","on serivce connected, count is"
                   
+ countService.getCount());
        }

        @Override
       
publicvoid onServiceDisconnected(ComponentName name) {
            countService
=null;
        }

    };

   
private ICountService countService;

   
/** Called when the activity is first created.*/
    @Override
   
public void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
this.bindService(new Intent("com.easymorse.CountService"),
               
this.serviceConnection, BIND_AUTO_CREATE);
    }

    @Override
   
protectedvoid onDestroy() {

          this.unbindService(serviceConnection);       

          super.onDestroy();      //注意前后
    }
}

 

编写传递基本型数据的远程服务

上面的示例,能够扩展为,让其余应用程序复用该服务。这样的服务叫远程(remote)服务,其实是进程间通讯(RPC)。

这时须要使用android接口描述语言(AIDL)来定义远程服务的接口,而不是上述那样简单的java接口。扩展名为aidl而不是java。 可用上面的ICountService改动而成ICountSerivde.aidl,eclipse会自动生成相关的java文件。

package com.easymorse;

interface ICountService {
   
int getCount();
}

 

编写服务(Service)类,稍有差异,主要在binder是经过远程得到的,须要经过桩(Stub)来获取。桩对象是远程对象的本地代理。

package com.easymorse;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class CountService extends Service {

   
privateboolean threadDisable;

   
privateint count;

   
private ICountService.Stub serviceBinder= new ICountService.Stub() {

        @Override
       
publicint getCount()throws RemoteException {
           
return count;
        }
    };

    @Override
   
public IBinder onBind(Intent intent) {
       
return serviceBinder;
    }

    @Override
   
public void onCreate() {
       
super.onCreate();
       
new Thread(new Runnable() {

            @Override
           
publicvoid run() {
               
while (!threadDisable) {
                   
try {
                        Thread.sleep(
1000);
                    }
catch (InterruptedException e) {
                    }
                    count
++;
                    Log.v(
"CountService","Count is " + count);
                }
            }
        }).start();
    }

    @Override
   
public void onDestroy() {
       
super.onDestroy();
       
this.threadDisable= true;
        Log.v(
"CountService","on destroy");
    }
}

 

配置文件AndroidManifest.xml和上面的相似,没有区别。

在Activity中使用服务的差异不大,只须要对ServiceConnection中的调用远程服务的方法时,要捕获异常。

private ServiceConnection serviceConnection= new ServiceConnection() {

    @Override
   
public void onServiceConnected(ComponentName name, IBinder service) {
        countService
= (ICountService) service;
       
try {
            Log.v(
"CountService","on serivce connected, count is"
                   
+ countService.getCount());
        }
catch (RemoteException e) {
           
thrownew RuntimeException(e);
        }
    }

    @Override
   
public void onServiceDisconnected(ComponentName name) {
        countService
=null;
    }

};

 

这样就能够在同一个应用程序中使用远程服务的方式和本身定义的服务交互了。

若是是另外的应用程序使用远程服务,须要作的是复制上面的aidl文件和相应的包构到应用程序中,其余调用等都同样。

编写传递复杂数据类型的远程服务

远程服务每每不仅是传递java基本数据类型。这时须要注意android的一些限制和规定:

  1. android支持String和CharSequence
  2. 若是须要在aidl中使用其余aidl接口类型,须要 import,即便是在相同包结构下;
  3. android容许传递实现Parcelable接口的类,须要import;
  4. android 支持集合接口类型List和Map,可是有一些限制,元素必须是基本型或者上述三种状况,不须要import集合接口类,可是须要对元素涉及到的类型 import;
  5. 非基本数据类型,也不是String和CharSequence类型的,须要有方向指示,包括in、out和 inout,in表示由客户端设置,out表示由服务端设置,inout是二者都可设置。

这里将前面的例子中返回的int数据改成复杂数据类型:

package com.easymorse;

import android.os.Parcel;
import android.os.Parcelable;

public class CountBean implements Parcelable {

   
public static final Parcelable.Creator<CountBean> CREATOR= new Creator<CountBean>() {

        @Override
       
public CountBean createFromParcel(Parcel source) {
            CountBean bean
=new CountBean();
            bean.count
= source.readInt();
           
return bean;
        }

        @Override
       
public CountBean[] newArray(int size) {
           
returnnew CountBean[size];
        }

    };

   
public int count;

    @Override
   
public void writeToParcel(Parcel dest,int flags) {
        dest.writeInt(
this.count);
    }

    @Override
   
public int describeContents() {
       
return;
    }

}

 

而后,须要在相同包下建一个同名的aidl文件,用于android生成相应的辅助文件:

package com.easymorse;

parcelable CountBean;

 

这一步是android 1.5后的变化,没法经过adt生成aidl,也不能用一个好比全局的project.aidl文件,具体见:

http://www.anddev.org/viewtopic.php?p=20991

而后,须要在服务的aidl文件中修改以下:

package com.easymorse;

import com.easymorse.CountBean;

interface ICountService {
    CountBean getCount();
}

 

其余的改动很小,只需将CountService和调用CountService的部分修改成使用CountBean便可

相关文章
相关标签/搜索