一、
Service主要有两种用途:
1.执行耗时操作。因为Service是工作在UI线程(即主线程)中的,所以当想在Service中执行耗时操作,应该新建一个线程。
2.用于组件之间的交互。通过将某些功能以Service组件的形式进行封装,然后提供给其他应用组件调用。
二、
Service的生命周期:
Service有两种调用的方式,根据调用方式的不同,有不同的生命周期。
1.启动模式(即startService())
如果Service用于执行长时间运行的操作,则一般采用启动模式。
如果当前指定的Service实例没有被创建,则调用Service的onCreate()来创建一个实例。
否则直接调用Service的onStartCommand()来运行Service。因此Service的onStartCommand()可能会重复调用多次。所以在onStartCommand()中要注意线程同步。
2.绑定模式(即bindService())
如果Service用于提供一种封装的功能供其他组件使用,则一般采用绑定模式。
如果当前指定的Service实例没有被创建,则调用Service的onCreate()来创建一个实例。
实例启动后,将调用onBind()。onBind将返回一个IBinder接口实例,IBinder允许客户端回调Service的方法。在绑定模式下,Android将调用者(如Activity)和Service绑定在一起,当调用者退出时,Service先后调用onUnbind()和onDestroy()退出。注意onBind只一次,不可多次绑定。
三、
启动模式下的Service:
重点就是onStartCommand()了:
@Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub return super.onStartCommand(intent, flags, startId); }
它的返回值可以取:
START_NOT_STICKY
如果Service在它启动后(从onStartCommand()返回后)被kill掉,那么它将不会被重启。适用场景:网上下载数据。
START_REDELIVER_INTENT
如果Service在它启动后(从onStartCommand()返回后)被kill掉,那么它将会被重启(onCreate()方法会执行),onStartCommand()会执行。并且最后传给它的Intent通过onStartCommand()会被重新传给它。这种模式保证了传递给它的Intent一定会被处理完毕。适用场景:关键业务处理。
START_STICKY
如果Service在它启动后(从onStartCommand()返回后)被kill掉,那么它将会被重启(onCreate()方法会执行),onStartCommand()不会执行。但是Intent不被保留,此时Intent的值为null。适用场景:后台播放音乐。
同时注意的是onStartCommand()中的多请求下的线程同步。
在开发Service组件的过程中,经常要涉及线程及线程同步等复杂的问题。这是我们可以使用IntentService(继承Service)。
IntentService:异步处理服务,新开一个线程,当完成所有的任务以后自己关闭。
IntentService有以下特点:
1.创建一个工作队列,它每次将一个Intent传递到onHandleIntent(),解决了同步的问题。
2.当所有请求被处理完成后,将自动停止服务。
3.提供了一个返回null的onBind()方法的默认实现。
4.提供了onStartCommand()方法的默认实现,它将所有的Intent发送到一个工作队列,并进一步发送到onHandleIntent()方法。
继承IntentService的类至少要实现两个函数:构造函数和onHandleIntent()函数。要覆盖IntentService的其它函数时,注意要通过super调用父类的对应的函数。
下面用一个例子展示一下:
package com.example.test; import java.text.SimpleDateFormat; import android.app.IntentService; import android.content.Intent; import android.os.Bundle; import android.util.Log; /* * IntentService注意点: * 1.通过一个无参数的构造方法,并在方法中调用超类中以字符串为参数的构造方法 * 其中字符串被用来作为IntentService的工作线程的名称 * 2.可以覆盖onCreate(),onDestory()等方法,不过必须在方法的最后调用超类 * 的对应方法的实现,否则将抛出意外 */ public class TestService extends IntentService { private int span; public TestService() { super("TestService"); } @Override protected void onHandleIntent(Intent intent) { Log.v("onHandleIntent", "服务启动时间" + getCurrentTime()); Bundle bundle = intent.getBundleExtra("bundle"); span = bundle.getInt("waitTime"); long endTime = System.currentTimeMillis() + span * 1000; Log.v("onHandleIntent", "服务持续时间" + span); while (System.currentTimeMillis() < endTime) { try { synchronized (this) { wait(endTime - System.currentTimeMillis()); } } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void onDestroy() { Log.v("onHandleIntent", "服务摧毁时间" + getCurrentTime()); super.onDestroy(); } private String getCurrentTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date = sdf.format(new java.util.Date()); return date; } }
package com.example.test; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((Button) findViewById(R.id.start)).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,TestService.class); Bundle bundle = new Bundle(); double a = Math.random() * 10; a = Math.ceil(a); int randomNum = new Double(a).intValue(); bundle.putInt("waitTime", randomNum); intent.putExtra("bundle", bundle); // Toast.makeText(getApplicationContext(), randomNum + "", Toast.LENGTH_SHORT).show(); startService(intent); } }); ((Button) findViewById(R.id.end)).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopService(new Intent(MainActivity.this,TestService.class)); } }); } }
四、
绑定模式下的Service:
1.本地绑定
要实现对Service组件功能的调用,Service组件要做以下改造:
a.将Service组件的功能封装到一个接口中
b.实现一个内部类,它继承Bind类(即实现IBinder接口),并实现上面的接口
c.在Service组件的onBind()中,返回b中的内部类对象,供其他组件使用
下面用一个例子展示一下:
package com.example.test2; public interface ICount { public abstract int getCount(); }
package com.example.test2; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; public class CountService extends Service { private int count; private ServiceBinder serviceBinder = new ServiceBinder(); @Override public void onCreate() { super.onCreate(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count++; Log.v("CountService", "Count is" + count); } } }).start(); } @Override public IBinder onBind(Intent intent) { return serviceBinder; } public class ServiceBinder extends Binder implements ICount { @Override public int getCount() { return count; } } }
package com.example.test2; 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.Menu; import android.view.MenuItem; public class MainActivity extends Activity implements ServiceConnection { private ICount iCount; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService(new Intent("com.example.test2.CountService"), this, BIND_AUTO_CREATE); } @Override public void onServiceConnected(ComponentName name, IBinder service) { iCount = (ICount) service; Log.v("MainActivity", iCount.getCount() + ""); } @Override public void onServiceDisconnected(ComponentName name) { iCount = null; } @Override protected void onDestroy() { unbindService(this); super.onDestroy(); } }
<service android:name=".CountService"> <intent-filter > <action android:name="com.example.test2.CountService"/> </intent-filter> </service>
2.远程绑定