Android学习笔记二十四.Service入门(二)绑定本地Service并与之通信

绑定本地Service并与之通信

   通过上一篇博文的前3步,我们就算完成了一个Service及使用该Service的应用程序(Service为该应用程序的组成部分)。但当程序通过startService()和stopService()启动、关闭Service时,Service与访问者之间基本上不存在太多的关联,因此Service和访问者之间也无法进行通信、数据交换。如果我们希望开发的Service能与访问者之间实现方法调用或数据交换,我们可以让访问者使用bindService()和unbindService()方法启动、关闭Service,从而实现访问者与本地Service之间的绑定。

1.启动Service方法

Context.bindService(Intent service,ServiceConnection conn,int flags)

参数说明:

service:该参数用于设置Activity通过Intent指定启动哪个Service;

conn:该参数是一个ServiceConnection对象,该对象用于监听访问者与Service之间的连接情况。当访问者与Service之间连接成功时将回调该ServiceConnection对象的onServiceConnected(ComponentName
name,IBinder service)方法;当Service所在的宿主进程由于异常中止或由于其他原因终止,导致该Service与访问者之间断开连接时回调ServiceConnection对象的onServiceDisconnected(ComponentName
name)方法。

注意:当调用者主动通过unBindService()方法断开与Service的连接时,ServiceConnection对象的onServiceDisconnected(ComponentName name)方法并不会被调用。

flags:指定绑定时是否自动创建Service(如果Service还未创建)。flags=0,不自动创建;flags=BIND_AUTO_CREATE,自动创建。

2.源码分析

(1)在Service子类中,通过继承Binder的方式实现IBinder类并声明一个IBinder对象;

(2)当访问者绑定该Service后,Service通过onBind()方法返回一个IBinder对象给访问者;

(3)在访问者子类中,当访问者与Service连接成功将回调ServiceConnection对象的onServiceConnected(ComponentName name,IBinder service)方法来获取Service的onBind方法所返回的MyBinder对象;,该IBinder对象可访问该Service状态数据,即count的值。

3.源码实战

(1)/com/exanple/android_service_bind/BindService.java

功能:实现一个Service子类,并再其实现一个IBinder内部类和一个线程

package com.example.android_service_bind;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class BindService extends Service {
 private int count;
 private boolean quit;
 private MyIBinder binder=new MyIBinder();	//声明一个IBinder对象
 //1.定义一个IBinder子类并实现一个获取Service运行状态(count)的方法
 public class MyIBinder extends Binder
 {
  public int getCount()
  {
   return count;	 //返回Service运行状态:count
  }
 }
 //2.Service子类必须实现的一个类,用于返回IBinder对象
 public IBinder onBind(Intent intent) {
  System.out.println("Service is Binded!");
  return binder;	//返回IBinder对象
 }
 //3.Service被创建时回调该方法
 @Override
 public void onCreate() {
  super.onCreate();
  System.out.println("Service is Created.");
  //创建并启动一个线程,实现动态修改count状态值
  new Thread()
  {
   @Override
   public void run()
   {
    while(!quit)	//标识Service关闭启动状态
    {
     try
     {
      Thread.sleep(1000);
     }catch(InterruptedException e)
     {
     }
     count++;
    }
   }
  }.start();
 }
 //4.Service被断开连接时回调方法
 @Override
 public boolean onUnbind(Intent intent) {
  System.out.println("Service is Unbinded");
  return true;
 }
 //5.Service被关闭之前回调该方法
 @Override
 public void onDestroy() {
  super.onDestroy();
  this.quit=true;	 //当调用该方法后(!quit)为假,线程结束
  System.out.println("Service is Destroy");
 }
}

(2)AndroidManifest.xml

实现:为Service子类配置一个Service组件,并为该Service组件的intent-filter配置action

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android_service_bind"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".BindServiceTest"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       <!-- 配置一个Service组件 -->
       <service android:name=".BindService">
        <intent-filter>
             <!-- 为该Service组件的intent-filter配置action -->
             <action android:name="com.example.service.BIND_SERVICE"/>
        </intent-filter>
       </service>
      </application>
</manifest>

(3)/com/exanple/android_service_bind/BindServiceTest.java

实现:定义一个ServiceConnection对象,通过该对象的onServiceConnected()方法获取Service返回的IBinder对象,并通过Intent对象启动和绑定指定Service。

