对于Service来说应该是每个搞android开发都必须会的.但是Service对于初学者来说又不那么容易理解,反正我学的时候总是感觉很乱,没有一个完整的体系,所以今天我就带着大家一起来学习学习Service,今天的内容都是我个人的理解,可能会有偏差,还请大神们指出。
Service按作用来分,可以分为本地服务和远程服务。本地服务也就是用于同一个应用程序当中,而远程服务一般用于不同应用程序之间。今天我们主要着重看一下本地服务。
Service的启动方式分为几种?
1. 通过startService方法启动
2. 通过bindService方法启动
那这两种不同的启动方式有些什么区别呢?我们先来看看google官方给出的Service生命周期图:
从图中我们可以看出由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,需求很简单,就是点击开始按钮播放音乐,点击停止按钮停止播放就可以了。
首先画出我们所需要的界面
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卡。
在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来得到的,但是我们可以看到控制台上面只是打印了构造函数被调用。
也就是说直接使用new来得到service不会走服务的生命周期。New出来的实例只存在于MainActivity当中,如果MainActivity退出了,这个实例也将消失。这时候音乐会停止吗?这个问题就留给你们自己测试。。。
接下来我们点击play2按钮。当音乐响起时,调用了LocalService的构造方法,onCreate方法,onStartCommand方法。
我们这时候退出Activity,发现音乐还在继续的播放,当我们重新从桌面进入应用程序后按stop2按钮,音乐一样可以停止掉。所以,如果你要实现后台播放音乐的功能,那么可以考虑使用startService方法。
当我们点击play3按钮时,会调用LocalServcie的构造方法,onCreate方法,onBind方法。如果这时候退出MainActivity,音乐会随之停止,因为这个时候的service是跟activity绑定在一起的。我们点击stop3按钮后,会调用onunBind方法,onDestroy方法。
这就跟google给出的生命周期图标对应上了。并且也证明了service是可以通过new得到的,service子类中的方法可以跟普通的方法一样调用。接下来我调用一下Service生命周期中的方法onStartCommand来启动音乐。修改MainActivity当中的代码:
修改后我们再次按play1按钮。控制台显示我们调用了onStartCommand方法,音乐再一次响起。
最后我们总结一下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。
好了,今天的内容就到这里,有什么遗漏的以后在逐渐添加上来。