展会信息港展会大全

android组件之service 实现机制
来源:互联网   发布日期:2016-01-13 22:13:54   浏览:1586次  

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

在远程的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

*

*verdateauthor

* ──────────────────────────────────

*2011-5-19Leon

*

* 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

*

* @authorLeon

* @version

* @sinceVer 1.1

* @Date2011-5-19

*/

public class RemoteServiceTransact extends Service {

private IBinder mBinder = null ;

private StringreplyString;

@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;

}

publicclass 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

*

*verdateauthor

* ──────────────────────────────────

*2011-6-21Leon

*

* 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

*

* @authorLeon

* @version

* @sinceVer 1.1

* @Date2011-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 ButtonbuttonRunService ;

private TextView textView ;

private IBinder iBinder;

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

LinearLayoutlayout=new LinearLayout(this);

layout.setOrientation(LinearLayout.VERTICAL);

this.setTitle("Test RunService..");

//定义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

*

*verdateauthor

* ──────────────────────────────────

*2011-6-22Leon

*

* 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 FromClinet : "+temp);

reply.writeString(temp);

return true;

case TRANSACTION_PAUSE:

this.pause();

Log.v(TAG,"The Message FromClinet : "+data.readString());

reply.writeString(data.readString());

return true;

case TRANSACTION_STOP:

this.stop();

Log.v(TAG,"The Message FromClinet :"+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;

publicProxy(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

*

*verdateauthor

* ──────────────────────────────────

*2011-6-22Leon

*

* 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

*

* @authorLeon

* @version

* @sinceVer 1.1

* @Date2011-6-22

*/

public class MyCustomRemoteBinder extends IMyMusicService.Stub{

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

赞助本站

人工智能实验室
AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港