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

       本文主要介绍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用起来不难,但是理解其中的机制就有一定的难度了,尤其是远程调用一定要对远程代理有深刻的认识才行。本系列设计的源码很多,如果有人需要请加最牛网的群找傻蛋索要。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值