android基础知识05:四大组件之service 03:实现机制

本文详细介绍了Android中Service的远程调用机制,包括进程间通讯原理、远程代理的作用及其实现方式,并通过示例代码展示了如何进行跨进程的服务调用。

本文主要介绍service相关内容。包括两篇文章:

android基础知识05:四大组件之service 01

android基础知识05:四大组件之service 02:远程调用

android基础知识05:四大组件之service 03:实现机制

本文介绍service的实现机制。内容来源于最牛网

在远程的Service调用中,Activity和Service到底是怎么沟通的?对于Service的远程调用,一般会在不同的工程中也就是两个不同的进程,那么进程的沟通机制是什么?傻蛋画了一个图来说明。


Android进程在产生时会:1.产生一个主线程。2. 产生Looper对象 3.产生一个消息队列。4.产生一个虚拟机对象来实现Java和C++之间的沟通。通过C/C++层的IPC来实现通讯。
进程间通讯:Android通过IBinder接口来实现进程间的通讯,MyActivity会调用IBinder的transact()函数通过IPC来调用远程的onTransact()函数。

在默认情况下,如果Servie和Activity、BroadcastReceiver在同一个工程里面,那么这些组件都会在同一个进程中执行,并且由主线程负责执行,当然也可以通过配置让其在不同的组件里面执行。

上文中我们谈到了,Service的远程沟通,既当Activity和Service不在一个进程中,它们之间是怎么相互通信的,不过只是停留在原理层面,今天傻蛋写了一个测试程序来进一步说明远程沟通机制。
Android框架的IPC沟通其实是依赖单一的IBinder接口,当Activity端呼叫IBinder接口的transact()函数时,就会透过IPC机制来呼叫远端的onTransact()函数。当调用transact函数之后,Android框架会根据线程的同步机制,等待远端的onTransact()函数执行完毕 并且返回,才继续向下面继续执行。傻蛋画了副图来进一步说明这个过程。
1. JavaBBinder是Android系统底层提供的类,其实它是个代理(代理模式),同时实现了IBinder接口,这样它onTransact()函数就能够调用myBinder的onTransact()函数
2. 当RemoteServiceTransactActivity需要夸进程来执行JavaBBinder的对象时,Android框架从RemoteServiceTransact所在的进程中启动一个线程Thread X来配合Thread A的执行,这样就变成进程内的通信了。
3. 通过这种远程代理的方式,使用者就会感觉到好像是在本地线程中执行程序一样了。


运行结果如下:


从结果我们可以看出Activity运行的线程名是main,Service运行的线程名也是main,不过和前面Activity的那个main不是一个,为了证明这一点我把它改名为main-changed,Binder所运行的线程名为Binder Thread #2 ,用来配合Activity主线程的远程调用。
测试代码如下:

RemoteMusicService.java

