展会信息港展会大全

Android核心组件Service 详解
来源:互联网   发布日期:2016-01-19 11:13:10   浏览:3284次  

导读:在Android框架中,Service是比较难以理解的一部分,而网上的大多数资料最多就是讲述了如何去用Service,并没有对Service做一 个深入的、系统的讲解。现在傻蛋将做一个系列文章,将对Service做一个由浅入深的梳理 ...

在Android框架中,Service是比较难以理解的一部分,而网上的大多数资料最多就是讲述了如何去用Service,并没有对Service做一 个深入的、系统的讲解。现在傻蛋将做一个系列文章,将对Service做一个由浅入深的梳理,帮助大家深入的掌握Android Service。

首先我们先来看看怎么使用Service,然后再谈Android Service的内部机制。

在Android中Service的启动方式有两种,今天先发第一种。

Service的启动方式一:

启动:Context.startService(new Intent(context,xxx.class));

停止:Context.stopService() ;

我画了一个Service启动的流程图,相信大家一看就懂。Activity通过 Intent启动Service,如果Service还没有运 行,则android先调用onCreate()然后调用onStart();如果 Service已经运行,则只调用onStart(),所以一个 Service的onStart方法可能会重复调用多次。 调用stopService就会触发Service的onDestroy()方法。

这一节里面傻蛋做了一个示例程序是一个音乐播放器,界面如下,功能很简单:播放、暂停、停止音乐、关闭Activity(这时Service仍然运 行,继续播放音乐)、退出程序(停止音乐退出Activity)。

由于要控制Service的动作,傻蛋写了一个播放的通用类,这样在以后的课程中还能用上,代码如下:

/**

* MyMediaController.java

* com.androidtest.service.mediaplayer

*

* Function: TODO

*

*verdateauthor

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

*2011-5-16Leon

*

* Copyright (c) 2011, 最牛网 All Rights Reserved.

*/

package com.zuiniuwang;

import java.io.Serializable;

import android.media.MediaPlayer;

/**

* ClassName:MyMediaController

* Function: Mediaplayer 的一个控制类,控制播放器的播放 暂停 停止 等动作

* REASON

*

* @author Leon

* @version

* @since Ver 1.1

* @Date 2011-5-16

*/