package com.example.android_service_bind;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class BindServiceTest extends Activity {
 Button bind,unbind,status;
 //1.保持所启动的Service的IBinder对象
 BindService.MyIBinder binder;
 //2.定义一个ServiceConnection对象
 private ServiceConnection conn = new ServiceConnection()
 {
  //a.当该Activity与Service连接成功时回调该方法
  @Override
  public void onServiceConnected(ComponentName name
    , IBinder service)
  {
   System.out.println("---Service is connected---");
   //获取Service的onBind方法所返回的MyBinder对象
   binder=(BindService.MyIBinder)service;
  }
  //b.当该Activity与Service连接不成功时回调该方法
  @Override
  public void onServiceDisconnected(ComponentName name) {
   System.out.println("---Service is Disconnected---");
  }
 };
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  //a.获取程序界面中的start、stop、getServiceStatus按钮
  bind = (Button)findViewById(R.id.bind);
  unbind = (Button)findViewById(R.id.unbind);
  status = (Button)findViewById(R.id.getServiceStatus);
  //b.创建启动Service的Intent
  final Intent intent = new Intent();
  intent.setAction("com.example.service.BIND_SERVICE");
  //c.绑定指定Service
  bind.setOnClickListener(new OnClickListener(){
   @Override
   public void onClick(View v) {
    bindService(intent,conn,Service.BIND_AUTO_CREATE);
   }
  });
  //d.解除绑定的Service
  unbind.setOnClickListener(new OnClickListener(){
   @Override
   public void onClick(View v) {
    unbindService(conn);
   }
  });
  //e.获取Service的状态,显示Service的count值
  status.setOnClickListener(new OnClickListener(){
   @Override
   public void onClick(View v) {
    Toast.makeText(BindServiceTest.this,"Service的count值为:"+binder.getCount()
       , Toast.LENGTH_SHORT).show();
   }
  });
 }

}

(4)主界面布局/res/layout/main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 <LinearLayout
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <Button
         android:id="@+id/bind"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:text="绑定service"/>
      <Button
         android:id="@+id/unbind"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:text="解除绑定"/>
       <Button
         android:id="@+id/getServiceStatus"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:text="获取状态"/>
    </LinearLayout>
</LinearLayout>

效果演示:

(1)当点击绑定Service时,访问者调用bindService()方法启动指定Service并与之绑定。观察DDMS的LogCat:

(2)当点击获取Service状态时,访问者通过IBinder对象binder调用Service内部类MyIBinder的getCount()方法获取count值;

(3)当点击解除绑定后,访问者通过unbindService(ServiceConnection conn) 方法解除对某个Service的绑定时,系统会先回调该Service的onUnbind()方法,然后再回调onDestroy()方法,Service服务被关闭。

注意:这里所谓的Service状态,实际上就是在Service服务中运行的实现count值累加的一个线程(在Service的onCreate()方法中实现)。当访问者调用bindService()方法启动并绑定该Service后,这个线程开始运行且count不停的进行累加1,直到访问者解除Service的绑定。


升华笔记:关于IBinder对象?

IBinder对象相当于Service组件的内部钩子,该钩子关联到绑定的Service组件,当其他程序组件绑定该Service时,Service子类将会把IBinder对象并返回给其他程序组件,其他程序组件通过该IBinder对象即可与Service组件进行实时通信。

出生:Service采用继承Binder(IBinder的实现类)的方法实现自己的IBinder对象,由Service提供的IBinder
onBinder(Intent intent)方法返回该IBinder对象,

去向:其他程序组件调用该ServiceConnection对象的onServiceConnected(ComponentName name,IBinder
service)方法时,传入Service组件返回的IBinder对象,从而实现了其他程序组件与被绑定的Service之间的通信。


参考:http://wear.techbrood.com/reference/android/app/Service.html

时间: 2024-12-27 21:06:16

Android学习笔记二十四.Service入门(二)绑定本地Service并与之通信的相关文章

Android学习笔记(十四)——在运行时添加碎片(附源码)

在运行时添加碎片 点击获取源码 将UI分割为多个可配置的部分是碎片的优势之一,但其真正强大之处在于可在运行时动态地把它们添加到活动中. 1.使用上一篇创建的Fragments项目,在main.xml文件中注释掉两个<fragment>元素: 2.在FragmentActivity.java中添加下面的代码: FragmentManager fragmentManager = getSupportFragmentManager();//向活动添加碎片 FragmentTransaction fr

