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

    本地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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值