对于Service来说应该是每个搞android开发都必须会的.但是Service对于初学者来说又不那么容易理解,反正我学的时候总是感觉很乱,没有一个完整的体系,所以今天我就带着大家一起来学习学习Service,今天的内容都是我个人的理解,可能会有偏差,还请大神们指出。

  Service按作用来分,可以分为本地服务和远程服务。本地服务也就是用于同一个应用程序当中,而远程服务一般用于不同应用程序之间。今天我们主要着重看一下本地服务。

  Service的启动方式分为几种?

1.      通过startService方法启动

2.      通过bindService方法启动

那这两种不同的启动方式有些什么区别呢?我们先来看看google官方给出的Service生命周期图:

本地服务 JSESSIONID 几分钟就更换了_android

从图中我们可以看出由startService方法启动要经历的生命周期依次为:onCreate()àonStartConmmand()àonDestroy();

由bindService方法启动要经历的生命周期依次为:onCreate()àonBind()àonUnbind()àonDestroy();

这是startService和bindService在生命周期上的区别,那他们之间是否还有其他的区别呢?

用startService开启一项服务必须由stopService或者stopSelf,stopSelfResult()结束。在Actiivty中使用startService启动服务后,如果Activity退出了,Service会在后台继续的运行,直到调用上面所说的三种方法服务才会结束。

在Activity中用bindService将Activity与Service绑定,一旦Activity退出了,Service也会停止运行。

我们接下来说一个稍微无聊但是又引起很多初学者疑问的问题,我自己创建了一个Service的子类,我除了采用以上两种方式启动Service还能不能使用其他方法?如果我只是想调用Service里面的方法不想走生命周期行不行?带着这个疑问,我们开始今天的实验。今天的实验是写一个音乐播放器的demo,需求很简单,就是点击开始按钮播放音乐,点击停止按钮停止播放就可以了。


本地服务 JSESSIONID 几分钟就更换了_android_02


首先画出我们所需要的界面

本地服务 JSESSIONID 几分钟就更换了_android_03

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical"
    >

    <Button
        android:id="@+id/play1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="play1" />

    <Button
        android:id="@+id/stop1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop1" />

    <Button
        android:id="@+id/play2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="play2" />

    <Button
        android:id="@+id/stop2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop2" />
    
    <Button
        android:id="@+id/play3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="play3" />
    
    <Button
        android:id="@+id/stop3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop3" />

</LinearLayout>



界面很简单,直接使用LinearLayout布局,在里面放了6个按钮。

接下来是MainActivity

package org.service.viking.activity;

import org.service.viking.R;
import org.service.viking.util.LocalService;
import org.service.viking.util.LocalService.LocalBinder;

import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.MediaStore.Audio.Media;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.support.v4.app.NavUtils;

public class MainActivity extends Activity implements OnClickListener {

	private Button play1,stop1,play2,play3,stop2,stop3;//定义6个按钮
	private LocalService localService=null;//直接new Service
	private LocalService localService2;//通过bindService的回掉方法返回的service
	private ServiceConnection serviceConnection=new ServiceConnection()
	{
		
		//当Activity与service截除绑定成功后,会调用这个方法
		@Override
		public void onServiceDisconnected(ComponentName paramComponentName)
		{
			localService2.stopMusic();//停止音乐播放
			localService2=null;
		}
		//当Activity与service绑定成功后,会调用这个方法
		@Override
		public void onServiceConnected(ComponentName paramComponentName,
				IBinder paramIBinder)
		{ 
			localService2=((LocalService.LocalBinder)paramIBinder).getService();//得到onBind中放回的Ibind对象,在调用getService方法得到service
			localService2.playMusic();//开始播放音乐
		}
	};
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
        
    }

    /**
     * 初始化数据
     */
    private void initData()
	{
    	localService=new LocalService();
	}

	/**
	 * 初始化控件
	 */
	private void initView()
	{
    	play1=(Button)findViewById(R.id.play1);
    	stop1=(Button) findViewById(R.id.stop1);
    	play2=(Button)findViewById(R.id.play2);
    	stop2=(Button) findViewById(R.id.stop2);
    	play3=(Button)findViewById(R.id.play3);
    	stop3=(Button) findViewById(R.id.stop3);
    	play1.setOnClickListener(this);
    	stop1.setOnClickListener(this);
    	play2.setOnClickListener(this);
    	stop2.setOnClickListener(this);
    	play3.setOnClickListener(this);
    	stop3.setOnClickListener(this);
	}

	@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

	/**通过tag标签来判断是启动service还是停止service
	 * @param tag
	 */
	public void serviceMethod(String tag)
	{
		Intent intent =new Intent(MainActivity.this,LocalService.class);
		if(tag.equals("play"))
		{
			intent.putExtra("tag", "play");
			startService(intent);
		}
		else
		{
			if(tag.equals("stop"))
			{
				stopService(intent);
			}
		}
	}
	
	@Override
	public void onClick(View v)
	{
		switch (v.getId())
		{
			//使用直接new LocalService的方法来播放音乐
			case R.id.play1:
				localService.playMusic();
				break;
			case R.id.stop1:
				localService.stopMusic();
				break;
			//使用startService的方式来播放音乐
			case R.id.play2:
				serviceMethod("play");
				break;
			case R.id.stop2:
				serviceMethod("stop");
				break;
			//使用bindService的方式来播放音乐
			case R.id.play3:
				Intent intent = new Intent(this,LocalService.class);
				bindService(intent,serviceConnection , Context.BIND_AUTO_CREATE);
				break;
			case R.id.stop3:
				unbindService(serviceConnection);
				break;
			default:
				break;
		}
	}
    
}