/** * RemoteMusicService.java * com.androidtest.service.mediaplayer * * Function: TODO * * ver date author * ────────────────────────────────── * 2011-5-19 Leon * * Copyright (c) 2011, TNT All Rights Reserved. */ package com.androidtest.service; import com.androidtest.R; import com.androidtest.parcelable.ParcelableObject; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; /** * ClassName:RemoteMusicService * Function: TODO ADD FUNCTION * Reason: TODO ADD REASON * * @author Leon * @version * @since Ver 1.1 * @Date 2011-5-19 */ public class RemoteServiceTransact extends Service { private IBinder mBinder = null ; private String replyString; @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); mBinder = new myBinder(); Thread.currentThread().setName("Service Thread Name : " + Thread.currentThread().getName()+"-chenaged"); } @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub replyString = Thread.currentThread().getName(); return mBinder; } public class myBinder extends Binder{ @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { // TODO Auto-generated method stub reply.writeString(replyString + " Binder Thread is :" + Thread.currentThread().getName()); return true; } } }RemoteServiceTransactActivity.java

/** * RemoteServiceTransactActivity.java * com.androidtest.service * * Function: TODO * * ver date author * ────────────────────────────────── * 2011-6-21 Leon * * Copyright (c) 2011, TNT All Rights Reserved. */ package com.androidtest.service; import com.androidtest.service.mediaplayer.IMusicService; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; /** * ClassName:RemoteServiceTransactActivity * Function: TODO ADD FUNCTION * Reason: TODO ADD REASON * * @author Leon * @version * @since Ver 1.1 * @Date 2011-6-21 */ public class RemoteServiceTransactActivity extends Activity { private static final String TAG= RemoteServiceTransactActivity.class.getSimpleName(); private final int WC=LinearLayout.LayoutParams.WRAP_CONTENT; private final int WP=LinearLayout.LayoutParams.FILL_PARENT; private Button buttonRunService ; private TextView textView ; private IBinder iBinder; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); LinearLayout layout=new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); this.setTitle("Test Run Service.."); //定义Button buttonRunService=new Button(this); buttonRunService.setId(1); buttonRunService.setText("Run Service"); buttonRunService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Parcel sendParcel = Parcel.obtain(); Parcel replyParcel =Parcel.obtain(); try{ iBinder.transact(2, sendParcel, replyParcel, 0); //先打印出Activity的主线程名 然后Service的主线程名更名然后返回,说明Service运行在不同 //的进程中 textView.setText("Activity Thread Name is :" +Thread.currentThread().getName() +" and "+replyParcel.readString()); }catch(Exception e){ e.printStackTrace(); } } }); //定义TextView textView = new TextView(this); textView.setText("Ready...."); //加入到Layout中 layout.addView(buttonRunService); layout.addView(textView); this.setContentView(layout); this.bindService(new Intent("com.androidtest.service.RemoteServiceTransact") , myServiceConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection myServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { iBinder=binder; Log.d(TAG, " onServiceConnected"); } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG, " onServiceDisconnected"); } }; }同时在Android Manifest中需要这样来定义这个Service,以便它能够在不同的进程中运行。

<service android:enabled="true" android:process=":remote" android:name=".service.RemoteServiceTransact"> <intent-filter> <action android:name="com.androidtest.service.RemoteServiceTransact" /> </intent-filter> </service> 自动生成的代码可以发现,这段代码其实是帮我们实现了一个代理类,这个代理能够通过进行不同进程间的通信。我们Activity调用这个代理类,而这个调用远程Service中的方法。从而实现了远程通信。
今天傻蛋自己写了一个底层远程通信的代理类(具体通信方式傻蛋在第6篇中已经讲了),同样实现了远程调用Service的效果。基本结构和ADT自动生成的代码相似,但是清晰了很多,使用方法和第四篇中的使用一某一样,十分通用。


IMyMusicService.java

/** * IMusicService.java * com.androidtest.service.mediaplayer * * Function: TODO * * ver date author * ────────────────────────────────── * 2011-6-22 Leon * * Copyright (c) 2011, TNT All Rights Reserved. */ package com.androidtest.service.remoteplayer; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.util.Log; /** * ClassName:IMusicService Function: TODO ADD FUNCTION Reason: TODO ADD REASON * * @author Leon * @version * @since Ver 1.1 * @Date 2011-6-22 */ public interface IMyMusicService { static final String TAG = IMyMusicService.class.getSimpleName(); static final int TRANSACTION_PLAY = android.os.IBinder.FIRST_CALL_TRANSACTION + 0; static final int TRANSACTION_PAUSE = android.os.IBinder.FIRST_CALL_TRANSACTION + 1; static final int TRANSACTION_STOP = android.os.IBinder.FIRST_CALL_TRANSACTION + 2; public void play() throws RemoteException ; public void pause()throws RemoteException ; public void stop() throws RemoteException ; public static abstract class Stub extends Binder implements IMyMusicService { //用于Service的接收 @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { // TODO Auto-generated method stub switch (code) { case TRANSACTION_PLAY: this.play(); /** * 注意 readString 是这样解释 * Read a string value from the parcel at the current dataPosition(). * 第二次读的话data position就会前移,此处就为null了 */ String temp = data.readString(); Log.v(TAG,"The Message From Clinet : "+temp); reply.writeString(temp); return true; case TRANSACTION_PAUSE: this.pause(); Log.v(TAG,"The Message From Clinet : "+data.readString()); reply.writeString(data.readString()); return true; case TRANSACTION_STOP: this.stop(); Log.v(TAG,"The Message From Clinet :"+data.readString()); reply.writeString(data.readString()); return true; } return super.onTransact(code, data, reply, flags); } //模板模式 public abstract void play() throws RemoteException ; public abstract void pause() throws RemoteException ; public abstract void stop() throws RemoteException ; //其实就是获取代理类,供Activity来调用 public static IMyMusicService asInterface(IBinder iBinder){ return new Proxy(iBinder); } //Activity其实就是通过此代理和远程的Stub进行通信 private static class Proxy implements IMyMusicService{ private IBinder iBinder; public Proxy(IBinder iBinder) { this.iBinder =iBinder; } @Override public void pause() { Parcel sendData = Parcel.obtain(); Parcel replyData =Parcel.obtain(); sendData.writeString("pause"); try { iBinder.transact(TRANSACTION_PAUSE, sendData, replyData, 0); Log.v(TAG, "The Message From Service " + replyData.readString() ); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub } @Override public void play() { // TODO Auto-generated method stub Parcel sendData = Parcel.obtain(); Parcel replyData =Parcel.obtain(); sendData.writeString("play"); try { iBinder.transact(TRANSACTION_PLAY, sendData, replyData, 0); Log.v(TAG, "The Message From Service " + replyData.readString()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void stop() { // TODO Auto-generated method stub Parcel sendData = Parcel.obtain(); Parcel replyData =Parcel.obtain(); sendData.writeString("stop"); try { iBinder.transact(TRANSACTION_STOP, sendData, replyData, 0); Log.v(TAG, "The Message From Service " + replyData.readString() ); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }MyCustomRemoteBinder.java

/** * MyRemoteBinder.java * com.androidtest.service.remoteplayer * * Function: TODO * * ver date author * ────────────────────────────────── * 2011-6-22 Leon * * Copyright (c) 2011, TNT All Rights Reserved. */ package com.androidtest.service.remoteplayer; import android.media.MediaPlayer; import android.os.RemoteException; import com.androidtest.service.mediaplayer.MyMediaController; /** * ClassName:MyRemoteBinder * Function: TODO ADD FUNCTION * Reason: TODO ADD REASON * * @author Leon * @version * @since Ver 1.1 * @Date 2011-6-22 */ public class MyCustomRemoteBinder extends IMyMusicService.Stub{ public MyCustomRemoteBinder(MediaPlayer mediaPlayer){ MyMediaController.mediaPlayer=mediaPlayer ; }; @Override public void play() throws RemoteException { // TODO Auto-generated method stub MyMediaController.play.execute(); } @Override public void pause() throws RemoteException { // TODO Auto-generated method stub MyMediaController.pause.execute(); } @Override public void stop() throws RemoteException { // TODO Auto-generated method stub MyMediaController.stop.execute(); }; } 使用AIDL语言,来让ADT帮助我们自动生成一个Stub类(Binder的子类),来实现不同进程中Service的调用。通过研究ADT本篇是Android Service的思考(7)的完结篇,Service用起来不难,但是理解其中的机制就有一定的难度了,尤其是远程调用一定要对远程代理有深刻的认识才行。本系列设计的源码很多,如果有人需要请加最牛网的群找傻蛋索要。


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值