【转】Pro Android学习笔记(十四):用户界面和控制(2):Text类控制

目录(?)[-] TextView 例子1在XML中设置autoLink属性 例子2在代码中设置autoLink属性 EditText AutoCompleteTextView MultiAutoCompleteTextView TextView TextView之前已经使用过很多,直接显示,比较简单.但是我们可以同“autoLink”属性,使用户可以点击一个网络连接.电话号码.邮箱地址.地图地址,通过系统应用打开它们.除此之外,还有其他的font属性,minLines,maxLines等等,都

Android学习笔记(十四)——自定义广播

//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 我们除了可以通过广播接收器来接收系统广播, 还可以在应用程序中发送自定义的广播.下面我们来分别试一试发送自定义的标准广播和有序广播. 一.标准广播: 1)在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行: 1 public class MyBroadcastReceiver extends BroadcastReceiver { 2 @Override 3 public void onReceiv

Android学习笔记(十四) Handler理论补充

一.如何下载Android源码 在SDK Manager中选中Sources for Android SDK. 二.ThreadLocal初步介绍 1)执行ThreadLocal对象(static final ThreadLocal sThreadLocal)的set(Object obj)方法,将会向存入一个以当前线程为键的键值对. 2)执行ThreadLocal对象的get()方法,将会根据当前线程对象为键,取出与之对应的值. 在之前提到的Looper方法调用中: 执行Looper.prep

Android学习笔记(十四)——在执行时加入碎片(附源代码)

在执行时加入碎片 点击获取源代码 将UI切割为多个可配置的部分是碎片的优势之中的一个,但其真正强大之处在于可在执行时动态地把它们加入到活动中. 1.使用上一篇创建的Fragments项目,在main.xml文件里凝视掉两个<fragment>元素: 2.在FragmentActivity.java中加入以下的代码: FragmentManager fragmentManager = getSupportFragmentManager();//向活动加入碎片 FragmentTransactio

Android学习笔记(十四)

Android中的数据存储 数据持久化就是指那些内存中的瞬时数据保存到存储设备中.Android系统中主要提供了三种方式用于简单地实现 数据持久功能,即文件存储.SharedPreferences存储和数据库存储. 1.文件存储是Android中一种基本的存储方式,它不对存储的内容进行任何的格式化处理,所有的数据是原封不动的 保存到文件中. 1.1 Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件. 这个方法接收两个参数: 1.第一个参数是文件名,

汇编入门学习笔记 (十四)—— 直接定址表

疯狂的暑假学习之  汇编入门学习笔记 (十四)-- 直接定址表 参考: <汇编语言> 王爽 第16章 1. 描述单元长度的标号 普通的标号:a,b assume cs:code code segment a:db 1,2,3,4,5,6,7,8 b:dw 0 start: mov si,offset a mov di,offset b mov ah,0 mov cx,8 s: mov al,cs:[si] add cs:[di],ax inc si loop s mov ax,4c00h in

Android学习笔记(十二)——使用意图传递数据的几种方式

使用意图传递数据的几种方式 点此获取完整代码 我们除了要从活动返回数据,也常常要传递数据给活动.对此我们可以使用Intent对象将这些数据传递给目标活动. 1.创建一个名为PassingData的项目,在activity_main.xml文件中添加一个Button: <Button android:id="@+id/btn_SecondActivity" android:layout_width="fill_parent" android:layout_hei

UI学习笔记---第十四天数据持久化

一.沙盒机制 每个应用程序位于文件系统的严格限制部分 每个应用程序只能在为该程序创建的文件系统中读取文件 每个应用程序在iOS系统内斗放在了统一的文件夹目录下 沙盘路径的位置 1. 通过Finder查找程序沙盘相对路径 ~/Library/Application Support/iPhone Simulator 2. 通过代码查找程序沙盘相对路径 NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory,NSSearc

《Javascript权威指南》学习笔记之十四:JavaScript内建类

前面的几篇博文分别介绍了对象.字符串.数组.日期等内建类,本篇将介绍Boolean/Math/Function/Arguments类 一.使用Boolean类处理逻辑值 Boolean类是JS的一个封装类,可以用于获取Boolean对象的原始数据类型或者字符串表示形式.new Boolean(value)用于创建一个Boolean对象,Boolean(value)它的参数转换成一个原始的布尔值,并且返回这个值.Boolean对象只有两个值:true或者false. value参数可以省略.如果省