public enum MyMediaController implements Serializable {

play {

@Override

public void execute() {

if (mediaPlayer != null && !mediaPlayer.isPlaying())

mediaPlayer.start();

// TODO Auto-generated method stub

}

},

pause {

@Override

public void execute() {

// TODO Auto-generated method stub

if (mediaPlayer != null && mediaPlayer.isPlaying()) {

mediaPlayer.pause();

}

}

},

stop {

@Override

public void execute() {

// TODO Auto-generated method stub

if (mediaPlayer != null) {

mediaPlayer.stop();

try {

// 在stop后如果要重新Start需要prepare一下

mediaPlayer.prepare();

// 从头播放

mediaPlayer.seekTo(0);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

};

public static MediaPlayer mediaPlayer;

public abstract void execute();

}

然后是控制播放的Activity:

/**

* MusicPlayer.java

* com.androidtest.activity

*

* Function: TODO

*

*verdateauthor

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

*2011-5-15Leon

*

* Copyright (c) 2011, 最牛网 All Rights Reserved.

*/

package com.androidtest.activity.musicplayer;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import com.androidtest.MyImageButton;

import com.androidtest.R;

import com.androidtest.service.mediaplayer.MyMediaController;

import com.androidtest.service.mediaplayer.NormalMusicService;

import com.androidtest.sharedpreferences.TestSharePreferences;

/**

* ClassName:MusicPlayer Function: TODO ADD FUNCTION Reason: TODO ADD REASON

*

* @author Leon

* @version

* @since Ver 1.1

* @Date 2011-5-15

*/

public class NormalMusicPlayerActivity extends Activity implements OnClickListener {

private static final String TAG = NormalMusicPlayerActivity.class.getSimpleName();

private Intent intent ;

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

this.setContentView(R.layout.music_player_layout);

Button playButton = (Button) this.findViewById(R.id.play);

playButton.setOnClickListener(this);

Button pauseButton =(Button) this.findViewById(R.id.pause);

pauseButton.setOnClickListener(this);

Button stopButton =(Button)this.findViewById(R.id.stop);

stopButton.setOnClickListener(this);

Button closeActivityButton =(Button)this.findViewById(R.id.close);

closeActivityButton.setOnClickListener(this);

Button exitActivityButton =(Button)this.findViewById(R.id.exit);

exitActivityButton.setOnClickListener(this);

intent = new Intent("com.androidtest.service.mediaplayer.NormalMusicService");

TestSharePreferences testSharePreferences=(TestSharePreferences)this.getApplication();

Log.v(TAG , ""+testSharePreferences.getSharedInteger());

}

private void playAction(MyMediaController playType) {

Bundle bundle = new Bundle();

bundle.putSerializable(NormalMusicService.INTENT_KEY, playType);

intent.putExtras(bundle);

NormalMusicPlayerActivity.this.startService(intent);

}

@Override

public void onClick(View view) {

// TODO Auto-generated method stub

switch (view.getId()) {

case R.id.play:

Log.d(TAG, "play.......");

playAction(MyMediaController.play);

break;

case R.id.pause:

Log.d(TAG, "pause.......");

playAction(MyMediaController.pause);

break;

case R.id.stop:

Log.d(TAG, "stop.......");

playAction(MyMediaController.stop);

break;

case R.id.close:

Log.d(TAG, "close.......");

this.finish();

break;

case R.id.exit:

Log.d(TAG, "exit.......");

stopService(intent);

this.finish();

}

}

}

最后是Service类

/**

* MusicService.java

* com.androidtest.service

*

* Function: TODO

*

*verdateauthor

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

*2011-5-15Leon

*

* Copyright (c) 2011, 最牛网 All Rights Reserved.

*/

package com.zuiniuwang.service;

import java.io.Serializable;

import android.app.Service;

import android.content.Intent;

import android.media.MediaPlayer;

import android.os.IBinder;

import android.util.Log;

import com.zuiniuwang.*;

/**

* ClassName:MusicService

* Function: TODO ADD FUNCTION

* Reason:TODO ADD REASON

*

* @authorLeon

* @version

* @sinceVer 1.1

* @Date2011-5-15

*/

public class NormalMusicService extends Service{

privateStringTAG = NormalMusicService.class.getSimpleName();

privateMediaPlayer myMediaPlayer ;

publicstatic finalString INTENT_KEY= "action" ;

@Override

public IBinder onBind(Intent arg0) {

// TODO Auto-generated method stub

return null;

}

@Override

public void onCreate() {

// TODO Auto-generated method stub

Log.v(TAG , TAG+ " onCreate()");

super.onCreate();

if(myMediaPlayer==null){

myMediaPlayer=MediaPlayer.create(this, R.raw.test) ;

myMediaPlayer.setLooping(false);

}

}

@Override

public void onStart(Intent intent, int startId) {

// TODO Auto-generated method stub

Log.v(TAG , TAG + " onStart()");

super.onStart(intent, startId);

if(intent!=null){

MyMediaController mediaControl =(MyMediaController)intent.getSerializableExtra(NormalMusicService.INTENT_KEY);

mediaControl.mediaPlayer=myMediaPlayer;

mediaControl.execute();

}

}

@Override

public void onDestroy() {

// TODO Auto-generated method stub

super.onDestroy();

Log.v(TAG , " onDestroy");

if(myMediaPlayer!=null){

myMediaPlayer.stop();

myMediaPlayer.release();

}

}

}

第二种是通过绑定的方式来启动Service。先看流程图。

点击 查看大图

绑定的方式和第一节的方式最大的不同就是在于,Activity可以和Service实现关联,当被关联的Activity结束后,相应的 Service 也会停止,同时在绑定了的Activity中我们还可以回调我们在Service中定义的方法。在这里我们使用了 this.bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE); 来启动Service,当Service创建了同时绑定了Activity之后,会回调我们定义的ServiceConnection(),从而传回 IBinder接口,我们就能够调用Service中的方法了。这时候Activity就和 Service实现了绑定,Activity退出了Service就相应的退出了。Service的申明如下,intent filter 是对接收Service的过滤。

<service android:enabled="true" android:name=".service.mediaplayer.BindMusicService">

<intent-filter>

<action android:name="com.androidtest.service.mediaplayer.BindMusicService" />

</intent-filter>

</service>

前两节中可以看到Activity和Service,context.startService对应着Service中的onStart()方 法,context.onBindService对应的是Service中的onBind()方法。当我们继想绑定一个Service又想 在 Activity停止时,Service不会停止,我们可以先StartService,然后再BindService()。这时候的流程图如下所 示:

点击查看 大图

此时需要注意一个问题,当Activity退出的时候,Sercvice并不会停止,此时我们可以再进入Activity重新绑定,当这时 候 Service就会调用onRebind()方法,但是调用onRebind()方法的前提是先前的onUnbind()方法执行成功,但是使 用 super.onUnbind(intent)是执行不成功的,这时候我们要手动的使其返回true,再次绑定时Rebind()就会执行。否则,如 果退出时不显示的指定onUnbind()为成功的话(为false),那么重新启动此Activity来绑定服务时,Service的onBind() 方法和onReBind都不会执行,但是ServiceConnection方法确一定会回调了。这说明在Service中的onBind()方法不同 于 onStart()方法不能被重复调用。

本节代码和上节代码大致相同,只不过是在bind之前先start service ,大家可以在DDMS中看 onUnbind() onRebind()的调用情况。

在Android平台中,一个进程通常不能访问其他进程中的内存区域的。但是,我们可以使用IDL语言来把对象伪装成操作系统能理解的简单形式,以 便伪装成对象跨越边界访问。

如果想在应用程序中调用其他进程中的Service,则需要用到AIDL,AIDL(android接口描述语言)是一种IDL语言,它可以生成一 段代码,可以使在一个android设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程中(例如:在一个Activity中)访问另一 个进程中(例如:一个Service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。

使用AIDL的方法如下:

1.首先要编写一个 IMusicService.aidl的服务接口,ADT会根据这个接口文件帮我们自动生成一个 Stub类,这个类继承了Binder类,同时继承了IMusicService这个接口,还可以看到其中包含了一个Proxy代理类,以实现远程代理, 访问不同的进程。(aidl和Stub类如下所示)。

/**

* IMusicService.aidl

* com.androidtest.service.mediaplayer

*

* Function: TODO

*

*verdateauthor

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

*2011-5-19Leon

*

* Copyright (c) 2011, TNT All Rights Reserved.

*/

package com.zuiniuwang.service;

/**

* ClassName:IMusicService

* Function: TODO ADD FUNCTION

* Reason:TODO ADD REASON

*

* @authorLeon

* @version

* @sinceVer 1.1

* @Date2011-5-19

*/

interface IMusicService{

void play();

void pause();

void stop();

}

2. 生成的Stub类如下,我们暂不做详细讲解,后面的课程中我们会尝试自己来写一个类似的类,完成不同进程的访问。

/*

* This file is auto-generated.DO NOT MODIFY.

* Original file: E:\\myworkspace\\musicservice4\\src\\com\\zuiniuwang\\service\\IMusicService.aidl

*/

package com.zuiniuwang.service;

/**

* ClassName:IMusicService

* Function: TODO ADD FUNCTION

* Reason:TODO ADD REASON

*

* @authorLeon

* @version

* @sinceVer 1.1

* @Date2011-5-19

*/

public interface IMusicService extends android.os.IInterface

{

/** Local-side IPC implementation stub class. */

public static abstract class Stub extends android.os.Binder implements com.zuiniuwang.service.IMusicService

{

private static final java.lang.String DESCRIPTOR = "com.zuiniuwang.service.IMusicService";

/** Construct the stub at attach it to the interface. */

public Stub()

{

this.attachInterface(this, DESCRIPTOR);

}

/**

* Cast an IBinder object into an com.zuiniuwang.service.IMusicService interface,

* generating a proxy if needed.

*/

public static com.zuiniuwang.service.IMusicService asInterface(android.os.IBinder obj)

{

if ((obj==null)) {

return null;

}

android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);

if (((iin!=null)&&(iin instanceof com.zuiniuwang.service.IMusicService))) {

return ((com.zuiniuwang.service.IMusicService)iin);

}

return new com.zuiniuwang.service.IMusicService.Stub.Proxy(obj);

}

public android.os.IBinder asBinder()

{

return this;

}

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException

{

switch (code)

{

case INTERFACE_TRANSACTION:

{

reply.writeString(DESCRIPTOR);

return true;

}

case TRANSACTION_play:

{

data.enforceInterface(DESCRIPTOR);

this.play();

reply.writeNoException();

return true;

}

case TRANSACTION_pause:

{

data.enforceInterface(DESCRIPTOR);

this.pause();

reply.writeNoException();

return true;

}

case TRANSACTION_stop:

{

data.enforceInterface(DESCRIPTOR);

this.stop();

reply.writeNoException();

return true;

}

}

return super.onTransact(code, data, reply, flags);

}

private static class Proxy implements com.zuiniuwang.service.IMusicService

{

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote)

{

mRemote = remote;

}

public android.os.IBinder asBinder()

{

return mRemote;

}

public java.lang.String getInterfaceDescriptor()

{

return DESCRIPTOR;

}

public void play() throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_play, _data, _reply, 0);

_reply.readException();

}

finally {

_reply.recycle();

_data.recycle();

}

}

