4、Service详解(二):本地Service

来源:互联网 发布:mac显示网络图标 编辑:程序博客网 时间:2024/05/21 07:14
    本地Service同Activity一样,会存在于当主前线程中,所以我们不能把耗时的操作放在Service中执行,比如把网络请求处理的任务放到这里面的话,同Activity一样,可会会报ANR问题,这点我们在开发的时候需要注意,官方的API也给出了这样的提示。Service和Activity的最大区别就在于Service没有前台界面,尽管没有界面,他还是有自己的生命周期的。

     Service生命周期:



    如果应用程序通过startService()方法来启动Service,Service的生命周期如上图左边所示。
    如果应用程序通过bindService()方法来启动Service,Service的生命周期如上图右边所示。


(一)通过startService()方法启动Service
    首先我们先看一下上面生命周期中的几个方法:
    • void onCreate();当该Service第一次被创建后将立即回掉该方法。
    •      void onStartCommand(Intent intent,int flags,int startId);该方法的早期方法是onStart(Intent intent,int startId);每次通过startService(Intent intent)方法启动Service时都会回掉该方法。
    • void onDestroy();当该Service被关闭之前会回掉该方法。

    下面的类定义了一个Service组件,我们只是输出了一条语句:

public class ServiceWithStart extends Service {     @Override     public IBinder onBind(Intent intent) {          // 使用startService方式不会回调这个方法          return null;     }     @Override     public void onCreate() {          System.out.println("Service OnCreate");          super.onCreate();     }     @Override     public int onStartCommand(Intent intent, int flags, int startId) {          System.out.println("Service OnStartCommand");          return super.onStartCommand(intent, flags, startId);     }     @Override     public void onDestroy() {          System.out.println("Service OnDestroy");          super.onDestroy();     }}


    所有的Service和Activity同样,需要在AndroidManifest.xml中配置,Activity和Service是平级的,同样配置在application标签内。
 <service android:name= ".ServiceWithStart" ></service>

    为了能够演示启动和停止Service,我们使用两个按钮,直接在activity_main.xml中插入2个按钮,分别用于启动Service和停止Service:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools= "http://schemas.android.com/tools"    android:layout_width= "match_parent"    android:layout_height= "match_parent"    android:background= "#fff"    android:gravity= "center"    android:orientation= "vertical" >    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        android:onClick="onBtnStartService"        android:text="使用StartService启动" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        android:onClick="onBtnStopService"        android:text="使用stop停止" /></LinearLayout>

   界面如图所示




    在MainActivity中分别实现启动和停止

public class MainActivity extends Activity {     private Intent intent;     @Override     protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);          setContentView(R.layout. activity_main);           intent = new Intent(this , ServiceWithStart.class );     }     //第一个按钮的绑定事件     public void onBtnStartService(View v) {          startService( intent);     }     //第二个按钮的绑定事件     public void onBtnStopService(View v) {          stopService( intent);     }}


所有的准备都完成了,下面我们开始测试:
1.进入应用,我们点击启动按钮,这时LogCat输出了两行信息,正如生命周期所示
         
Service OnCreate
          Service OnStartCommand
2.再次点击启动按钮,LogCat输出了一行信息,说明Service已经有一个实例启动,不会再执行OnCreate
          
 Service OnStartCommand
3.点击停止按钮,LogCat输出一行信息,Service已经停止。
          Service OnDestroy
4.再次点击启动按钮,Service再次启动
          
Service OnCreate
          Service OnStartCommand
5.直接点击返回键,退出应用,Logcat没有任何输出,Service仍在后台运行,这个问题一定要注意

6.再次进入应用,点击返回键,LogCat输出一行信息,退出应用没有停止的Service已经停止。
          Service OnDestroy




(二)通过bindService()方法启动Service
    首先我们先看一下生命周期中的几个方法:
       void onCreate()和 void onDestroy()就不说了,上面讲过了看下其他的
IBinder onBind(Intent intent):该方法是Service子类必须实现的方法,该方法返回一个IBinder对象,应用程序可通过该对象与Service组件通信。
boolean onUnbind(Intent intent):当该Service上绑定的所有客户端都断开连接时将会回调该方法。
     使用startService方式启动的Service与访问者之间基本上不存在关联,因此Service与访问者之间也无法进行通信和数据交换。如果需要访问者和Service进行方法调用和数据交换,则应该使用bindService方法启动和关闭。
     使用bindService方式,需要调用Context的bindService(Intent service,ServiceConnection conn,int flags)该方法有三个参数,三个参数都比较重要,下面依次介绍这三个参数:
service:通过Intent指定要启动的Service
conn:ServiceConnection对象用于访问者与Service连接情况,当连接成功则回调该对象的onServiceConneccted(ComponentName name,IBinder service)方法,当访问者与Service由于异常情况终止(unBindService断开连接时不回调)连接时会回调该对象的onServiceDisconnected(ComponentName name)方法。
flags:指定绑定时是否自动创建Service,0为不自动创建,BIND_AUTO_CREATE为自动创建。
     我们可以看到Service中的onBind方法会返回一个IBinder对象,于此同时,我们看到bindService中conn参数对应的对象中有个方法onServiceConneccted(ComponentName name,IBinder service),这里面有个参数IBinder service,其实这两个IBinder对戏那个是同一个,而组件与Service的通信也是通过IBinder方式来完成的。通常我们采用继承Binder的方式来实现自己的IBinder对象。
     下面我们还是通过例子来看一下bindService方式怎么使用:
     首先定义了一个Service组件,我们想要看看组件与Service之间的通信情况,所以我们先定义一个接口,方便数据访问:
public interface IBook {     //获取书的名字     String getNameById( int Id);}

