Android Service總結(上)
Service是android中在後臺執行,不提供使用者介面的一個元件。
Service可以有2種形式:啟動的(Started)Service和繫結的(Bound)Service。
啟動的Service是由一個元件(比如Activity)呼叫startService建立的,在後臺獨立行的元件(即使啟動它的元件被銷燬)。它並不返回執行結果。
繫結的Service可以在執行期間和程序內的其他元件(僅限於Activity,Content Provider, Service)進行互動,繫結的Service相當於一個伺服器,呼叫它的元件相當於客戶端,在執行期間客戶端可以向Service傳送請求,繫結,獲取結果,還可以實現程序間通訊(IPC)
一.啟動的(Started)Service
1.建立和管理Service
建立一個Servcie:
1)建立一個類繼承Service
public class MyService extends Service{ @Override public void onCreate() { Log.v("test","onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.v("test","onStartCommand"); return START_NOT_STICKY; } @Override public void onDestroy() { Log.v("test","onDestroy"); } @Override public IBinder onBind(Intent intent) { return null; } }
2)在manifest檔案中宣告Servcie:
<manifest ... >
...
<application ... >
<service android:name="com.example.test.workspace1.app.MyService" />
...
</application>
</manifest>
啟動Service:
在Activity或者其他元件中,可以通過Intent啟動指定的Service
停止Servcie:Intent intent = new Intent(this, MyService.class); startService(intent);
可以在Activity或者其他元件中呼叫 stopService(Intent)或者在onStartCommand中呼叫 stopSelf()
2.生命週期和回撥方法
在呼叫startService()方法後,如果service沒有建立,則執行onCreate方法,之後執行onStartCommand方法。否則直接執行onStartCommand方法。當呼叫stopService或者stopSelf後,呼叫onDestory回撥,然後銷燬Servcie。
3.返回值
onStartCommand方法返回一個int值,這個返回值告訴系統應該怎麼處理被系統關閉的Service,返回值可以是以下3個:
START_NOT_STICKY:當系統在onStartCommand返回後關閉Service後,不重新建立Service。
START_STICKY:當系統在onStartCommand返回後關閉Service,重新建立Service並且呼叫onStartCommand,但不重新傳遞上一個intent而是用null代替,除非用pendingIntent啟動Service。
START_REDELIVER_INTENT:當系統在onStartCommand返回後關閉Service,重新建立Service並且呼叫onStartCommand,重新傳遞最新的ntent,pendingIntent按照順序傳遞。這個返回值適用於後臺操作需要恢復上一次的進度的情況。
4.使用IntentService
Service是執行在主執行緒中的,如果Servcie中有阻塞的操作,超過10秒的話會造成ANR。為避免這種情況發生,可以在Service中啟用新的執行緒。或者繼承IntentService類。
IntentService完成的工作:
1.建立一個單獨的執行緒來執行 onStartCommand中的任務。
2.建立一個工作佇列,每次將一個intent 傳到onHandleIntent的實現中
3.提供一個 onStartCommand的預設實現,將intent送往工作佇列,並由onHandleIntent處理
4.當所有任務完成後,自己呼叫 stopSelf
參照IntentService的原始碼:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
protected abstract void onHandleIntent(Intent intent);
在onCreate的時候建立了子執行緒,並用它的Looper物件建立一個handler用來接收任務請求,在onStartCommand中呼叫onStart發出handler請求Message中封裝了請求的intent。在ServiceHandler這個內部類的Handler中的handleMessage裡,呼叫onHandleIntent做具體的耗時操作,任務結束後,呼叫stopSelf結束Service。
使用IntentService要簡單的多,實現自己的IntentService只需要2步:
1.重寫構造方法。
2.實現onHandleIntent方法,耗時的操作寫在onHandleIntent中。
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
二.繫結的(Bound)Service
當實現一個可供繫結的Service時,需要在Service中提供Ibinder介面。有三種方式可以提供Ibinder介面,分別是繼承Binder類,使用Messenger,使用AIDL。
(一).繼承Binder類。
如果需要繫結的Service對應用是私有的,並且不存在跨程序的通訊情況,那麼應該使用這種方式。
實現方式:
1.在Service類中,建立繼承Binder的類,這個類包含返回當前Service例項的方法,或者返回其他元件可以呼叫的公共方法。
2.在 onBind()回撥中返回Binder類的例項。
3.在繫結Service的元件中,例項化ServiceConnection,並且在ServiceConnection的onServiceConnected回撥中獲得Binder的例項,用它呼叫Service中的公共方法。
例子:
Service:
public class MyService extends Service{
private final IBinder mBinder = new LocalBinder();
private final Random mGenerator = new Random();
public class LocalBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
//onBind方法返回Binder的實現類
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
//供其他元件呼叫的公共方法
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
@Override
public void onCreate() {
Log.v("test","onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("test","onStartCommand");
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
Log.v("test","onDestroy");
}
}
LocalBinder 類是Binder的實現類,裡面提供了getService方法返回這個Service的例項,繫結此Service的元件可以在ServiceConnection的onServiceConnected中,通過這個方法獲取Service的例項,以呼叫其他公共方法。
Activity:
public class MainActivity extends ActionBarActivity {
private Button showNumber;
private MyService mService;
boolean mBound = false; //是否已經綁定了Service
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showNumber = (Button) findViewById(R.id.showNumber);
showNumber.setOnClickListener(btnListener);
}
@Override
protected void onStart() {
super.onStart();
//繫結Service
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
MyService.LocalBinder localBinder = (MyService.LocalBinder)iBinder;
mService = localBinder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
private View.OnClickListener btnListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mBound) {
int num = mService.getRandomNumber();
Toast.makeText(MainActivity.this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
};
}
佈局xml:
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.test.workspace1.app.MainActivity">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true">
<Button
android:id="@+id/showNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="顯示隨機數"
android:layout_centerInParent="true"
/>
</RelativeLayout>
</RelativeLayout>
這個Activity在onStart的時候綁定了Service,在繫結期間,通過Service的例項,不斷呼叫其getRandomNumber方法以獲得不同的隨機數。(二)使用Messenger
如果Service需要和其他程序互動(IPC),可以使用Messenger為Service提供介面。
使用Messenger的概要:
1.在Service內部實現Handler,接受客戶端請求,這個Handler作為例項化Messenger的構造方法的引數
2.在Service的onBind()回撥中通過建立的Messenger物件返回IBinder物件的例項。
3.客戶端用Ibinder例項作為引數例項化 Messenger,Messenger用來向Service傳送訊息,Service在Handler內部的 handleMessage方法中處理請求。
例子:
應用A的Service:
public class MessengerService extends Service{
//記錄所有繫結的客戶端數量
private ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private int mValue = 0;
private static final int MSG_REGISTER_CLIENT = 1;
private static final int MSG_UNREGISTER_CLIENT = 2;
private static final int MSG_SET_VALUE = 3;
//這個handler接受客戶端的請求
class IncomingHandler extends Handler {
//處理客戶端的資料
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
mValue = msg.arg1;
for (int i = mClients.size()-1; i >= 0; i--) {
//向客戶端傳送客戶端傳來的最新資料
try {
mClients.get(i).send(Message.obtain(null, MSG_SET_VALUE, mValue, 0));
} catch (RemoteException e) {
mClients.remove(i);
e.printStackTrace();
}
}
break;
default:
super.handleMessage(msg);
}
}
}
//IncomingHandler作為Messenger的構造方法引數
final Messenger mMessenger = new Messenger(new IncomingHandler());
//onBind方法中通過建立的Messenger物件返回IBinder物件的例項
@Override
public IBinder onBind(Intent intent) {
Log.v("test","onBind");
return mMessenger.getBinder();
}
@Override
public void onCreate() {
Log.v("test","onCreate");
}
@Override
public void onDestroy() {
Log.v("test","onDestroy");
}
}
在這個Service內部實現了IncomingHandler,接受客戶端請求,這個Handler作為例項化Messenger(mMessenger)的構造方法的引數。在handler中,當收到MSG_SET_VALUE的訊息後,將這個值傳送給所有繫結它的客戶端。應用A的Manifest檔案:
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".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>
<service android:name=".MessengerService" >
<intent-filter>
<action android:name="com.test.MessengerService" />
</intent-filter>
</service>
</application>
應用B的Activity:
public class MainActivity extends ActionBarActivity {
private Messenger mService = null;
private boolean mBound = false;
private TextView mCallbackText;
private static final int MSG_REGISTER_CLIENT = 1;
private static final int MSG_UNREGISTER_CLIENT = 2;
private static final int MSG_SET_VALUE = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bindBtn = (Button) findViewById(R.id.bind);
bindBtn.setOnClickListener(mBindListener);
Button unbindBtn = (Button) findViewById(R.id.unbind);
unbindBtn.setOnClickListener(mUnbindListener);
mCallbackText = (TextView)findViewById(R.id.text);
}
private View.OnClickListener mBindListener = new View.OnClickListener() {
public void onClick(View v) {
doBindService();
}
};
private View.OnClickListener mUnbindListener = new View.OnClickListener() {
public void onClick(View v) {
doUnbindService();
}
};
private void doBindService() {
Intent intent = new Intent("com.test.MessengerService");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
mBound = true;
mCallbackText.setText("繫結.");
}
private void doUnbindService() {
if (mBound) {
if (mService != null) {
try {
//向Service傳送解除繫結的訊息
Message msg = Message.obtain(null, MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
mBound = false;
mCallbackText.setText("解除繫結.");
// msg = Message.obtain(null, MSG_SET_VALUE, this.hashCode(), 0);
// mService.send(msg);
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService = new Messenger(iBinder);
Toast.makeText(MainActivity.this, "與Service建立連線", Toast.LENGTH_SHORT).show();
try {
Message msg = Message.obtain(null, MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
//向Service傳送一串字元
msg = Message.obtain(null, MSG_SET_VALUE, this.hashCode(), 0);
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mService = null;
Toast.makeText(MainActivity.this, "與Service斷開連線", Toast.LENGTH_SHORT).show();
}
};
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_VALUE:
mCallbackText.setText("service傳遞的數字: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
}
//與Service中初始化的方法一樣
final Messenger mMessenger = new Messenger(new IncomingHandler());
}
MainActivity在繫結Service後,在onServiceConnected回撥中通過new Messenger(iBinder);得到Service的Messenger物件,通過這個Messenger物件傳送訊息,Service的handler可以接收到請求並處理,同時在MainActivity中有個全域性變數mMessenger,它初始化的方法和Service中是一樣的。在onServiceConnected中將它賦值給Message物件的replyTo屬性,通過Service的Messenger物件傳送給Service的handler,這樣Service中可以通過Activity傳送過去的Messenger物件,向Activity傳送請求,由Activity中的handler處理。
以上的Service和Activity在2個不同的APP中,它們完成的互動是:Activity將一段hashCode傳送給繫結的Service,Service記錄所有繫結自己的客戶端,並且一旦收到最新的hashCode,就將他們傳送給所有繫結的客戶端,所有繫結的客戶端中的Activity收到返回的hashCode後顯示在TextView上。這樣就通過Messenger物件,完成了2個不同程序收發訊息的互動。
執行效果:
相比Messenger,AIDL適用於讓多個APP同時訪問你的Service進行通訊的情況,一般的應用用不到AIDL,下篇單獨寫。
繫結Service的生命週期:
當Service在客戶端呼叫bindService時是不會回撥onStartCommand方法的,而是呼叫onBind,呼叫unbindService後,onUnbind方法回撥,接下來是onDestroy。
當一個Service綁定了多個客戶端時,只有所有的客戶端呼叫unbindService後,Service才會銷燬。
Service有2個回撥方法 onRebind和 onUnbind,當onUnbind返回true時,客戶端呼叫bindService時,Service會執行onRebind方法而不是onBind。