標籤:
service用於長期在幕後處理任務,而不需要對使用者可見。
service有2種基本的啟動方式:
startService():使用這種方式,來進行單一的任務,不需要返回結果給調用者
bindService():與上面的相反。
下面是一些關於服務的重要說明,非常值得詳細瞭解的:
繼承service,實現自己的service;
在manifest中聲明service,服務位於主線程,並不會建立自己的子線程。
下面是一些重寫的方法:
onCreate();當服務被建立時調用,只調用一次。
onStartCommand();它與startService()對應,當服務啟動後調用。如果你重寫了該方法,你就有責任自己去
當任務結束以後,調用stopSelf()或者stopService()來停止服務。如果你是綁定的服務,就不需重新該方法了。
onBind();它與bindService()對應,通過返回IBinder,來與service交流。如果你並不像綁定它,就直接返回null
onDestroy();當服務不再被使用時需要銷毀時調用,你應該在這裡用來停止線程,登出監聽器,廣播。
如果一個組件如activity使用的是 startService()來啟動服務的話,就會觸發 onStartCommand(),然後服務就會一直運行,直到任務結束;服務的停止需要
手動控制:在啟動服務的組件中調用 stopService()或者在服務本類中調用stopSelf()
如果一個組件使用的是bindService()來啟動服務的話,該服務就會運行,直到組件不約束它。
在系統記憶體很低的情況下,系統會強制停止服務,來恢複用於運行activity;但是下面這些情況下的服務不易被系統停止:服務與activity綁定或者
服務位於前台。但是如果服務最後還是被停止了,我們要重新啟用服務。
service的建立和使用,
也可以使用IntentService,它是service子類,在處理背景工作時,不需要你自己開線程,可以直接在 onHandleIntent()中進行任務。
<span style="font-size:14px;">1.<manifest ... > ... <application ... > <service android:name=".ExampleService" android:exported="false" /> ... </application></manifest>public class HelloIntentService extends IntentService { A constructor is required, and must call the super IntentService(String) constructor with a name for the worker thread. public HelloIntentService() { super("HelloIntentService"); } * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. @Override protected void onHandleIntent(Intent intent) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000); } catch (InterruptedException e) { // Restore interrupt status. Thread.currentThread().interrupt(); } }}</span>
<span style="font-size:14px;"></span>
使用intentservice,在任務結束後會自動關閉服務。
2.使用startService()來啟動服務,一般在不需要互動的情況下使用這種方式,在onStartCommand()中接受Intent ;
Intent intent = new Intent(this, HelloService.class);
startService(intent);
如果你的服務沒有經過綁定,那麼startService(intent)中的intent就是唯一的向service互動的方式;service中通過廣播來向外發布回調。如果多次啟動服務,會導致onStartCommand()被多次調用。
onStartCommand()必須返回一個整形值,描述這當系統殺掉服務時,系統該如何繼續service
START_NOT_STICKY,殺掉後,不再重建
START_STICKY,殺掉後,自動重啟,然後接收的intent=null
START_REDELIVER_INTENT,殺掉後,自動重啟,然後接收的intent不為空白;像檔案下載
3.使用bindService()啟動;這種方式使用更加複雜,但也更加靈活。當你需要與服務互動的時候,可以採用這種方式;
必須重寫 onBind()方法返回IBinder,來與其他組件交流。在其他組件中擷取該IBinder對象
有一種client-server的感覺,可以有多個用戶端與server交流
用戶端組件,必須建立ServiceConnection,用來監聽與service的串連;
可以有多個用戶端串連service,但是service的onBind只會執行一次,所以分發給其他用戶端的都是同一個IBinder對象
只有在多個用戶端都解除綁定了服務之後,服務才會被銷毀。
使用這種服務有個最重要的地方就是定義IBinder;
有3種方式:
a.直接繼承Binder,這也是一般app常用的一種方式,適用於你的服務與你的app是處於同一個進程中的。你的服務僅僅是為了
處理背景工作。
b.使用Messenger,適用於需要跨進程通訊且安全執行緒且非並發,同一個時間只能接受到一個請求,你可以使用Messenger和Handler這2個 類來進行service和client的互動。
服務端建立信使對象,通過通過onbind將服務端信使對象放在IBind中回調給用戶端,這樣子客戶端就可以拿到服務端信使對象,使用服務端信使對象給服務端發資訊了。
在用戶端串連上服務端後,可以建立用戶端信使對象,將用戶端信使對象以訊息的形式,發送給服務端,服務端拿到用戶端信使對象後可以給用戶端發訊息。
這個是安全執行緒的。因為都是在同一個隊列裡,在同一個線程裡進行的。它本身是基於AIDL構建的。
c.使用AIDL,適用於需要跨進程,且非安全執行緒,能夠同一時間處理多個請求的場合。
它的使用是建立一個.aidl file檔案,裡面定義介面,然後android sdk tool使用它來產生一個抽象類別,抽象類別裡實現各種介面方法。
注意:對於大多數應用,我們都不應該使用AIDL來bindservice.因為這會使得結果更複雜。
首先我們看第一種方式;直接繼承Binder,例如一個情境一個app是用來音樂播放,它關聯了一個activity。activity啟用服務來後台播放音樂。
服務端:
<span style="font-size:14px;">public class LocalService extends Service { // Binder given to clients private final IBinder mBinder = 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 { LocalService getService() { // Return this instance of LocalService so clients can call public methods return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } //method for clients public int getRandomNumber() { return mGenerator.nextInt(100); }}</span>
用戶端:
<span style="font-size:14px;">public class BindingActivity extends Activity { LocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @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) { // Call a method from the LocalService. // However, if this call were something that might hang, then this request should // occur in a separate thread to avoid slowing down the activity performance. int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } // 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; } };}</span>
接下來我們看第二種方式;使用Messenger,
如果您只需要在 Activity 可見時與服務互動,則應在 onStart() 期間綁定,在 onStop() 期間取消綁定。
如果您希望 Activity 在後台停止運行狀態下仍可接收響應,則可在 onCreate() 期間綁定,在 onDestroy() 期間取消綁定。請注意,這意味著您的 Activity 在其整個運行過程中(甚至包括後台運行期間)都需要使用服務,因此如果服務位於其他進程內,那麼當您提高該進程的權重時,系統終止該進程的可能性會增加
下面是個完整的例子;用戶端和服務端使用Messenger來通訊。
服務端:Server_Service
<span style="font-size:14px;">package com.example.administrator.service_messenger;import android.app.Service;import android.content.Intent;import android.content.ServiceConnection;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.Messenger;import android.os.RemoteException;import android.widget.Toast;/** * 在manifest資訊清單檔中聲明service為另起新的進程。 */public class Server_Service extends Service { private Messenger clientMessenger; /** * 服務端的handler */ class ServerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case Client.MSG_FROM_CLIENT: String fromClient = msg.getData().getString("msgFromClient"); //收到來自用戶端的資訊 Toast.makeText(getApplicationContext(), fromClient, Toast.LENGTH_SHORT).show(); //回複一下用戶端 Message msgFromServer = Message.obtain(null, Client.MSG_FROM_SERVER); msgFromServer.arg1 =200; try { clientMessenger.send(msgFromServer); } catch (RemoteException e) { e.printStackTrace(); } break; case Client.MSG_FROM_CLIENT_MESSE: //得到用戶端的信使對象 clientMessenger = msg.replyTo; break; } } } /** * 服務端的Messenger,使用服務端的handler做參數構造。 */ final Messenger serverMessenger = new Messenger(new ServerHandler()); /** * 我們在這裡使用服務端的信使對象serverMessenger的IBinder,將其返回給用戶端中的 * serviceConntected */ @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "用戶端綁定服務端成功", Toast.LENGTH_SHORT).show(); return serverMessenger.getBinder(); } @Override public void unbindService(ServiceConnection conn) { super.unbindService(conn); Toast.makeText(getApplicationContext(), "用戶端取消了服務綁定", Toast.LENGTH_SHORT).show(); }}</span>在manifest下配置:
<span style="font-size:14px;"><service android:name=".Server_Service" android:process=":v1"/></span>
用戶端:Client
<span style="font-size:14px;">package com.example.administrator.service_messenger;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.Messenger;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Toast;import java.io.UnsupportedEncodingException;import java.util.Calendar;public class Client extends AppCompatActivity { public static final int MSG_FROM_CLIENT = 1; public static final int MSG_FROM_CLIENT_MESSE = 2; public static final int MSG_FROM_SERVER = 3; //在用戶端裡拿到了服務端的信使對象 Messenger serverMessenger = null; //標記是否已經連上了服務端 boolean mBound; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_client); //綁定服務 findViewById(R.id.bt_bind).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { bindService(); } }); //通過服務端的信使來發資訊給服務端 findViewById(R.id.bt_send).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { sendInfoToServer(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } }); //手動解除綁定服務 findViewById(R.id.bt_unbind).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { unBindService(); } }); } @Override protected void onDestroy() { super.onDestroy(); //在activity銷毀時,可以選擇取消綁定服務 unBindService(); } //用戶端的handler private Handler clientHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_FROM_SERVER: Toast.makeText(getApplicationContext(), "server received :"+msg.arg1+"", Toast.LENGTH_SHORT).show(); break; } } }; /** * 用戶端的Messenger,使用客戶的handler做參數構造。 */ final Messenger clientMessenger = new Messenger(clientHandler); private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { //當我們串連上服務端後,會調用該方法。在方法裡會回調給我們IBinder對象, //由於bindservice綁定服務後,服務端返回給用戶端的IBinder是同一個。 //所以這裡我們可以利用IBinder來構建服務端裡的信使對象。 //之後有了服務端的信使對象,就可以隨便網服務端發送資訊了。 //構建服務端信使對象 serverMessenger = new Messenger(service); mBound = true; //並將用戶端的messenger發給服務端,讓服務端也可以給用戶端發送訊息 Message msg = Message.obtain(null, MSG_FROM_CLIENT_MESSE); msg.replyTo = clientMessenger; try { serverMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } public void onServiceDisconnected(ComponentName className) { // 當串連服務端異常時調用 serverMessenger = null; mBound = false; } }; private void sendInfoToServer() throws UnsupportedEncodingException { if (!mBound) return; Message msg = Message.obtain(null, MSG_FROM_CLIENT, 0, 0); String strMsg = "server received:" + Calendar.getInstance().getTime(); Bundle data = new Bundle(); data.putString("msgFromClient", strMsg); msg.setData(data); try { serverMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onStart() { super.onStart(); // 在介面可見時,可以選擇去綁定服務 } @Override protected void onStop() { super.onStop(); // 在介面不可見時,可以選擇去解除綁定服務 } private void bindService() { bindService(new Intent(this, Server_Service.class), mConnection, Context.BIND_AUTO_CREATE); } private void unBindService() { if (mBound) { unbindService(mConnection); mBound = false; } }}</span>
最後我們來看第三種方式使用AIDL:
android中Service使用詳解