Chinaunix首页 | 论坛 | 博客
  • 博客访问: 269017
  • 博文数量: 138
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 971
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-03 10:05
文章分类

全部博文(138)

文章存档

2016年(1)

2015年(137)

我的朋友

分类: Android平台

2015-03-03 22:03:34

运行service有两种方式
1 通过startService(intent)启动:其他组件调用startService()时回调onStartCommand(intent,flag,startId)来启动service;
2 通过bindService(intent,conn,flag)启动:保持调用者与service绑定在一起,以实现service和调用者进行通信和数据交换;参数intent用以指定调用者要启动的service,conn用以监听调用者和service之间的连接情况:当调用者与service成功连接时将回调conn对象的onServiceConnection(ComponentName name,IBinder service)方法;当service的调用对象所在进程意外中止,则调用conn对象的onServiceDisconnected()方法。
具体的 用bindService实现其他组件与service绑定
 Bind Service就像是C/S架构中的服务端,其他组件(比如 Activity)绑定到它(通过 bindService()),可以向它发送请求,可以接受从它返回的响应,它甚至还提供了进程间通信(IPC)功能。
其他组件.bindService(intent,comm,flag);
                                                     comm = new comm()           //当其他组件与service连接成功时,就会(自动)回调onServiceConnected()
                                                    {
                                                           public  void onServiceConnected(name  ,IBinder  service );
                                                                                                                                 这里的参数IBinder service 是由 Bind Service的IBinder onBind()方法的返回值提供。
                                                     }
//这样就实现了其他组件和Bind Service的绑定
part I

Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中 通过来声明。可以通过contect.startservice和contect.bindserverice来启动。 


Service和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现。 

service的两种模式 
本地服务 Local Service 用于应用程序内部。 
它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。 
用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。 

远程服务 Remote Service 用于android系统内部的应用程序之间。 
它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。 

可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。 
startService()/bindService()不是完全分离的. 
生命周期 
Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当我们第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。 

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

part II   
定义(启动)一个Service:

1、如何定义(启动)一个Service:

核心步骤如下:

  • 创建一个类继承android.app.Service类,实现抽象方法onBind(),重写onCreate()、onStartCommand()、onDestry();---2.0 以前的版本为onStart()
  • 在清单文件中配置Service。
  • 通过调用startService()间接调用onCreate()和onStartCommand(),开启服务

新建一个Android项目ServiceTest,具体步骤如下:

(1)新建一个MyService类,继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法,代码如下:

按 Ctrl+C 复制代码
package com.example.servicetest;


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


public class MyService extends Service {  
      
    public static final String TAG = "MyService";  
  
    //创建服务时调用
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate");  
    }  
  
    //服务执行的操作
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand");  
        return super.onStartCommand(intent, flags, startId);  
    }  
      
    //销毁服务时调用
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy");  
    }  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }  
}
按 Ctrl+C 复制代码

可以看到,我们只是在onCreate()、onStartCommand()和onDestroy()方法中分别打印了一句话,并没有进行其它任何的操作,注意代码注释中这三个方法的作用

onBind()方法是Service中唯一的一个抽象方法,所以必须要在子类里实现。我们知道,Service可以有两种启动方式:一种是startService(),另一种是bindService()。第二种启动方式才会用到onBind()方法。我们这先用第一种方式启动Service,所以暂时忽略onBind()方法。

(2)在清单文件中声明:(和Activity标签并列)

 <service android:name=".MyService"> service>

(3)修改activity_main.xml代码,如下:

复制代码
<LinearLayout xmlns:android="" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1_start_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Service" /> <Button android:id="@+id/button2_stop_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Stop Service" /> LinearLayout> 
复制代码

我们在布局文件中加入了两个按钮,一个用于启动Service,一个用于停止Service。

(4)在MainActivity作为程序的主Activity,在里面加入启动Service和停止Service的逻辑,代码如下:

package com.example.servicetest;


import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;




public class MainActivity extends Activity implements OnClickListener {  
      
    private Button button1_start_service;  
  
