在很多情况下,一些与用户很少需要产生交互的应用程序,我们一般让它们在后台运行就行了,而且在它们运行期间我们仍然能运行其他的应用。
为了处理这种后台进程,Android引入了Service的概念。Service在Android中是一种长生命周期的组件,它不实现任何用户界面。最常见的例子如:媒体播放器程序,它可以在转到后台运行的时候仍然能保持播放歌曲;或者如文件下载程序,它可以在后台执行文件的下载。启动一个服务有两种方式,分别是采用startService方法和绑定Service的方式。在第一种方式启动后,Service会持续运行,直到调用stopService()或stopSelf()方法,如果调用方(例如在Activity中)销毁了,服务仍然还会运行。而第二种是先绑定后启动,绑定一个服务后调用方可以启动服务,并且能和服务依照定义的接口进行通信,并且还能接收服务方发送的广播消息,。我们先来看看第一种,在这个例子中我们实现一个使用Service播放音乐,在Activity界面上使用两个按钮, 分别控制服务的启动的停止。
使用startService方法启动服务
界面PlayActivity.java
package com.usestart.example; import com.usestart.example.R; 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; public class PlayActivity extends Activity implements OnClickListener { private static final String TAG = "com.example1.PlayActivity"; Button buttonStart, buttonStop; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); buttonStart = (Button) findViewById(R.id.buttonStart); buttonStop = (Button) findViewById(R.id.buttonStop); buttonStart.setOnClickListener(this); buttonStop.setOnClickListener(this); } public void onClick(View src) { switch (src.getId()) { case R.id.buttonStart: Log.d(TAG, "onClick: starting srvice"); startService(new Intent(this, PlayService.class)); break; case R.id.buttonStop: Log.d(TAG, "onClick: stopping srvice"); stopService(new Intent(this, PlayService.class)); break; } } @Override protected void onDestroy() { Log.i(TAG, "----Activity is onDestroy"); super.onDestroy(); } }
后台服务PlayService.java
package com.usestart.example; import java.io.IOException; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.IBinder; import android.util.Log; import android.widget.Toast; public class PlayService extends Service { private static final String TAG = "com.example.PlayService"; MediaPlayer player; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { Toast.makeText(this, "Play Service Created", Toast.LENGTH_LONG).show(); Log.d(TAG, "onCreate"); player =new MediaPlayer(); try { player.setDataSource("http://content.12530.com/upload/rings2/20090519/600618000223600902000001132715/000053488222_000019.mp3"); player.prepare(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } player.setLooping(true); // Set looping } @Override public void onDestroy() { Toast.makeText(this, "Play Service Stopped", Toast.LENGTH_LONG).show(); Log.d(TAG, "onDestroy"); player.stop(); } @Override public void onStart(Intent intent, int startid) { Toast.makeText(this, "Play Service onStart", Toast.LENGTH_LONG).show(); Log.d(TAG, "onStart"); player.start(); } }
在这个示例中我们演示了使用第一种方法启动一个服务,在界面上点击start按钮,后服务启动,音乐开始播放,点击Stop按钮服务停止和销毁,音乐停止播放。当点击手机上的回退按钮时我们发现Activity的onDestroy被调用,但是Service的onDestroy并未被调用,音乐也一直播放,直到再重新进入程序点击Stop按钮,服务的onDestroy才被调用。并且我们在界面上反复点击Start按钮,发现onStart回被多次调用,但是并没有多个音乐在播放,可以发现一个服务一旦启动了,再次调用star这个服务,onStart方法也被调用,但是不能导致再次启动这个服务。
使用bind方式来启动服务
package com.usebind.example; import android.app.Activity; import android.content.ComponentName; 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; public class PlayActivity2 extends Activity implements OnClickListener{ private static final String TAG = "com.example1.PlayActivity2"; Button buttonStart, buttonStop; Intent serviceIntent; ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i("INFO", "------------Service bind"); } @Override public void onServiceDisconnected(ComponentName arg0) { Log.i("INFO", "--------Service unBind"); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); buttonStart = (Button) findViewById(R.id.buttonStart); buttonStop = (Button) findViewById(R.id.buttonStop); buttonStart.setOnClickListener(this); buttonStop.setOnClickListener(this); serviceIntent = new Intent(this, PlayService.class); bindService(serviceIntent, conn, BIND_AUTO_CREATE); } public void onClick(View src) { switch (src.getId()) { case R.id.buttonStart: Log.d(TAG, "onClick: starting srvice"); //startService(new Intent(this, PlayService.class)); //bindService(new Intent("com.wissen.testApp.service.MY_SERVICE"), conn, Context.BIND_AUTO_CREATE); Intent i = new Intent(this, PlayService.class); this.startService(i); break; case R.id.buttonStop: Log.d(TAG, "onClick: stopping srvice"); unbindService(conn); //只有unbind之后调用stop才能将服务停止 stopService(new Intent(this, PlayService.class)); break; } } @Override protected void onDestroy() { Log.d(TAG, "----Activity is onDestroy"); unbindService(conn); super.onDestroy(); } }
使用bind方式方式时,在调用方调用bindService方法之后,服务并没有启动,只有在调用了startService方法之后服务才被启动。并且在Activity销毁时必须调用unbindService断开和这个服务的连接,否则程序回出错。如果是网速较慢或其他原因在运行上面的例子时回出现 no response错误,程序退出。导致这个原因是因为在service的onCreate方法是在主线程中运行的,不能出现运行耗时较长的代码,如果出现耗时较长的操作会将cpu阻塞住,这样很容易出现no response的错误.所以在编写一个服务时应将运行耗时较长的代码放到另外一个线程中或AsyncTask的doInBackground方法中执行。
在附件中ServicesPlay1是使用第一种方式,ServicesPlay1使用第二种方式的示例代码。