public void pause() throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_pause, _data, _reply, 0);

_reply.readException();

}

finally {

_reply.recycle();

_data.recycle();

}

}

public void stop() throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);

_reply.readException();

}

finally {

_reply.recycle();

_data.recycle();

}

}

}

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 android.os.RemoteException;

public void pause() throws android.os.RemoteException;

public void stop() throws android.os.RemoteException;

}

3. 在Activity中得到Binder的方式,是通过Stub类的IMusicService.Stub.asInterface(binder)方法 (这一点和以前不同)。相应的代码如下:

/**

* RemoteMusicPlayerActivity.java

* com.androidtest.activity.musicplayer

*

* Function: TODO

*

*verdateauthor

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

*2011-5-20Leon

*

* Copyright (c) 2011, TNT All Rights Reserved.

*/

package com.zuiniuwang.playeractivity;

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.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import com.zuiniuwang.R;

import com.zuiniuwang.service.IMusicService;

/**

* ClassName:RemoteMusicPlayerActivity Function: TODO ADD FUNCTION Reason: TODO

* ADD REASON

*

* @author Leon

* @version

* @since Ver 1.1

* @Date 2011-5-20

*/

public class RemoteMusicPlayerActivity extends Activity implements

OnClickListener {

private static final String TAG = RemoteMusicPlayerActivity.class

.getSimpleName();

private Button playButton, pauseButton, stopButton, closeActivityButton,

exitActivityButton;

private IMusicService musicServiceInterface;

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

this.setContentView(R.layout.music_player_layout);

findViews();

bindViews();

connection();

}