    private Button button2_stop_service;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        button1_start_service = (Button) findViewById(R.id.button1_start_service);  
        button2_stop_service = (Button) findViewById(R.id.button2_stop_service);  
        button1_start_service.setOnClickListener(this);  
        button2_stop_service.setOnClickListener(this);  
    }  
  
    @Override  
    public void onClick(View v) {  
        switch (v.getId()) {  
        case R.id.button1_start_service:  
            Intent startIntent = new Intent(this, MyService.class);  
            startService(startIntent);  
            break;  
        case R.id.button2_stop_service:  
            Intent stopIntent = new Intent(this, MyService.class);  
            stopService(stopIntent);  
            break;  
        default:  
            break;  
        }  
    }  
  
}

核心代码:31行至32行、35行至36行。

可以看到,在Start Service按钮的点击事件里,我们构建出了一个Intent对象,并调用startService()方法来启动MyService,这时MyService会调用自身的onCreate()方法(该Service未创建),接着调用onStart()方法然后在Stop Serivce按钮的点击事件里,我们同样构建出了一个Intent对象,并调用stopService()方法来停止MyService  这时MyService会调用自身的onDestory()方法注意startService()和stopService()方法都是定义在Context类当中的,所以可以在MainActivity中直接调用这两个方法.代码的逻辑非常简单。

这样的话,一个简单的带有Service功能的程序就写好了。

启动和停止服务:

定义好服务之后,接下来看一下如何启动和停止一个服务,这主要是借助Intent来实现的。注意startService()和stopService()方法都是定义在Context类当中的,所以可以在MainActivity中直接调用这两个方法。

运行上面的程序,点击button1_start_service按钮,启动服务,后台打印日志如下:

说明服务启动成功。

那么如果我再连续点三次button1_start_service按钮,后台增加的日志如下:

事实上onCreate()方法只会在Service第一次被创建的时候调用,而onStartCommand()方法在每次启动服务的时候都会调用

我们还可以在正在“设置--应用---运行”中找到这个服务,如下图所示:

点开上图中的红框部分,可以看到:

如果我们再点击button2_stop_service按钮或者点击上图中的“Stop”,MyService服务就停止掉了:

需要注意的是:

  • 服务对象同时只会有一个
  • 默认情况下,一个started的Service与启动他的组件在同一个线程中。上面的实例中,服务就是在主线程中运行的,如果是在服务中完成耗时操作的话,容易造成主线程阻塞。

2、停止一个started服务有两种方法:

(1)在外部使用stopService()

(2)在服务内部(onStartCommand方法内部)使用stopSelf()方法。

3、onStartCommand方法的返回值:

onStartCommand方法执行时,返回的是一个int型。这个整型可以有三个返回值:START_NOT_STICKY、START_STICKY、START_REDELIVER_INTENT

  • START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand方法后,服务被异常kill掉,系统不会自动重启该服务。
  • START_STICKY:如果Service进程被kill掉,保留Service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建Service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到Service,那么参数Intent将为null。
  • START_REDELIVER_INTENT:重传Intent。使用这个返回值时,系统会自动重启该服务,并将Intent的值传入。


part III     关于IntentService

intentService 会创建单独的线程来处理所有的intent请求。
IntentService为onBind() 方法提供了默认实现,默认实现的方法返回null;为onStartCommand()方法提供了默认实现,该方法实现了将请求intent添加到队列中;需自己实现onHandleIntent()方法,该方法中定义service需要完成的任务。
具体的 startService()会回调onStartCommand()方法,默认onStartCommand()方法将请求加入队列,接着用onHandleIntent()实现具体操作。

IntentService的引入:---平行于service,因为有优于service的属性而被使用

我们在第一段中就已经说了,服务中的代码默认运行在主线程中,如果直接在服务里执行一些耗时操作,容易造成ANR(Application Not Responding)异常,所以就需要用到多线程的知识了。

因此一个比较标准的服务可以这样写