LocalServic.java:

package org.service.viking.util;

import java.io.IOException;


import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;

public class LocalService extends Service
{
	private MediaPlayer player;//播放音频需要的类
	private String url="/mnt/sdcard/1.mp3";//音频的存放地址
	
	public LocalService()
	{
		System.out.println("Service construct..");
	}


	@Override
	public void onCreate()
	{
		System.out.println("Service onCreate...");
		super.onCreate();
	}

	@Override
	public IBinder onBind(Intent arg0)
	{
		System.out.println("Service onBind...");
		IBinder iBinder=new LocalBinder();
		return iBinder;
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId)
	{
		System.out.println("Service onStartCommand...");
		String tag=intent.getStringExtra("tag");
		//判断如果传过来的值是play就播放音乐
		if(tag.equals("play"))
		{
			playMusic();
		}
		
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public boolean onUnbind(Intent intent)
	{
		System.out.println("Service onUnbind...");
		return super.onUnbind(intent);
	}

	@Override
	public void onDestroy()
	{
		System.out.println("Service onDestroy...");
		stopMusic();
		super.onDestroy();
	}
	
	public void print()
	{
		System.out.println("print in Service");
	}
	
	public void playMusic()
	{
		player=MediaPlayer.create(this,Uri.parse(url));
		player.start();
	}
	
	public void stopMusic()
	{
		if(player!=null)
		{
			player.stop();
			try
			{
				//停止播放音乐以后需要调用这个方法
				player.prepare(); 
			}
			catch (IllegalStateException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	
	//创建一个内部类
	public class LocalBinder extends Binder
	{
	 	public LocalBinder()
		{
			
		}
		/**得到一个Service对象
		 * @return
		 */
		public LocalService getService()
		{
			return LocalService.this;
		}
	}
	
}



在LocalService中我们在他的生命周期函数中打印一句话,这样可以观察到这个函数是何时被调用的。然后把音频文件放入sd卡。

本地服务 JSESSIONID 几分钟就更换了_android_04

在AndroidManifest.xml中注册Service



<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.service.viking"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".activity.MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".util.LocalService"></service>
    </application>

</manifest>




好了,代码到这里就完成了,我们来测试一下效果如何。

首先我们点击play1按钮,点击后,悠扬的音乐就开始响起。。。这说明Service的子类是可以直接用new来得到的,但是我们可以看到控制台上面只是打印了构造函数被调用。



本地服务 JSESSIONID 几分钟就更换了_生命周期_05

也就是说直接使用new来得到service不会走服务的生命周期。New出来的实例只存在于MainActivity当中,如果MainActivity退出了,这个实例也将消失。这时候音乐会停止吗?这个问题就留给你们自己测试。。。

接下来我们点击play2按钮。当音乐响起时,调用了LocalService的构造方法,onCreate方法,onStartCommand方法。


本地服务 JSESSIONID 几分钟就更换了_android_06


我们这时候退出Activity,发现音乐还在继续的播放,当我们重新从桌面进入应用程序后按stop2按钮,音乐一样可以停止掉。所以,如果你要实现后台播放音乐的功能,那么可以考虑使用startService方法。

当我们点击play3按钮时,会调用LocalServcie的构造方法,onCreate方法,onBind方法。如果这时候退出MainActivity,音乐会随之停止,因为这个时候的service是跟activity绑定在一起的。我们点击stop3按钮后,会调用onunBind方法,onDestroy方法。


本地服务 JSESSIONID 几分钟就更换了_ide_07

这就跟google给出的生命周期图标对应上了。并且也证明了service是可以通过new得到的,service子类中的方法可以跟普通的方法一样调用。接下来我调用一下Service生命周期中的方法onStartCommand来启动音乐。修改MainActivity当中的代码:



本地服务 JSESSIONID 几分钟就更换了_android_08

修改后我们再次按play1按钮。控制台显示我们调用了onStartCommand方法,音乐再一次响起。


本地服务 JSESSIONID 几分钟就更换了_生命周期_09


最后我们总结一下Service的知识点:

1.  Service用于本地服务时,如果不跟activity进行交互可以使用startService方法启动服务

2.  Service用于本地服务,如果跟activity进行交互可以使用bindService方法绑定服务

3.  activity中想调用service中的方法,但是不想走生命周期,则可以直接new一个service。

4.  Service也是运行在主线程当中的,如果有耗时的任务,需要重新开启线程。

5.  startService启动Service必须用stopService关闭,并且不论调用了多少次startService都只需调用一次stopService。可以在androidManifest当中给Service设置action,这样可以方便启动和停止Service。

好了,今天的内容就到这里,有什么遗漏的以后在逐渐添加上来。