private void findViews() {

playButton = (Button) this.findViewById(R.id.play);

pauseButton = (Button) this.findViewById(R.id.pause);

stopButton = (Button) this.findViewById(R.id.stop);

closeActivityButton = (Button) this.findViewById(R.id.close);

exitActivityButton = (Button) this.findViewById(R.id.exit);

}

private void bindViews() {

playButton.setOnClickListener(this);

pauseButton.setOnClickListener(this);

stopButton.setOnClickListener(this);

closeActivityButton.setOnClickListener(this);

exitActivityButton.setOnClickListener(this);

}

private void connection() {

Intent intent = new Intent(

"com.androidtest.service.mediaplayer.RemoteMusicService");

this.startService(intent);

this.bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);

}

private ServiceConnection myServiceConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder binder) {

musicServiceInterface = IMusicService.Stub.asInterface(binder);

Log.d(TAG, " onServiceConnected");

}

@Override

public void onServiceDisconnected(ComponentName name) {

musicServiceInterface = null;

Log.d(TAG, " onServiceDisconnected");

}

};

@Override

public void onClick(View view) {

// TODO Auto-generated method stub

try {

switch (view.getId()) {

case R.id.play:

Log.d(TAG, "play.......");

musicServiceInterface.play();

break;

case R.id.pause:

Log.d(TAG, "pause.......");

musicServiceInterface.pause();

break;

case R.id.stop:

Log.d(TAG, "stop.......");

musicServiceInterface.stop();

break;

case R.id.close:

//Activity退出之前要解除绑定,不然会报错

this.unbindService(myServiceConnection);

Log.d(TAG, "close.......");

this.finish();

break;

case R.id.exit:

Log.d(TAG, "exit.......");

this.unbindService(myServiceConnection);

this.stopService(new Intent("com.androidtest.service.mediaplayer.RemoteMusicService"));

this.finish();

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

4. 最后在此Service注册的时候我们需要指定它是在一个不同的进程中运行的,本例子指定的是remote进程。注意 process参数。

<!-- 注册Service -->

<service android:enabled="true"

android:name=".service.RemoteMusicService"android:process=":remote">

<intent-filter>

<action android:name="com.androidtest.service.mediaplayer.RemoteMusicService" />

</intent-filter>

</service>

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

点击查看 大图

Android进程在进行远程通讯时会:

1.产生一个主线程。

2. 产生Looper对象

3.产生一个消息队列。

4.产生一个虚拟机对象来实现Java和C++之间的沟通。

5.通过C/C++层的IPC来实现远程通讯。

所谓的 进程间通讯:Android通过IBinder接口来实现进程间的通讯,MyActivity会调用IBinder的transact() 函数通过IPC来调用远程的onTransact()函数。注意: 在默认情况下,如果Service和 Activity、 BroadcastReceiver在同一个工程里面,那么这些组件都会在同一个进程中执行,并且由主线程负责执行,当然也可以通过 配置让其在不同的组件里面执行,比如上一节我们就让Service在Remote进程中运行。

赞助本站

人工智能实验室

相关热词: Service 组件

相关内容
AiLab云推荐
展开

热门栏目HotCates

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