    来看一下我们的Service,这里要重点看我们自己定义的Binder对象,通信就靠它了:
public class ServiceWithBind extends Service {     private static String[] bookArr = { "Java", "设计模式" , "IOS开发" , "网络编程" };     // Service被创建时回调此方法     @Override     public void onCreate() {           super.onCreate();          System. out.println("Service onCreate" );     }     // 绑定Service时回调该方法     @Override     public IBinder onBind(Intent intent) {          System. out.println("Service onBind" );           return new BookBinder();     }     // Service被断开连接时回调此方法     @Override     public boolean onUnbind(Intent intent) {          System. out.println("Service onUnbind" );           return super .onUnbind(intent);     }     // Service被关闭之前回调此方法     @Override     public void onDestroy() {          System. out.println("Service onDestroy" );           super.onDestroy();     }     // 自定义的IBinder实现类     private class BookBinder extends Binder implements IBook {           @Override           public String getNameById(int Id) {               // TODO Auto-generated method stub               if (Id < 0 || Id >= bookArr.length) {                    return "" ;              } else {                    return bookArr [Id];              }          }     }}

同样需要在AndroidMenifist中注册:

<service android:name=".ServiceWithBind" ></service>

我们定义一个布局文件activity_bindservice.xml,放上三个按钮,1个绑定按钮,1个解绑定按钮,1个查看通信的按钮:
<?xml version= "1.0" encoding ="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width= "match_parent"    android:layout_height= "match_parent"    android:background= "#fff"    android:gravity= "center"    android:orientation= "vertical" >    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        android:onClick="onBtnBindService"        android:text="使用bindService启动" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        android:onClick="onBtnUnbindService"        android:text="使用unbindService停止" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        android:onClick="onBtnBookName"        android:text="查看书的名字" /></LinearLayout>

界面如图所示


在Activity中实现各种方法,注意看通信中重要的ServiceConnection 对象,在哪里可以拿到我们的访问接口

public class BindDomeActivity extends Activity {     private Intent intent;     private IBook myBook;     @Override     protected void onCreate(Bundle savedInstanceState) {           // TODO Auto-generated method stub           super.onCreate(savedInstanceState);          setContentView(R.layout. activity_bindservice);           intent = new Intent(this , ServiceWithBind.class );     }     private ServiceConnection conn = new ServiceConnection() {           @Override           public void onServiceDisconnected(ComponentName name) {               // TODO Auto-generated method stub          }           @Override           public void onServiceConnected(ComponentName name, IBinder service) {               // TODO Auto-generated method stub               myBook = (IBook) service;          }     };     //绑定Service的按钮点击事件     public void onBtnBindService(View v) {          bindService( intent, conn, Service.BIND_AUTO_CREATE );     }     //取消绑定Service的按钮点击事件     public void onBtnUnbindService(View v) {          unbindService( conn);     }     //查看Service中书籍     public void onBtnBookName(View v) {          String bookName = myBook.getNameById(2);          System. out.println(bookName);          Toast. makeText(this, "书名:" + bookName, Toast.LENGTH_SHORT).show();     }}


所有的准备都完成了,下面我们开始测试:
1.进入应用,我们点击启动按钮,这时LogCat输出了两行信息,正如生命周期所示
          
Service onCreate
          Service onBind
2.点击停止按钮,LogCat输出两行信息,说明已经解除连接,并且停止了Service
         
 Service onUnbind
          Service onDestroy
3.再重复执行步骤1,这时候Service已经绑定,我们退出应用,会发现有错误提示,这是由于还没有解除绑定就退出组件了,所以会报错,我们开发过程中要注意解除绑定
4.再重复执行步骤1,点击查看书的名字按钮,会提示:书名:IOS开发,这说明我们成功的通信了。





(三)start和bind方式的区别与注意
1、通过startService()和stopService()启动关闭服务。适用于服务和访问者之间没有交互的情况。如果服务和访问者之间需要方法调用或者传递参数,则需要使用bindService()和unbindService()方法启动关闭服务。
2、采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法,这个时候访问者和服务绑定在一起。
3、如果访问者要与服务进行通信,那么,onBind()方法必须返回Ibinder对象。如果访问者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果访问者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()—>onDestroy()方法。
4、Service的onCreate的方法只会被调用一次,就是你无论多少次的startService又 bindService,Service只被创建一次。如果先是bind了,那么start的时候就直接运行Service的onStart方法,如果先是start,那么bind的时候就直接运行onBind方法。如果你先bind上了,就stop不掉了,只能先UnbindService, 再StopService,所以是先start还是先bind行为是有区别的。

                     


0 0
原创粉丝点击