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