复制代码
 1 package com.example.servicetest;  2  3 import android.app.Service;  4 import android.content.Intent;  5 import android.os.IBinder;  6  7 public class MyService extends Service {  8  9 public static final String TAG = "MyService"; 10 11 //服务执行的操作 12  @Override 13 public int onStartCommand(Intent intent, int flags, int startId) { 14 new Thread(new Runnable() { 15 public void run() { 16 //处理具体的逻辑 17 stopSelf(); //服务执行完毕后自动停止 18  } 19  }).start();  20 return super.onStartCommand(intent, flags, startId); 21  } 22 23  @Override 24 public IBinder onBind(Intent intent) { 25 // TODO Auto-generated method stub 26 return null; 27  } 28 29 }
复制代码

核心代码:14至19行,在子线程中处理具体的逻辑。

需要注意的是,如果没有第17行的stopSelf(),服务一旦启动后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来;所以我们添加了17行的stopSelf(),服务执行完毕后会自动停止

虽说上面的这种写法并不复杂,但总会有一些程序猿忘记开启线程,或者忘记调用stopSelf()方法。为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类,这个类就很好的解决了上面所提到的两种尴尬。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent()回调方法中执行,并且每次只会执行一个工作线程,执行完第一个后,再执行第二个,以此类推。

2、IntentService的作用:

当我们需要这样一次性完成的任务时,就可以使用IntentService来完成

3、IntentService的用法:

我们在上面的项目ServiceTest基础上进行修改,步骤如下:

(1)新建一个MyIntentService类,继承自IntentService,并重写父类的onHandleIntent()方法,代码如下:

复制代码
 1 package com.example.servicetest;  2  3 import android.app.IntentService;  4 import android.content.Intent;  5 import android.util.Log;  6  7 public class MyIntentService extends IntentService{  8  9 public MyIntentService() { 10 super("MyIntentService");//调用父类有参构造函数。这里我们手动给服务起个名字为:MyIntentService 11 // TODO Auto-generated constructor stub 12  } 13 14 //该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止 15  @Override 16 protected void onHandleIntent(Intent intent) { 17 // TODO Auto-generated method stub 18 for(int i = 0;i<3;i++) { 19 //打印当前线程的id 20 Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId()); 21 try { 22 Thread.sleep(1000); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block 25  e.printStackTrace(); 26  } 27  } 28  } 29 30  @Override 31 public void onDestroy() { 32 // TODO Auto-generated method stub 33 super.onDestroy(); 34 Log.d("MyIntentService","onDestroy"); 35  } 36 }
复制代码

这里首先要提供一个无参的构造方法,并且必须在其内部调用父类的有参构造方法(9至12行),我们在第10行手动将服务的名字改为“MyIntentService”。

然后在子类中实现onHandleIntent()这个抽象方法,可以在这个方法里去处理一些具体的逻辑,我们就用三次for循环,打印当前线程的id,每次延时1秒。

因为这个服务在运行结束后会自动停止,所以我们在onDestroy()方法中打印日志验证一下。

(2)在清单文件中对服务进行注册服务:

<service android:name=".MyIntentService"> service>

(3)在activity_main.xml中添加一个按钮button3_stop_intentservice,用于启动MyIntentService服务,代码略。

(4)在MainActivity里面加入启动IntentService的逻辑,核心代码如下:

1 case R.id.button3_stop_intentservice: 2 Log.d("MainActivity","主线程的id是:"+Thread.currentThread().getId()); 3 Intent intentService = new Intent(this,MyIntentService.class); 4  startService(intentService); 5 default:

我们在第02行中,打印主线程的id。

运行程序,点击按钮button3_stop_intentservice,显示如下:

由此可见,启动一个IntentService和启动一个普通的Service,步骤是一样的。

4、Service和Thread的关系:

不少Android初学者都可能会有这样的疑惑,Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案可能会有点让你吃惊,因为Service和Thread之间没有任何关系!

之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,你还会觉得它和Thread有什么关系吗?

其实,后台和子线程是两个完全不同的概念:

Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,Service既然是运行在主线程里,在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例;而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

所以说,一个比较标准的Service,就可以写成本段中第1节的样子。

part IV 

   使用Bind Service完成Service和Activity之间的通信


有没有什么办法能让它们俩的关联更多一些呢?比如说在Activity中指挥Service去干什么,Service就去干什么。当然可以,只需要让Activity和Service建立关联就好了。

1、Bind Service的介绍: ---所谓绑客户端定服务,指的是service.onBind()收到bindService函数发出的intent对象后返回对应的IBinder对象

应用程序组件(客户端)通过调用bindService()方法能够绑定服务,然后Android系统会调用服务的onBind()回调方法,则个方法会返回一个跟服务器端交互的Binder对象。

这个绑定是异步的,bindService()方法立即返回,并且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须创建一个ServiceConnection类的实例,并且把这个实例传递给bindService()方法。ServiceConnection对象包含了一个系统调用的传递IBinder对象的回调方法,即 onServiceConnected().

注意:只有Activity、Service、Content Provider能够绑定服务;BroadcastReceiver广播接收器不能绑定服务。

2、实现Service和Activity之间通信步骤:

我们依然在第二段中的项目ServiceTest基础上进行修改。

观察上面第二段中MyService中的代码,你会发现一直有一个onBind()方法我们都没有使用到,这个方法其实就是用于和Activity建立关联的,修改MyService中的代码,如下所示:

复制代码
 1 package com.example.servicetest;  2  3 import android.app.Service;  4 import android.content.Intent;  5 import android.os.Binder;  6 import android.os.IBinder;  7 import android.util.Log;  8  9 public class MyService extends Service { 10 11 public static final String TAG = "MyService"; 12 13 private MyBinder mBinder = new MyBinder();  14 15  @Override 16 public void onCreate() { 17 super.onCreate(); 18 Log.d(TAG, "onCreate"); 19  } 20 21  @Override 22 public int onStartCommand(Intent intent, int flags, int startId) { 23 Log.d(TAG, "onStartCommand"); 24 return super.onStartCommand(intent, flags, startId); 25  } 26 27  @Override 28 public void onDestroy() { 29 super.onDestroy(); 30 Log.d(TAG, "onDestroy"); 31  } 32 33  @Override 34 public IBinder onBind(Intent intent) { 35 return mBinder; //在这里返回新建的MyBinder类 36  } 37 38 //MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度 39 class MyBinder extends Binder { 40 41 public void startDownload() { 42 Log.d("TAG", "startDownload() executed"); 43 // 执行具体的下载任务  44  } 45 public int getProgress(){ 46 Log.d("TAG", "getProgress() executed"); 47 return 0; 48  } 49 50  } 51 52 }  
复制代码

38至50行:新建一个MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度。当然,这里只是两个模拟方法,并没有实现真正的功能,我们通过打印日志的形式来体现。

接着创建MyBinder的实例(13行),然后在onBind()方法里返回这个实例(35行)。

核心代码是35行,返回这个mBinder,是一个IBinder类型,就可以把这个IBinder类型传递到MainActivity中,从而调用Service里面的方法。下面就要看一看,在MainActivity是如何调用Service里面的两个方法的。

(2)检查清单文件,是否已经对Service进行注册:

  

(3)在activity_main.xml中继续添加两个按钮button3_bind_service和button4_unbind_service,用于绑定服务和取消绑定服务。最终,activity_main.xml的完整代码如下:

复制代码
<LinearLayout xmlns:android="" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1_start_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Service" /> <Button android:id="@+id/button2_stop_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Stop Service" /> <Button android:id="@+id/button3_bind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="bind Service" /> <Button android:id="@+id/button4_unbind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="unbind Service" /> LinearLayout> 
复制代码

(4)接下来再修改MainActivity中的代码,让MainActivity和MyService之间建立关联,代码如下所示:

复制代码
 package com.example.servicetest02;


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;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;


public class MainActivity extends Activity implements OnClickListener {
    private Button button1_start_service;
    private Button button2_stop_service;
    private Button button3_bind_service;
    private Button button4_unbind_service;
    private MyService.MyBinder myBinder;
    
    boolean mBound = false; //一开始,并没有和Service绑定.这个参数是用来显示绑定状态
    
    //匿名内部类:服务连接对象
    private ServiceConnection connection = new ServiceConnection() {
        
        //当服务异常终止时会调用。注意,解除绑定服务时不会调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBound = false; //服务异常终止时,状态为未绑定
        }
        
        //和服务绑定成功后,服务会回调该方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            //在Activity中调用Service里面的方法
            myBinder.startDownload();
            myBinder.getProgress();
            mBound = true; //true说明是绑定状态
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1_start_service = (Button) findViewById(R.id.button1_start_service);
        button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
        button3_bind_service = (Button) findViewById(R.id.button3_bind_service);
        button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
        button1_start_service.setOnClickListener(this);
        button2_stop_service.setOnClickListener(this);
        button3_bind_service.setOnClickListener(this);
        button4_unbind_service.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button1_start_service:
            Intent startIntent = new Intent(this, MyService.class);
            startService(startIntent);
            break;
        case R.id.button2_stop_service:
            Intent stopIntent = new Intent(this, MyService.class);
            stopService(stopIntent);
            break;
        case R.id.button3_bind_service:
            Intent bindIntent = new Intent(this, MyService.class);
            bindService(bindIntent, connection, BIND_AUTO_CREATE);
            break;
        case R.id.button4_unbind_service:
            //如果和Service是绑定的状态,就解除绑定。
            if(mBound){
                unbindService(connection);
                mBound=false;
            }
            break;


        default:
            break;
        }
    }
} 

可以看到,这里我们首先创建了一个ServiceConnection的匿名类(24行),在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,如果当前Activity与服务连接成功后,服务会回调onServiceConnected()方法,

在onServiceConnected()方法中,我们又通过向下转型得到了MyBinder的实例(34行),有了这个实例,Activity和Service之间的关系就变得非常紧密了。现在我们可以在Activity中根据具体的场景来调用MyBinder中的任何public方法(36、37行),即实现了Activity指挥Service干什么Service就去干什么的功能。

当然,现在Activity和Service其实还没关联起来了呢,这个功能是在Bind Service按钮的点击事件里完成的。可以看到,这里我们仍然是构建出了一个Intent对象,然后调用bindService()方法将Activity和Service进行绑定。bindService()方法接收三个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后会自动创建Service(即使之前没有创建Service也没有关系),这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。

然后如何我们想解除Activity和Service之间的关联怎么办呢?调用一下unbindService()方法就可以了,这也是Unbind Service按钮的点击事件里实现的逻辑。

现在让我们重新运行一下程序吧,在MainActivity中点击一下Bind Service按钮,LogCat里的打印日志如下图所示:

可以看到,只点击了Bind Service按钮,但是oncreate()方法得到了执行,而onStartCommand()方法不会执行。

另外需要注意,任何一个Service在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity建立关联,还可以和任何一个Activity建立关联,而且在建立关联时它们都可以获取到相同的MyBinder实例。

如何销毁Service:

根据上面第一段的知识,我们介绍了销毁Service最简单的一种情况:现在卸载程序,重新运行程序,点击Start Service按钮启动Service,再点击Stop Service按钮停止Service,这样MyService就被销毁了:

现在回到本段内容。卸载程序,重新开始。那么如果我们只点击的Bind Service按钮呢?由于在绑定Service的时候指定的标志位是BIND_AUTO_CREATE,说明点击Bind Service按钮的时候Service也会被创建,这时应该怎么销毁Service呢?其实也很简单,点击一下Unbind Service按钮,将Activity和Service的关联解除就可以了:

以上这两种销毁的方式都很好理解。那么如果我们既点击了Start Service按钮,又点击了Bind Service按钮会怎么样呢?这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将Unbind Service按钮和Stop Service按钮都点击一下(没有先后顺序),Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。

点击Unbind Service按钮后,再次点击Unbind Service按钮按钮引发的问题:

假设现在Service和Activity已经相关联了,点击Unbind Service按钮能够解除绑定,如果继续点击Unbind Service按钮,程序会异常退出,这说明代码不够完善,我们需要在代码中加一个判断是否绑定的标记mBound。在改MainActivity中增加一部分代码,最终改MainActivity的完整代码如下:(加粗字体是添加的内容)

复制代码
 1 package com.example.servicetest02;  2  3 import android.app.Activity;  4 import android.content.ComponentName;  5 import android.content.Intent;  6 import android.content.ServiceConnection;  7 import android.os.Bundle;  8 import android.os.IBinder;  9 import android.util.Log; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 14 public class MainActivity extends Activity implements OnClickListener { 15 private Button button1_start_service; 16 private Button button2_stop_service; 17 private Button button3_bind_service; 18 private Button button4_unbind_service; 19 private MyService.MyBinder myBinder; 20 21 boolean mBound = false; //一开始,并没有和Service绑定.这个参数是用来显示绑定状态 22 23 //匿名内部类:服务连接对象 24 private ServiceConnection connection = new ServiceConnection() { 25 26 //当服务异常终止时会调用。注意,解除绑定服务时不会调用 27  @Override 28 public void onServiceDisconnected(ComponentName name) { 29 mBound = false; //服务异常终止时,状态为未绑定 30  } 31 32 //和服务绑定成功后,服务会回调该方法 33  @Override 34 public void onServiceConnected(ComponentName name, IBinder service) { 35 myBinder = (MyService.MyBinder) service; 36 //在Activity中调用Service里面的方法 37  myBinder.startDownload(); 38  myBinder.getProgress(); 39 mBound = true; //true说明是绑定状态 40  } 41  }; 42  @Override 43 protected void onCreate(Bundle savedInstanceState) { 44 super.onCreate(savedInstanceState); 45  setContentView(R.layout.activity_main); 46 button1_start_service = (Button) findViewById(R.id.button1_start_service); 47 button2_stop_service = (Button) findViewById(R.id.button2_stop_service); 48 button3_bind_service = (Button) findViewById(R.id.button3_bind_service); 49 button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service); 50 button1_start_service.setOnClickListener(this); 51 button2_stop_service.setOnClickListener(this); 52 button3_bind_service.setOnClickListener(this); 53 button4_unbind_service.setOnClickListener(this); 54  } 55  @Override 56 public void onClick(View v) { 57 switch (v.getId()) { 58 case R.id.button1_start_service: 59 Intent startIntent = new Intent(this, MyService.class); 60  startService(startIntent); 61 break; 62 case R.id.button2_stop_service: 63 Intent stopIntent = new Intent(this, MyService.class); 64  stopService(stopIntent); 65 break; 66 case R.id.button3_bind_service: 67 Intent bindIntent = new Intent(this, MyService.class); 68  bindService(bindIntent, connection, BIND_AUTO_CREATE); 69 break; 70 case R.id.button4_unbind_service: 71 //如果和Service是绑定的状态,就解除绑定。 72 if(mBound){ 73  unbindService(connection); 74 mBound=false; 75  } 76 break; 77 78 default: 79 break; 80  } 81  } 82 }
复制代码

添加的代码是第21行、29行、72行至74行。

这样的话,连续点击Unbind Service按钮,就不会使程序出现异常。

3、started服务与bind服务的区别:

区别一:生命周期

  • 通过started方式的服务会一直运行在后台,需要由组件本身或外部组件来停止服务才会以结束运行
  • bind方式的服务,生命周期就要依赖绑定的组件

区别二:参数传递

  • started服务可以给启动的服务对象传递参数,但无法获取服务中方法的返回值
  • bind服务可以给启动的服务对象传递参数,也可以通过绑定的业务对象获取返回结果

实际开发中的技巧;

  • 第一次先使用started方式来启动一个服务
  • 之后可以使用bind的方式绑定服务,从而可以直接调用业务方法获取返回值

4、Service的生命周期:

一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onstartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于onstartCommand()方法执行。服务启动过后,会一直保持运行状态,直到stopService()或stopself()方法被调用。注意虽然每次调用一次startService()方法,onstartCommand()方法就会以执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopself()方法,服务就会停止。

另外,还可以调用Context的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后调用方可以获取到onBind()方法里返回的IBinder对象的实例,这样,就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。






part V 使用Bind Service完成IPC进程间通信
http://www.cnblogs.com/smyhvae/p/4070518.html                五 六 





阅读(1023) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~