根据疯狂Android讲义一书学习:
Service介绍
Service作为安卓四大组建之一,也需要在AndroidManifest.xml中配置,并且根据<intent-filter.../>属性指定可以被哪些Intent启动。
Service和Activity来自相同基类Context,也可一调用getResoureces(),getContentResolver()等方法。
Service的生命周期:
- IBinder onBind(Intent intent): 该方法是Service子类必须实现的方法,返回一个IBinder对象,应用程序通过该IBinder对象与Service组建通信。
- void onCreate():第一次创立时回调。
- void onDestroy():被关闭之前回调。
- void onStartCommand(Intent intent, int flags, int startId):早期版本为void onStart(Intent intent, int startId),每次客户端调用startService(Intent)方法启动Service时回调。
- boolean onUnbind(Intent intent):当该Service上绑定的客户端都断开链接时回调。
下边定义了一个Service组件:
/**
*
*/
package org.crazyit.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class FirstService extends Service
{
// 必须实现的方法
@Override
public IBinder onBind(Intent arg0)
{
return null;
}
// Service被创建时回调该方法。
@Override
public void onCreate()
{
super.onCreate();
System.out.println("Service is Created");
}
// Service被启动时回调该方法
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
System.out.println("Service is Started");
return START_STICKY;
}
// Service被关闭之前回调。
@Override
public void onDestroy()
{
super.onDestroy();
System.out.println("Service is Destroyed");
}
}
上边代码是基础的Service组建必须实现的方法。
然后需要在AndroidManifest.xml中配置:
<!-- 配置一个Service组件 -->
<service android:name=".FirstService">
<intent-filter>
<!-- 为该Service组件的intent-filter配置action -->
<action android:name="org.crazyit.service.FIRST_SERVICE" />
</intent-filter>
</service>
和Activity类似,指定intent-filter标签说明该Service可以被哪些Intent启动。
和Activity不同的是,不需要android:label等属性,因为Service永远在后台运行,没必要指定标签。
介绍完基础的Service类和配置后说一下Service的两种启动方式:
- 通过Context的startService()方法启动:如果用这种方式启动,访问者和Service之间没有关联,如果访问者退出,Service依然运行。
- 通过Context的bindService()方法启动:如果用这种方式启动,访问者和Service绑定在了一起,如果访问者退出,Service也就终止。
启动和停止Service
下边的Activity作为Service的访问者,由两个按钮组成,一个用来启动Service,另一个用来关闭Service。
package org.crazyit.service;
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;
public class StartServiceTest extends Activity
{
Button start, stop;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取程序界面中的start、stop两个按钮
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
// 创建启动Service的Intent
final Intent intent = new Intent();
// 为Intent设置Action属性
intent.setAction("org.crazyit.service.FIRST_SERVICE");
start.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
// 启动指定Serivce
<strong>startService(intent);</strong>
}
});
stop.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
// 停止指定Serivce
<strong>stopService(intent);</strong>
}
});
}
}
可以看到启动Service的方法为startService(Intent intent),关闭Service的方法为stopService(Intent intent)。
如果连续点击三次启动按钮,可以发现onStartCommand()方法被调用了3次,而onStart()方法只有第一次被回调。
绑定本地Service并与之通信
使用startService()、stopService()的方式启动、停止 Service,访问者和Service之间没有关联,也无法进行通信和数据交换。
如果需要进行方法调用或数据交换,应该使用bindService()和unbindService()方式。
先介绍Context的bindService(Intent service,ServiceConnection conn,int flags)方法的三个参数:
- Intent service:该参数通过Intent指定要启动哪个Service。
- ServiceConnection conn:该参数是一个ServiceConnection对象,该对象用于监听访问者和Service之间的连接情况。当访问者与Service连接成功时回调ServiceConnection对象的onServiceConnected(ComponentName name, IBinder binder)方法;当连接断开时回调onServiceDisconnected(ComponentName name)方法。
- Int flags:该参数指定绑定时是否自动创建Service。如果为0则不自动创建,或者为BIND_AUTO_CREATE。
通信是通过onServiceConnected方法的IBinder对象进行的,Service类必须实现onBind(IBinder binder)方法,该方法的参数binder会传给ServiceConnect的onServiceConnected方法,这样访问者就可以通过IBinder对象与Service进行通信。
下边的例子实现了onBind()方法,并且实现了自己的IBinder的实现类:
package org.crazyit.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class BindService extends Service
{
private int count;
private boolean quit;
// 定义onBinder方法所返回的对象
private MyBinder binder = new MyBinder();
// 通过继承Binder来实现IBinder类
public class MyBinder extends Binder //①
{
public int getCount()
{
// 获取Service的运行状态:count
return count;
}
}
// 必须实现的方法,绑定该Service时回调该方法
@Override
public IBinder onBind(Intent intent)
{
System.out.println("Service is Binded");
// 返回IBinder对象
return binder;
}
// Service被创建时回调该方法。
@Override
public void onCreate()
{
super.onCreate();
System.out.println("Service is Created");
// 启动一条线程、动态地修改count状态值
new Thread()
{
@Override
public void run()
{
while (!quit)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
}
count++;
}
}
}.start();
}
// Service被断开连接时回调该方法
@Override
public boolean onUnbind(Intent intent)
{
System.out.println("Service is Unbinded");
return true;
}
// Service被关闭之前回调该方法。
@Override
public void onDestroy()
{
super.onDestroy();
this.quit = true;
System.out.println("Service is Destroyed");
}
}
上边代码中的①部分是一个继承了Binder(实现了IBinder接口)的内部类。
下边的Activity会绑定上边的Service,并且通过MyBinder获取Service的内部数据(Count)。该Activity包含三个按钮,第一个用于绑定Service,第二个用于解除绑定,第三个用于获取Service的
package org.crazyit.service;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class BindServiceTest extends Activity
{
Button bind, unbind, getServiceStatus;
// 保持所启动的Service的IBinder对象
BindService.MyBinder binder;
// 定义一个ServiceConnection对象
private ServiceConnection conn = new ServiceConnection()
{
// 当该Activity与Service连接成功时回调该方法
@Override
public void onServiceConnected(ComponentName name
, IBinder service)
{
System.out.println("--Service Connected--");
// 获取Service的onBind方法所返回的MyBinder对象
binder = (BindService.MyBinder) service; //①
}
// 当该Activity与Service断开连接时回调该方法
@Override
public void onServiceDisconnected(ComponentName name)
{
System.out.println("--Service Disconnected--");
}
};
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取程序界面中的start、stop、getServiceStatus按钮
bind = (Button) findViewById(R.id.bind);
unbind = (Button) findViewById(R.id.unbind);
getServiceStatus = (Button) findViewById(R.id.getServiceStatus);
// 创建启动Service的Intent
final Intent intent = new Intent();
// 为Intent设置Action属性
intent.setAction("org.crazyit.service.BIND_SERVICE");
bind.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 绑定指定Serivce
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
unbind.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 解除绑定Serivce
unbindService(conn);
}
});
getServiceStatus.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 获取、并显示Service的count值
Toast.makeText(BindServiceTest.this,
"Serivce的count值为:" + binder.getCount(),
Toast.LENGTH_SHORT).show(); //②
}
});
}
}
上边代码中的①部分把onBind(Intent intent)方法返回的Binder对象给到ServiceConnection对象里。
代码②通过Binder的getCount()方法访问Service的数据。
当activity调用unbindService()时会调用Service的onUnbind()方法和onDestroy()方法。
与之前通过startService访问Service不同,之前每次点击启动服务按钮都会回调Service的onStartCommand()方法,但如果多次按下绑定按钮,只调用一次onBind()方法。
Service的生命周期
上边讲的两种情况,Service的生命周期也有所不同。
第一种通过startService()启动的生命周期为:onCreate()-->onStartCommand()-->onDestroy();
第二种通过bindService()启动的生命周期为:onCreate()-->onBind()-->onUnbind()-->onDestroy()。
还有一种情况是通过startService()启动,然后再通过bindService()去绑定:
onCreate()-->onStartCommand()-->onBind()-->onUnbind()-->onRebind()。
可以看到这时Context的unBindService()方法并不会回调Service的onDestory()方法。这是因为该service是通过startService启动起来的,这种方式建立的Service并没有和访问者真正的绑定,因此生命周期不会受到影响。解除绑定后Service依然存在,需要通过stopService()方法关闭。
使用IntentService
IntentService是Service的一个子类。
Service和它所在的应用位于同一个线程,因此不应该用来处理耗时任务。如果需要执行耗时任务,需要自己新起一个子线程,就像上边的例子,在onCreate方法中新起得子线程。
IntentService可以直接解决这个问题,对于异步的startService()请求,IntentService会建立一个队列并在新建的worker线程里依次处理Intent。
归纳IntentService的特征:
- IntentService会新建单独的worker thread来处理所有的intent请求。
- IntentService的onHandleIntent()方法已经在新的线程里运行,开发者无需维护多线程。
- 当IntentService处理完所有的Intent后会自动停止,无需开发者再调用Service的stopSelf()方法(相当于访问Context的stopService()方法)来停止该Service。
- 默认实现了onBind()方法,返回null。
- 默认实现了onStartCommand()方法,将Intent放入队列中。
因此实现IntentService无须必须重写onBind和onStartCommand方法,而需要重写onHandleIntent()方法。
下边的activity的两个按钮分别启动一个普通service一个intentService来进行耗时任务:
package org.crazyit.service;
import android.os.Bundle;
import android.view.View;
import android.app.Activity;
import android.content.Intent;
public class IntentServiceTest extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void startService(View source)
{
// 创建需要启动的Service的Intent
Intent intent = new Intent(this, MyService.class);
// 启动Service
startService(intent);
}
public void startIntentService(View source)
{
// 创建需要启动的IntentService的Intent
Intent intent = new Intent(this, MyIntentService.class);
// 启动IntentService
startService(intent);
}
}
MyService继承Service,MyIntentService继承IntentService,下边是两个Service的实现:
MyService:
package org.crazyit.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service
{
@Override
public IBinder onBind(Intent intent)
{
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// 该方法内执行耗时任务可能导致ANR(Application Not Responding)异常
long endTime = System.currentTimeMillis() + 20 * 1000;
System.out.println("onStart");
while (System.currentTimeMillis() < endTime)
{
synchronized (this)
{
try
{
wait(endTime - System.currentTimeMillis());
}
catch (Exception e)
{
}
}
}
System.out.println("---耗时任务执行完成---");
return START_STICKY;
}
}
上边的 service在onStartCommand方法里执行了一个耗时20秒的任务,因为阻塞主线程所以会造成ANR(Application not responding)异常。
然后是IntentService:
package org.crazyit.service;
import android.app.IntentService;
import android.content.Intent;
public class MyIntentService extends IntentService
{
public MyIntentService()
{
super("MyIntentService");
}
// IntentService会使用单独的线程来执行该方法的代码
@Override
protected void onHandleIntent(Intent intent)
{
// 该方法内可以执行任何耗时任务,比如下载文件等,此处只是让线程暂停20秒
long endTime = System.currentTimeMillis() + 20 * 1000;
System.out.println("onStart");
while (System.currentTimeMillis() < endTime)
{
synchronized (this)
{
try
{
wait(endTime - System.currentTimeMillis());
}
catch (Exception e)
{
}
}
}
System.out.println("---耗时任务执行完成---");
}
}
可以看到在IntentService里并不需要再重写onBind和onStartCommand方法。而onHandleIntent里的任务会自动在新的worker thread里执行,从而不会阻塞主线程,因此不会造成ANR异常。