上篇文章中我们谈到了,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. 通过这种远程代理的方式,使用者就会感觉到好像是在本地线程中执行程序一样了。

 

BroadcastReceiver的思考(6)_第1张图片

运行结果如下:

 

 

BroadcastReceiver的思考(6)_第2张图片

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

测试代码如下:

?[Copy to clipboard] Download RemoteMusicService.java
 
 
   
   
   
   
  1. /**  
  2.  * RemoteMusicService.java  
  3.  * com.androidtest.service.mediaplayer  
  4.  *  
  5.  * Function: TODO  
  6.  *  
  7.  *   ver     date           author  
  8.  * ──────────────────────────────────  
  9.  *           2011-5-19      Leon  
  10.  *  
  11.  * Copyright (c) 2011, TNT All Rights Reserved.  
  12. */  
  13.    
  14. package com.androidtest.service;  
  15.    
  16. import com.androidtest.R;  
  17. import com.androidtest.parcelable.ParcelableObject;  
  18.    
  19. import android.app.Service;  
  20. import android.content.Intent;  
  21. import android.media.MediaPlayer;  
  22. import android.os.Binder;  
  23. import android.os.IBinder;  
  24. import android.os.Parcel;  
  25. import android.os.Parcelable;  
  26. import android.os.RemoteException;  
  27. import android.util.Log;  
  28.    
  29. /**  
  30.  * ClassName:RemoteMusicService  
  31.  * Function: TODO ADD FUNCTION  
  32.  * Reason:   TODO ADD REASON  
  33.  *  
  34.  * @author   Leon  
  35.  * @version  
  36.  * @since    Ver 1.1  
  37.  * @Date     2011-5-19  
  38.  */  
  39. public class RemoteServiceTransact extends Service {  
  40.    
  41.     private IBinder mBinder = null ;  
  42.     private String  replyString;  
  43.    
  44.     @Override  
  45.     public void onCreate() {  
  46.    
  47.         // TODO Auto-generated method stub  
  48.         super.onCreate();  
  49.         mBinder = new myBinder();  
  50.         Thread.currentThread().setName("Service Thread Name : " +  
  51.                 Thread.currentThread().getName()+"-chenaged");  
  52.    
  53.     }  
  54.    
  55.     @Override  
  56.     public IBinder onBind(Intent intent) {  
  57.    
  58.         // TODO Auto-generated method stub  
  59.         replyString = Thread.currentThread().getName();  
  60.         return mBinder;  
  61.    
  62.     }  
  63.    
  64.     public  class myBinder extends Binder{  
  65.    
  66.         @Override  
  67.         protected boolean onTransact(int code, Parcel data, Parcel reply,  
  68.                 int flags) throws RemoteException {  
  69.    
  70.             // TODO Auto-generated method stub  
  71.             reply.writeString(replyString +  " Binder Thread is :" + Thread.currentThread().getName());  
  72.             return true;  
  73.    
  74.         }  
  75.    
  76.     }  
  77.    
?[Copy to clipboard] Download RemoteServiceTransactActivity.java
 
 
   
   
   
   
  1. /**  
  2.  * RemoteServiceTransactActivity.java  
  3.  * com.androidtest.service  
  4.  *  
  5.  * Function: TODO  
  6.  *  
  7.  *   ver     date           author  
  8.  * ──────────────────────────────────  
  9.  *           2011-6-21      Leon  
  10.  *  
  11.  * Copyright (c) 2011, TNT All Rights Reserved.  
  12. */  
  13.    
  14. package com.androidtest.service;  
  15.    
  16. import com.androidtest.service.mediaplayer.IMusicService;  
  17.    
  18. import android.app.Activity;  
  19. import android.content.ComponentName;  
  20. import android.content.Context;  
  21. import android.content.Intent;  
  22. import android.content.ServiceConnection;  
  23. import android.os.Bundle;  
  24. import android.os.IBinder;  
  25. import android.os.Parcel;  
  26. import android.util.Log;  
  27. import android.view.View;  
  28. import android.widget.Button;  
  29. import android.widget.LinearLayout;  
  30. import android.widget.TextView;  
  31.    
  32. /**  
  33.  * ClassName:RemoteServiceTransactActivity  
  34.  * Function: TODO ADD FUNCTION  
  35.  * Reason:   TODO ADD REASON  
  36.  *  
  37.  * @author   Leon  
  38.  * @version  
  39.  * @since    Ver 1.1  
  40.  * @Date     2011-6-21  
  41.  */  
  42. public class RemoteServiceTransactActivity extends Activity {  
  43.     private static final String TAGRemoteServiceTransactActivity.class.getSimpleName();  
  44.     private final int WC=LinearLayout.LayoutParams.WRAP_CONTENT;  
  45.     private final int WP=LinearLayout.LayoutParams.FILL_PARENT;  
  46.     private Button  buttonRunService ;  
  47.     private TextView textView ;  
  48.     private IBinder iBinder;  
  49.     @Override  
  50.     protected void onCreate(Bundle savedInstanceState) {  
  51.         // TODO Auto-generated method stub  
  52.         super.onCreate(savedInstanceState);  
  53.         LinearLayout  layout=new LinearLayout(this);  
  54.         layout.setOrientation(LinearLayout.VERTICAL);  
  55.         this.setTitle("Test Run  Service..");  
  56.    
  57.         //定义Button  
  58.         buttonRunService=new Button(this);  
  59.         buttonRunService.setId(1);  
  60.         buttonRunService.setText("Run Service");  
  61.         buttonRunService.setOnClickListener(new View.OnClickListener() {  
  62.    
  63.             @Override  
  64.             public void onClick(View v) {  
  65.    
  66.                 // TODO Auto-generated method stub  
  67.                 Parcel sendParcel = Parcel.obtain();  
  68.                 Parcel replyParcel =Parcel.obtain();  
  69.                 try{  
  70.                     iBinder.transact(2, sendParcel, replyParcel, 0);  
  71.                     //先打印出Activity的主线程名 然后Service的主线程名更名然后返回,说明Service运行在不同  
  72.                                 //的进程中  
  73.                     textView.setText("Activity Thread Name is :" +Thread.currentThread().getName()  
  74.                             +" and "+replyParcel.readString());  
  75.                 }catch(Exception e){  
  76.                     e.printStackTrace();  
  77.                 }  
  78.    
  79.             }  
  80.         });  
  81.         //定义TextView  
  82.         textView = new TextView(this);  
  83.         textView.setText("Ready....");  
  84.         //加入到Layout中  
  85.         layout.addView(buttonRunService);  
  86.         layout.addView(textView);  
  87.         this.setContentView(layout);  
  88.    
  89.         this.bindService(new Intent("com.androidtest.service.RemoteServiceTransact")  
  90.                    , myServiceConnection, Context.BIND_AUTO_CREATE);  
  91.    
  92.     }  
  93.    
  94.     private ServiceConnection myServiceConnection = new ServiceConnection() {  
  95.    
  96.         @Override  
  97.         public void onServiceConnected(ComponentName name, IBinder binder) {  
  98.             iBinder=binder;  
  99.             Log.d(TAG, " onServiceConnected");  
  100.         }  
  101.    
  102.         @Override  
  103.         public void onServiceDisconnected(ComponentName name) {  
  104.    
  105.             Log.d(TAG, " onServiceDisconnected");  
  106.         }  
  107.    
  108.     };  
  109.    

同时在Android Manifest中需要这样来定义这个Service,以便它能够在不同的进程中运行。

 

   
   
   
   
  1. <service android:enabled="true" android:process=":remote" 
  2.                                         android:name=".service.RemoteServiceTransact"> 
  3.      <intent-filter> 
  4.         <action android:name="com.androidtest.service.RemoteServiceTransact" /> 
  5.      intent-filter> 
  6.  service>