Android Service、IntentService,Service和组件间通信
Service組件
Service 和Activity 一樣同為Android 的四大組件之一,并且他們都有各自的生命周期,要想掌握Service 的用法,那就要了解Service 的生命周期有哪些方法,并且生命周期中各個方法回調的時機和作用
什么是service?service的基本概念
Service是Android中實現程序后臺運行的解決方案,非常適合用于去執行哪些不需要和用戶交互而且還要求長期運行的任務。不能運行在一個獨立的進程當中,而是依賴與創建服務時所在的應用程序進程。只能在后臺運行,并且可以和其他組件進行交互。
Service可以在很多場合使用,比如播放多媒體的時候用戶啟動了其他Activity,此時要在后臺繼續播放;比如檢測SD卡上文件的變化;比如在后臺記錄你的地理信息位置的改變等等,總之服務是藏在后臺的。
定義(啟動)一個Service
Service 有兩種啟動方式,并且它的兩種啟動方式的生命周期是不一樣的。
1.startService方式啟動Service
當應用組件通過startService方法來啟動Service 時,Service 則會處于啟動狀態,一旦服務啟動,它就會在后臺無限期的運行,生命周期獨立于啟動它的組件,即使啟動它的組件已經銷毀了也不受任何影響,由于啟動的服務長期運行在后臺,這會大量消耗手機的電量,因此,我們應該在任務執行完成之后調用stopSelf()來停止服務,或者通過其他應用組件調用stopService 來停止服務。
startService 啟動服務后,會執行如下生命周期:onCreate() -> onStartCommand() -> onStart()(現在已經廢棄) -> onDestroy() 。具體看一下它的幾個生命周期方法:
-
onCreate() :首次啟動服務的時候,系統會調用這個方法,在onStartCommand 和 onBind 方法之前,如果服務已經啟動起來了,再次啟動時,則不會調用此方法,因此可以在onCreate 方法中做一些初始化的操作,比如要執行耗時的操作,可以在這里創建線程,要播放音樂,可以在這里初始化音樂播放器。
-
onStartCommand():?當通過startService 方法來啟動服務的時候,在onCreate 方法之后就會回調這個方法,此方法調用后,服務就啟動起來了,將會在后臺無限期的運行,直到通過stopService 或者 stopSelf 方法來停止服務。
-
onDestroy():當服務不再使用且將被銷毀時,系統將調用此方法。服務應該實現此方法來清理所有資源,如線程、注冊的偵聽器、接收器等。 這是服務接收的最后一個調用。
了解了這幾個生命周期方法后,就來寫一個簡單Service 。
要使用Service 就要通過繼承Service類(或者繼承IntentService ,后文會講)來實現,代碼如下:
package com.example.servicetest;import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log;public class MyService extends Service { public static final String TAG = "MyService"; //創建服務時調用 @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate"); } //服務執行的操作 @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand"); return super.onStartCommand(intent, flags, startId); } //銷毀服務時調用 @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); } @Override public IBinder onBind(Intent intent) { return null; } }可以看到,我們只是在onCreate()、onStartCommand()和onDestroy()方法中分別打印了一句話,并沒有進行其它任何的操作,注意代碼注釋中這三個方法的作用。
onBind()方法是Service中唯一的一個抽象方法,所以必須要在子類里實現。我們知道,Service可以有兩種啟動方式:一種是startService(),另一種是bindService()。第二種啟動方式才會用到onBind()方法。我們這先用第一種方式啟動Service,所以暫時忽略onBind()方法。
(2)在清單文件中聲明:(和Activity標簽并列)
<service android:name=".MyService"> </service>(3)修改activity_main.xml代碼,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1_start_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Service" /> <Button android:id="@+id/button2_stop_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Stop Service" /> </LinearLayout>?
我們在布局文件中加入了兩個按鈕,一個用于啟動Service,一個用于停止Service。
(4)在MainActivity作為程序的主Activity,在里面加入啟動Service和停止Service的邏輯,代碼如下:
package com.example.servicetest;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 MainActivity extends Activity implements OnClickListener { private Button button1_start_service; private Button button2_stop_service; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button1_start_service = (Button) findViewById(R.id.button1_start_service); button2_stop_service = (Button) findViewById(R.id.button2_stop_service); button1_start_service.setOnClickListener(this); button2_stop_service.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button1_start_service: Intent startIntent = new Intent(this, MyService.class); startService(startIntent); break; case R.id.button2_stop_service: Intent stopIntent = new Intent(this, MyService.class); stopService(stopIntent); break; default: break; } } }代碼所示,在Start Service按鈕的點擊事件里,我們構建出了一個Intent對象,并調用startService()方法來啟動MyService。然后在Stop Serivce按鈕的點擊事件里,我們同樣構建出了一個Intent對象,并調用stopService()方法來停止MyService。代碼的邏輯非常簡單。
小結:通過startService 方式啟動的服務,服務會無限期的在后臺運行,直到通過stopService 或 stopSelf 來終止服務。服務獨立于啟動它的組件,也就是說,當組件啟動服務后,組件和服務就再也沒有關系了,就算啟動它的組件被銷毀了,服務照樣在后臺運行。通過這種方式啟動的服務不好與組件之間通信。
bindService 方式啟動服務
除了startService 來啟動服務之外,另外一種啟動服務的方式就是通過bindService 方法了,也就是綁定服務,其實通過它的名字就容易理解,綁定即將啟動組件和服務綁定在一起。前面講的通過startService 方式啟動的服務是與組件相獨立的,即使啟動服務的組件被銷毀了,服務仍然在后臺運行不受干擾。但是通過bindSerivce 方式綁定的服務就不一樣了,它與綁定組件的生命周期是有關的。如下:
多個組件可以綁定到同一個服務上,如果只有一個組件綁定服務,當綁定的組件被銷毀時,服務也就會停止了。如果是多個組件綁定到一個服務上,當綁定到該服務的所有組件都被銷毀時,服務才會停止。
bindService 綁定服務 和startService 的生命周期是不一樣,bindServie 的生命周期如下:onCreate -> onBind -> onUnbind ->onDestroy。其中重要的就是onBind 和onUnbind 方法。
-
onBind():?當其他組件想通過bindService 與服務綁定時,系統將會回調這個方法,在實現中,你必須返回一個IBinder接口,供客戶端與服務進行通信,必須實現此方法,這個方法是Service 的一個抽象方法,但是如果你不允許綁定的話,返回null 就可以了。
-
onUnbind():?當所有與服務綁定的組件都解除綁定時,就會調用此方法。
了解了這2個方法后,我們來看一下怎么綁定一個服務。
1,首先,添加一個類 繼承 Binder ,在Binder 類中添加其他組件要與服務交互的方法,并在onBind() 方法中返回IBinder 實例對象:
2, 綁定服務的時候,需要提供一個ServiceConnection 接口,在接口回調中獲取Binder 對象,與服務進行通信。
private SimpleService.MyBinder mMyBinder;// 綁定/解除綁定 Service 回調接口private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 綁定成功后回調//1 ,獲取Binder接口對象mMyBinder = (SimpleService.MyBinder) service;//2, 從服務獲取數據String content = mMyBinder.getStringInfo();// 3,界面提示Toast.makeText(ServiceSimpleActivity.this,content,Toast.LENGTH_LONG).show();}@Overridepublic void onServiceDisconnected(ComponentName name) {// 解除綁定后回調mMyBinder = null;}};3,綁定和解除綁定服務
case R.id.bind_service:Intent intent = new Intent(this,SimpleService.class);// 綁定服務 bindService(intent,mConnection, Context.BIND_AUTO_CREATE);break;case R.id.unbind_service:// 解除綁定服務 unbindService(mConnection);break;可以看到,綁定服務的生命周期內依次調用了onCreate ,onBind,onUnbind 和 onDestroy 方法,只有中間兩個生命周期方法與startService 啟動服務是不同的。
兩種方式的生命周期的異同:
Service生命周期.png
started服務與bind服務的區別:
區別一:生命周期
- 通過started方式的服務會一直運行在后臺,需要由組件本身或外部組件來停止服務才會以結束運行
- bind方式的服務,生命周期就要依賴綁定的組件
區別二:參數傳遞
- started服務可以給啟動的服務對象傳遞參數,但無法獲取服務中方法的返回值
- bind服務可以給啟動的服務對象傳遞參數,也可以通過綁定的業務對象獲取返回結果
實際開發中的技巧;
- 第一次先使用started方式來啟動一個服務
- 之后可以使用bind的方式綁定服務,從而可以直接調用業務方法獲取返回值
IntentService
IntentService 是Service 的子類,它使用工作線程逐一處理所有啟動請求,果您不要求服務同時處理多個請求,這是最好的選擇。 您只需實現 onHandIntent方法即可,該方法會接收每個啟動請求的 Intent,使您能夠執行后臺工作。
IntentService 示例
IntentService 默認為我們開啟了一個工作線程,在任務執行完畢后,自動停止服務,因此在我們大多數的工作中,使用IntentService 就夠了,并且IntentService 比較簡單,只要實現一個方法OnHandleIntent,接下來看一下示例:
(1)新建一個MyIntentService類,繼承自IntentService,并重寫父類的onHandleIntent()方法,代碼如下:
1 package com.example.servicetest;2 3 import android.app.IntentService;4 import android.content.Intent;5 import android.util.Log;6 7 public class MyIntentService extends IntentService{8 9 public MyIntentService() { 10 super("MyIntentService");//調用父類有參構造函數。這里我們手動給服務起個名字為:MyIntentService 11 // TODO Auto-generated constructor stub 12 } 13 14 //該方法在會在一個單獨的線程中執行,來完成工作任務。任務結束后,該Service自動停止 15 @Override 16 protected void onHandleIntent(Intent intent) { 17 // TODO Auto-generated method stub 18 for(int i = 0;i<3;i++) { 19 //打印當前線程的id 20 Log.d("MyIntentService","IntentService線程的id是:"+Thread.currentThread().getId()); 21 try { 22 Thread.sleep(1000); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 } 28 } 29 30 @Override 31 public void onDestroy() { 32 // TODO Auto-generated method stub 33 super.onDestroy(); 34 Log.d("MyIntentService","onDestroy"); 35 } 36 }這里首先要提供一個無參的構造方法,并且必須在其內部調用父類的有參構造方法(9至12行),我們在第10行手動將服務的名字改為“MyIntentService”。
然后在子類中實現onHandleIntent()這個抽象方法,可以在這個方法里去處理一些具體的邏輯,我們就用三次for循環,打印當前線程的id,每次延時1秒。
因為這個服務在運行結束后會自動停止,所以我們在onDestroy()方法中打印日志驗證一下。
(2)在清單文件中對服務進行注冊服務:
<service android:name=".MyIntentService"> </service>(3)在activity_main.xml中添加一個按鈕button3_stop_intentservice,用于啟動MyIntentService服務,代碼略。
(4)在MainActivity里面加入啟動IntentService的邏輯,核心代碼如下:
1 case R.id.button3_stop_intentservice: 2 Log.d("MainActivity","主線程的id是:"+Thread.currentThread().getId()); 3 Intent intentService = new Intent(this,MyIntentService.class); 4 startService(intentService); 5 default:我們在第02行中,打印主線程的id。
運行程序,點擊按鈕button3_stop_intentservice,顯示如下:
由此可見,啟動一個IntentService和啟動一個普通的Service,步驟是一樣的。
4、Service和Thread的關系:
不少Android初學者都可能會有這樣的疑惑,Service和Thread到底有什么關系呢?什么時候應該用Service,什么時候又應該用Thread?答案可能會有點讓你吃驚,因為Service和Thread之間沒有任何關系!
之所以有不少人會把它們聯系起來,主要就是因為Service的后臺概念。Thread我們大家都知道,是用于開啟一個子線程,在這里去執行一些耗時操作就不會阻塞主線程的運行。而Service我們最初理解的時候,總會覺得它是用來處理一些后臺任務的,一些比較耗時的操作也可以放在這里運行,這就會讓人產生混淆了。但是,如果我告訴你Service其實是運行在主線程里的,你還會覺得它和Thread有什么關系嗎?
其實,后臺和子線程是兩個完全不同的概念:
Android的后臺就是指,它的運行是完全不依賴UI的。即使Activity被銷毀,或者程序被關閉,只要進程還在,Service就可以繼續運行。比如說一些應用程序,始終需要與服務器之間始終保持著心跳連接,就可以使用Service來實現。你可能又會問,Service既然是運行在主線程里,在這里一直執行著心跳連接,難道就不會阻塞主線程的運行嗎?當然會,但是我們可以在Service中再創建一個子線程,然后在這里去處理耗時邏輯就沒問題了。
既然在Service里也要創建一個子線程,那為什么不直接在Activity里創建呢?這是因為Activity很難對Thread進行控制,當Activity被銷毀之后,就沒有任何其它的辦法可以再重新獲取到之前創建的子線程的實例;而且在一個Activity中創建的子線程,另一個Activity無法對其進行操作。但是Service就不同了,所有的Activity都可以與Service進行關聯,然后可以很方便地操作其中的方法,即使Activity被銷毀了,之后只要重新與Service建立關聯,就又能夠獲取到原有的Service中Binder的實例。因此,使用Service來處理后臺任務,Activity就可以放心地finish,完全不需要擔心無法對后臺任務進行控制的情況。
所以說,一個比較標準的Service,就可以寫成本段中第1節的樣子。
IntentService總結:IntentService是Service 的子類,默認給我們開啟了一個工作線程執行耗時任務,并且執行完任務后自 動停止服務。擴展IntentService比較簡單,提供一個構造方法和實現onHandleIntent 方法就可了,不用重寫父類的其他方法。但是如果要綁定服務的話,還是要重寫onBind 返回一個IBinder 的。使用Service 可以同時執行多個請求,而使用IntentService 只能同時執行一個請求。
Service 與應用組件通信的幾種方式
1,BroadcastReceiver
通過前文我們知道,startService方式啟動的服務在后臺,無限期地運行,并且與啟動它的組件是獨立的,啟動Service 之后也就與啟動它的組件沒有任何關系了。因此它是不能與啟動它的組件之間相互通信的。雖然Service 沒有提供這種啟動方式的通信方法,我們還是可以通過其他方式來解決的,這就用到了BroadcastReceiver。
場景描述:通過startService 啟動一個長期在后臺運行的下載圖片服務,然后在界面上點擊下載按鈕,通過intent 傳遞一個下載鏈接給Service,在下載完成后,通過BroadcastReceiver 通知Activity 界面顯示圖片。看一下代碼實現:
Service代碼如下:
public class DownloadService extends Service {public static final String IMAGE = "iamge_url";public static final String RECEIVER_ACTION = "com.zhouwei.simpleservice";private static final String TAG = "DownloadService";public static final String ACTION_START_SERVICER = "com.zhouwei.startservice";public static final String ACTION_DOWNLOAD = "com.zhouwei.startdownload";private Looper mServiceLooper;private ServiceHandler mServiceHandler;private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper){super(looper);}@Overridepublic void handleMessage(Message msg) {// 工作線程做耗時下載String url = (String) msg.obj;Bitmap bitmap = null;try {bitmap = Picasso.with(getApplicationContext()).load(url).get();Intent intent = new Intent();intent.putExtra("bitmap",bitmap);intent.setAction(RECEIVER_ACTION);// 通知顯示 LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);} catch (IOException e) {e.printStackTrace();}//工作完成之后,停止服務 stopSelf();}}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {// 開啟一個工作線程做耗時工作HandlerThread thread = new HandlerThread("ServiceHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);thread.start();// 獲取工作線程的LoopermServiceLooper = thread.getLooper();// 創建工作線程的HandlermServiceHandler = new ServiceHandler(mServiceLooper);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i(TAG,"call onStartCommand...");if(intent.getAction().equals(ACTION_DOWNLOAD)){handleCommand(intent);}else if(intent.getAction().equals(ACTION_START_SERVICER)){//do nothing }return START_STICKY;}private void handleCommand(Intent intent){String url = intent.getStringExtra(IMAGE);// 發送消息下載Message message = mServiceHandler.obtainMessage();message.obj = url;mServiceHandler.sendMessage(message);} }新建了一個DownloadService ,在里面啟動了一個工作線程,在線程里下載圖片,然后通過BroadcastReceiver 通知Activity顯示。
Activity的代碼很簡單,注冊BroadcastReceiver,在onReceiver中顯示圖片就好了,代碼如下:
private ImageView mImageView;private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// 顯示圖片Bitmap bitmap = intent.getParcelableExtra("bitmap");mImageView.setImageBitmap(bitmap);}};/*** 啟動下載*/private void startDownload(){Intent intent = new Intent(this,DownloadService.class);// 啟動服務intent.putExtra(DownloadService.IMAGE,"http://www.8kmm.com/UploadFiles/2012/8/201208140920132659.jpg");intent.setAction(DownloadService.ACTION_DOWNLOAD);startService(intent);}?
?
?
?
?
?
?
?
?
?
聲明
https://blog.csdn.net/qq_34115898/article/details/83347882
文章來源
轉載于:https://www.cnblogs.com/ythzxxq/p/9978158.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Android Service、IntentService,Service和组件间通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: docker 出现 Error resp
- 下一篇: Android Studio 管理所有