Service步骤
1、声明<service android:name=".ExampleService" > </service>,其与activity同级,形同:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activityd"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service android:name=".ExampleService" >
</service>
<activity
android:name="com.example.activityd.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2、创建service,可以继承Service,也可以是IntentService
一、使用IntentService,
- 这是一个 Service的子类,该子类使用线程处理所有启动请求,一次一个.这是不使用服务处理多任务请求的最佳选择.你需要做的只是实现 onHandleIntent()方法即可,可以为每个启动请求接收到intent,放到后台工作即可。 IntentService类可以做这些事情:
- 从应用的主线程当中创建一个默认的线程执行所有的intents发送给onStartCommand()方法,该线程从主线程分离.
- 创建工作队列,每次传递一个intent 给onHandleIntent()方法实现,所以不必担心多线程.
- 所有的请求被处理后服务停止,所以你永远都不必调用stopSelf()函数.
- 默认实现onBind()方法返回null.
- 默认实现onStartCommand()方法是发送一个intent给工作队列,然后发送给onHandleIntent()方法的实现。
一个构造函数,和onHandleIntent()方法的重载:
public class ExampleService extends IntentService {
public ExampleService() {//必须是空的
super(“Whatever");
// TODO Auto-generated constructor stub
}
@Override
protected void onHandleIntent(Intent intent) {
long endTime = System.currentTimeMillis() + 5 * 1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.widget.Toast;
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// 处理程序从线程中收到的消息
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 通常我们在这里做一些事情,例如下载文件。
// 这里,我们只让程序睡眠5s
long endTime = System.currentTimeMillis() + 5 * 1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// 使用startId停止服务,所以我们在处理其他任务的过程中不会停止该服务.
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// 启动线程开启服务,注意默认服务通常运行在进程的主线程中,所以我们创建了另一个线程。
// 而且我们也不想阻塞程序。另外我们把该线程放在后台运行,这样cpu耗时操作就不会破坏应用界面.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// 获得线程的Looper,用于我们的Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// 对于每个启动请求,发送消息用于启动一个任务,传递start ID,这样当完成此任务时就知道谁发出该请求.
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// 如果服务已被杀死,执行return后,线程重启.
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// 如果我们不提供服务绑定,返回null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
但是,在onStartCommand()方法里因为是自己处理每个调用,所以你可以同时处理多个请求。例子中虽然没演示,但如果需要,你完全可以为每个请求创建一个线程,按正确的方式处理这些请求.(比起上面的IntentService,Service代替了需要等待上次请求处理完毕的方式。)
注意onStartCommand()方法必须返回一个整型变量.这个变量描述可能已被系统停止的服务,如果被停止,系统会接着继续服务(正如下面讨论,IntentService的实现中默认自动帮你做了个处理,尽管可以修改它),返回值必须是如下几个常量之一:
START_NOT_STICKY
- 如果系统在onStartCommand()返回后停止服务,系统不会重新创建服务,除非有等待的意图需要处理.如果想避免运行不需要的服务,或者让应用可以轻松的启动未完成的任务,这是一个安全的选择。
START_STICKY
- 如果系统在onStartCommand()返回后停止服务,系统重新创建服务并且调用onStartCommand()函数,但是不要传送最后一个意图。相反,系统用一个空意图调用onStartCommand(),除非还有想启动服务的意图,这种情况下,这些意图会被传递.这很适合多媒体播放的情况(或类似服务),这种情况下服务不执行命令,但是会无限运行下去,等待处理任务。
START_REDELIVER_INTENT
- 如果系统在onStartCommand()返回后停止服务,系统使用用最后传递给service的意图,重新创建服务并且调用onStartCommand()方法, 任何未被处理的意图会接着循环处理。所以用服务处理像下载之类的可能需要立即重启的任务,非常合适。
关于这些返回值的更多信息,请参考文档for each constant.
3、启动Service调用那个startService(intent)方法
象启动Activity一样,
Intent intent = new Intent(this,
ExampleService.class);
startService(intent);
这是在Fragment中启动:
Intent intent = new Intent(UiFragment.this.getActivity(),
ExampleService.class);
UiFragment.this.getActivity().startService(intent);
startService()方法立即返回,安卓系统会调用onStartCommand()方法。如果服务没有运行,系统首先调用onCreate()方法,然后在调用onStartCommand()方法。
如果系统不提供绑定,通过传递意图给startService()方法是应用组件和服务唯一的沟通方式。但是如果你想服务发送一个返回结果,可以让客户端使用广播(调用getBroadcast()方法获取)创建一个PendingIntent启动一个服务,然后把该意图传递给要启动的服务,服务就可以使用此广播传递结果了。
并发请求启动服务导致并发响应调用onStartCommand()函数,但是,唯一停止服务的方法就是调用stopSelf()或者stopService()方法。
4、停止服务
调用了stopSelf()或stopService(),系统会尽快销毁服务。
但是,当前如果服务在onStartCommand()方法中同时处理多个请求,那么当你正在处理这样的启动请求时,肯定不应该停止服务,因为很有可能在停止服务时刚好收到一个新的请求(在第一个请求的结尾停止服务会终止第二个请求).要避免这个问题,你可以使用stopSelf(int)方法确保服务被停止,而且这个服务是最经常启动的。就是说,当调用stopSelf(int),你需要传递给请求启动的ID(这个ID传递给onStartCommand()方法)给对应的服务.如果在可以调用stopSelf(int)之前,服务接收到一个新的启动请求,服务就不会匹配,也就不会停止。
绑定服务
一个绑定的服务是客户服务器接口上的一个服务器。一个绑定的服务允许组件(如:活动)来绑定一个服务,传送请求,接收响应,甚至执行进程间的通信(IPC)。绑定服务通常只生存在其服务于另一个程序组件时,并且不会无限期的在后台运行。
绑定服务是一个Service类的一个实现,允许其它服务绑定到它并与其互动。为服务提供绑定,你必须实现onBind()回调方法。这个方法返回一个IBinder对象,它定义了程序接口,用户可以用其与服务交互。
多个用户可以同时连接到一个服务。但是,系统只以第一个用户绑定时调用onBind()方法来获得IBinder对象。然后,系统将同一个IBinder对象传递给后续绑定的客户,并不会重新调用onBind()方法。
一、创建绑定服务方式一:扩展binder类
服务只被本地应用程序所使用,并且不需要在多个进程间工作。这个方法只有在客户和服务在同一个应用程序和进程中时才可行,这种情况很常见。例如,这个方法对于一个音乐应用程序将会非常有用,它需要绑定一个活动到它自己的服务用来以后台播放音乐。
设定Binder:
1.在你的服务中,创建一个Binder类的实例,实现以下功能之一:
-
- 包含客户可以调用的公共方法
- 返回当前Service的实例,其包含了用户可以访问的公共方法
- 或返回这个服务包含的另一个类,并含有客户可以访问的公共方法
服务和客户端必须在同一个应用程序中的原因是客户端可以计算返回的对象并恰当的调用其APIs。服务和客户端也必须在同一个线程的原因是这种技术不能执行线程间操作。
代码如下:
MainActivity.java
package com.example.activityd;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Toast;
import com.example.activityd.ExampleService.LocalBinder;
public class MainActivity extends Activity {
ExampleService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, ExampleService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get
// LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/**
* Called when a button is clicked (the button in the layout file attaches
* to this method with the android:onClick attribute)
*/
public void onButtonClick(View v) {
if (mBound) {
int num = mService.getRandomNumber();// Get the random number by the
// method in Service.
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
}
ExampleService.java
package com.example.activityd;
import java.util.Random;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
public class ExampleService extends Service {
// Binder given to clients
private final IBinder mBinder = (IBinder) new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
ExampleService getService() {
// Return this instance of LocalService so clients can call public
// methods
return ExampleService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
在客户端主要通过,bindService(intent, mConnection, Context.BIND_AUTO_CREATE)、onServiceConnected、onServiceDisconnected方法;服务端通过,Binder子类和onBind方法,见上。
二、创建绑定服务方式二:使用一个消息传递器
如果你需要你的服务能够与远程进程通信,那么你可以使用一个Messenger为你的服务提供接口。这个方法允许你执行进程间通信(IPC),而不需要使用AIDL。
Handler与Message的处理机制,应该之前都看到过。在此的实现机制,如下:
- 服务实现了一个Handler,用来接收每一次调用从客户端返回的回调方法。
通过这种方法,在服务端没有客户端能调用的“方法”。而是,客户传递“消息”(Message对象),同时服务在其Handler中接收。
应用程序先是,onStart,再调用bindService(new Intent(this, ExampleService.class), mConnection,Context.BIND_AUTO_CREATE),服务中的onBind方法获取IBinder对象,在调用客户端的onServiceConnected方法创建服务连接。
具体代码如下:
package com.example.activityd;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
/**
* 客户端所需做的只是基于服务返回的IBinder创建一个Messenger并使用send()方法发送一条消息。
*
* @author hunter
*
*/
public class MainActivity extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
Log.d("huang", "onStart");
bindService(new Intent(this, ExampleService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d("huang", "onServiceConnected");
mService = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName className) {
Log.d("huang", "onServiceDisconnected");
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound)
return;
// Create and send a message to the service, using a supported 'what'
// value
Message msg = Message.obtain(null, ExampleService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
================================================================
package com.example.activityd;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import android.widget.Toast;
public class ExampleService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!",
Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
Log.d("huang", "onBind");
return mMessenger.getBinder();
}
}
三、创建绑定服务方式三:使用AIDL
通过这个接口,客户端和服务器之间可以顺利的进行进程间通讯(IPC)。只有您允许来自不同应用的客户端访问您的IPC服务并且您希望在服务中处理多线程,使用AIDL才是必要的。如果您不需要使用并发的IPC访问不同的应用,您应该通过继承Binder来创建您的接口,或者,如果您确实需要使用IPC,但是不需要处理多线程,那请继承Messenger来实现您的接口。
一个AIDL接口
1、创建一个aidl文件
您必须使用java语言构建一个.aidl文件。每个.aild文件必须定义一个单独的接口,并且使用确定的声明和方法签名。可以先创建一个Java接口,再将其后缀名改掉,改成aidl。
所支持的数据类型:
- 基本数据类型 (such as
int
,long
,char
,boolean
, and so on) String
CharSequence
List
Map
就像定义Java接口。
由于在Eclipse中没有直接创建aidl文件的选项,我们可以先创建一个接口,如:IRemoteService.java,
在其中添加方法,代码如下:
package com.example.aidl;
//IRemoteService.aidl
//Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/**
* Demonstrates some basic types that you can use as parameters and return
* values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
接口定义完后,将
IRemoteService.java
文件复制到某个文件夹中,将其改名为IRemoteService.aidl,Eclipse删掉
,将IRemoteService.java
文件IRemoteService.aidl
放置于此。保存之后,会在gen目录下,生成IRemoteService.java文件。
2、实现AIDL接口
生成的接口包括一个名为Stub的子类,这个子类是他的父接口的一个抽象实现(例如 YourInterface.Stub),并且声明.aidl文件里的所有方法。Stub也定义一些辅助方法,尤其是 asInterface(),它需要一个IBinder(通常是传递给客户端的一个回调方法。)并且返回一个stub接口的实例。
@Override public android.os.IBinder asBinder()
{
return this;
}
要实现.AIDL里面定义的接口,就要继承已经生成的Binder接口(例如 YourInterface.Stub)并且继承.aidl文件的方法。
如下:
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing } };mBinder 是Stub类的一个实例(一个 Binder),它定义了服务的RPC接口。
3、公开接口
package com.example.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid() {
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}
至此,服务端也就搞定了。继续创建客户端,由于在不同的应用程序中,在创建一个Android工程。
当一个客户端(比如一个activty调用)android.content.ServiceConnection, int) bindService()方法来和这个服务连接时,这个客户端的android.os.IBinder) onServiceConnected() 方法接收mBinder实例并且通过服务的onBind方法返回。客户端也必须访问接口类,所以如果客户端和服务分属两个不同的应用,那客户端应用必须复制.aidl文件到他的src/目录(这个目录生成android.os.Binder接口,提供客户端访问的AIDL接口)。将在客户端创建与服务端应用程序一样的包名(放置aidl文件),复制于此。
当客户端在android.os.IBinder) onServiceConnected() 回调方法中接收IBinder时,它必须调用YourServiceInterface.Stub.asInterface(service)来与您的YourServiceInterface 接口类型保持一致。例如:
IRemoteService mIRemoteService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { // Following the example above for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service mIRemoteService = IRemoteService.Stub.asInterface(service); } // Called when the connection with the service disconnects unexpectedly public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "Service has unexpectedly disconnected"); mIRemoteService = null; } };
客户端代码:
package com.example.aidlclient;
import com.example.aidl.IRemoteService;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private Button btnGetId = null;
private Button btnOk = null;
private IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mIRemoteService = IRemoteService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName className) {
mIRemoteService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOk = (Button) findViewById(R.id.btn_getOk);
btnOk.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("huang", "ONCLICK 111");
Intent intent = new Intent("com.example.aidl.IRemoteService");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
Log.d("huang", "ONCLICK 222");
}
});
btnGetId = (Button) findViewById(R.id.btn_getid);
btnGetId.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("huang", "OK");
// 调用方法
try {
mIRemoteService.getPid();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
需要注意的地方:
服务端清单文件中,
<service android:name=".RemoteService" >
<intent-filter>
<action android:name="com.example.aidl.IRemoteService" />
</intent-filter>
</service>
先运行服务端应用,在运行客户端应用,否则会出现NullPointerException,mIRemoteService为null,onServiceConnected没有运行。