欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android Activity与Service通信(不同进程之间)详解

程序员文章站 2024-03-04 12:43:17
在android中,activity主要负责前台页面的展示,service主要负责需要长期运行的任务,所以在我们实际开发中,就会常常遇到activity与service之间...

在android中,activity主要负责前台页面的展示,service主要负责需要长期运行的任务,所以在我们实际开发中,就会常常遇到activity与service之间的通信,我们一般在activity中启动后台service,通过intent来启动,intent中我们可以传递数据给service,而当我们service执行某些操作之后想要更新ui线程,我们应该怎么做呢?接下来我就介绍三种方式来实现service与activity之间的通信问题

activity与service通信的方式有三种:

 继承binder类

  这个方式只有当你的acitivity和service处于同一个application和进程时,才可以用,比如你后台有一个播放背景音乐的service,这时就可以用这种方式来进行通信。

用例子来说明其使用方法:

  1. 来看service的写法:

 public class localservice extends service { 
  // 实例化自定义的binder类 
  private final ibinder mbinder = new localbinder(); 
  // 随机数的生成器 
  private final random mgenerator = new random(); 
 
  /** 
   * 自定义的binder类,这个是一个内部类,所以可以知道其外围类的对象,通过这个类,让activity知道其service的对象 
   */ 
  public class localbinder extends binder { 
    localservice getservice() { 
      // 返回activity所关联的service对象,这样在activity里,就可调用service里的一些公用方法和公用属性 
      return localservice.this; 
    } 
  } 
 
  @override 
  public ibinder onbind(intent intent) { 
    return mbinder; 
  } 
 
  /** public方法,activity可以进行调用 */ 
  public int getrandomnumber() { 
   return mgenerator.nextint(100); 
  } 
} 
 

   在service里定义一个内部类,binder的子类,通过这个类,把service的对象传给activity,这样activity就可以调用service里的公用方法和公用属性了,但这种方式,一定要在同一个进程和同一个application里。

   2. 再看相应activity的代码: 

public class bindingactivity extends activity { 
  localservice mservice; 
  boolean mbound = false; 
 
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.main); 
  } 
 
  @override 
  protected void onstart() { 
    super.onstart(); 
    // 绑定service,绑定后就会调用mconnetion里的onserviceconnected方法 
    intent intent = new intent(this, localservice.class); 
    bindservice(intent, mconnection, context.bind_auto_create); 
  } 
 
  @override 
  protected void onstop() { 
    super.onstop(); 
    // 解绑service,这样可以节约内存 
    if (mbound) { 
      unbindservice(mconnection); 
      mbound = false; 
    } 
  } 
 
  /** 用户点击button,就读取service里的随机数 */ 
  public void onbuttonclick(view v) { 
    if (mbound) { 
      // 用service的对象,去读取随机数 
      int num = mservice.getrandomnumber(); 
      toast.maketext(this, "number: " + num, toast.length_short).show(); 
    } 
  } 
 
  /** 定交serviceconnection,用于绑定service的*/ 
  private serviceconnection mconnection = new serviceconnection() { 
 
    @override 
    public void onserviceconnected(componentname classname, 
        ibinder service) { 
      // 已经绑定了localservice,强转ibinder对象,调用方法得到localservice对象 
      localbinder binder = (localbinder) service; 
      mservice = binder.getservice(); 
      mbound = true; 
    } 
 
    @override 
    public void onservicedisconnected(componentname arg0) { 
      mbound = false; 
    } 
  }; 
} 

    这里就是通过ibinder来得到localservice对象,再去调用其public方法。

使用messenger

   上面的方法只能在同一个进程里才能用,如果要与另外一个进程的service进行通信,则可以用messenger。

    其实实现ipc的方式,还有aidl,但推荐使用messenger,有两点好处:

      1. 使用messenger方式比使用aidl的方式,实现起来要简单很多

      2. 使用messenger时,所有从activity传过来的消息都会排在一个队列里,不会同时请求service,所以是线程安全的。如果

你的程序就是要多线程去访问service,就可以用aidl,不然最好使用messenger的方式。

  不过,其实messenger底层用的就是aidl实现的,看一下实现方式,先看service的代码:

 public class messengerservice extends service { 
  /** 用于handler里的消息类型 */ 
  static final int msg_say_hello = 1; 
 
  /** 
   * 在service处理activity传过来消息的handler 
   */ 
  class incominghandler extends handler { 
    @override 
    public void handlemessage(message msg) { 
      switch (msg.what) { 
        case msg_say_hello: 
          toast.maketext(getapplicationcontext(), "hello!", toast.length_short).show(); 
          break; 
        default: 
          super.handlemessage(msg); 
      } 
    } 
  } 
 
  /** 
   * 这个messenger可以关联到service里的handler,activity用这个对象发送message给service,service通过handler进行处理。 
   */ 
  final messenger mmessenger = new messenger(new incominghandler()); 
 
  /** 
   * 当activity绑定service的时候,通过这个方法返回一个ibinder,activity用这个ibinder创建出的messenger,就可以与service的handler进行通信了 
   */ 
  @override 
  public ibinder onbind(intent intent) { 
    toast.maketext(getapplicationcontext(), "binding", toast.length_short).show(); 
    return mmessenger.getbinder(); 
  } 
} 
 

 再看一下activity的代码:

public class activitymessenger extends activity { 
  /** 向service发送message的messenger对象 */ 
  messenger mservice = null; 
 
  /** 判断有没有绑定service */ 
  boolean mbound; 
 
  private serviceconnection mconnection = new serviceconnection() { 
    public void onserviceconnected(componentname classname, ibinder service) { 
      // activity已经绑定了service 
      // 通过参数service来创建messenger对象,这个对象可以向service发送message,与service进行通信 
      mservice = new messenger(service); 
      mbound = true; 
    } 
 
    public void onservicedisconnected(componentname classname) { 
      mservice = null; 
      mbound = false; 
    } 
  }; 
 
  public void sayhello(view v) { 
    if (!mbound) return; 
    // 向service发送一个message 
    message msg = message.obtain(null, messengerservice.msg_say_hello, 0, 0); 
    try { 
      mservice.send(msg); 
    } catch (remoteexception e) { 
      e.printstacktrace(); 
    } 
  } 
 
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.main); 
  } 
 
  @override 
  protected void onstart() { 
    super.onstart(); 
    // 绑定service 
    bindservice(new intent(this, messengerservice.class), mconnection, 
      context.bind_auto_create); 
  } 
 
  @override 
  protected void onstop() { 
    super.onstop(); 
    // 解绑 
    if (mbound) { 
      unbindservice(mconnection); 
      mbound = false; 
    } 
  } 
} 

 注意:以上写的代码只能实现从activity向service发送消息,如果想从service向activity发送消息,只要把代码反过来写就可以了。

  使用aidl

    aidl,android interface definition language。建立aidl服务要比建立普通的服务复杂一些,具体步骤如下:

   (1)在eclipse android工程的java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于java代码,但会稍有不同。详细介绍见实例的内容。

   (2)如果aidl文件的内容是正确的,adt会自动生成一个java接口文件(*.java)。

   (3)建立一个服务类(service的子类)。

   (4)实现由aidl文件生成的java接口。

   (5)在androidmanifest.xml文件中配置aidl服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的id,也就是intent类的参数值。

          感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!