1. 程式人生 > >Android中Service的使用詳解和注意點(LocalService)

Android中Service的使用詳解和注意點(LocalService)

開始,先稍稍講一點android中Service的概念和用途吧~

Service分為本地服務(LocalService)和遠端服務(RemoteService):

1、地服務依附在主程序上而不是獨立的程序,這樣在一定程度上節約了資源,另外Local服務因為是在同一程序因此不需要IPC,也不需要AIDL。相應bindService會方便很多。主程序被Kill後,服務便會終止。

2、程服務為獨立的程序,對應程序名格式為所在包名加上你指定的android:process字串。由於是獨立的程序,因此在Activity所在程序被Kill的時候,該服務依然在執行,不受其他程序影響,有利於為多個程序提供服務具有較高的靈活性。該服務是獨立的程序,會佔用一定資源,並且使用AIDL進行IPC稍微麻煩一點。

按使用方式可以分為以下三種:

1、startService 啟動的服務:主要用於啟動一個服務執行後臺任務,不進行通訊。停止服務使用stopService;

2、bindService 啟動的服務:該方法啟動的服務可以進行通訊。停止服務使用unbindService;

3、startService 同時也 bindService 啟動的服務:停止服務應同時使用stepService與unbindService

Service 與 Thread 的區別

很多時候,你可能會問,為什麼要用 Service,而不用 Thread 呢,因為用 Thread 是很方便的,比起 Service 也方便多了,下面我詳細的來解釋一下。

1). Thread:Thread 是程式執行的最小單元,它是分配CPU的基本單位。可以用 Thread 來執行一些非同步的操作。

2). Service:Service 是android的一種機制,當它執行的時候如果是Local Service,那麼對應的 Service 是執行在主程序的 main 執行緒上的。如:onCreate,onStart 這些函式在被系統呼叫的時候都是在主程序的 main 執行緒上執行的。如果是Remote Service,那麼對應的 Service 則是執行在獨立程序的 main 執行緒上。因此請不要把 Service 理解成執行緒,它跟執行緒半毛錢的關係都沒有!

既然這樣,那麼我們為什麼要用 Service 呢?其實這跟 android 的系統機制有關,我們先拿 Thread 來說。Thread 的執行是獨立於 Activity 的,也就是說當一個 Activity 被 finish 之後,如果你沒有主動停止 Thread 或者 Thread 裡的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這裡會出現一個問題:當 Activity 被 finish 之後,你不再持有該 Thread 的引用。另一方面,你沒有辦法在不同的 Activity 中對同一 Thread 進行控制。

舉個例子:如果你的 Thread 需要不停地隔一段時間就要連線伺服器做某種同步的話,該 Thread 需要在 Activity 沒有start的時候也在執行。這個時候當你 start 一個 Activity 就沒有辦法在該 Activity 裡面控制之前建立的 Thread。因此你便需要建立並啟動一個 Service ,在 Service 裡面建立、執行並控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一 Service,而系統也只會建立一個對應 Service 的例項)。

因此你可以把 Service 想象成一種訊息服務,而你可以在任何有 Context 的地方呼叫 Context.startService、Context.stopService、Context.bindService,Context.unbindService,來控制它,你也可以在 Service 裡註冊 BroadcastReceiver,在其他地方通過傳送 broadcast 來控制它,當然這些都是 Thread 做不到的。

Service的生命週期

onCreate  onStart  onDestroy  onBind 

1). 被啟動的服務的生命週期:如果一個Service被某個Activity 呼叫 Context.startService 方法啟動,那麼不管是否有Activity使用bindService繫結或unbindService解除繫結到該Service,該Service都在後臺執行。如果一個Service被startService 方法多次啟動,那麼onCreate方法只會呼叫一次,onStart將會被呼叫多次(對應呼叫startService的次數),並且系統只會建立Service的一個例項(因此你應該知道只需要一次stopService呼叫)。該Service將會一直在後臺執行,而不管對應程式的Activity是否在執行,直到被呼叫stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。

2). 被繫結的服務的生命週期:如果一個Service被某個Activity 呼叫 Context.bindService 方法繫結啟動,不管呼叫 bindService 呼叫幾次,onCreate方法都只會呼叫一次,同時onStart方法始終不會被呼叫。當連線建立之後,Service將會一直執行,除非呼叫Context.unbindService 斷開連線或者之前呼叫bindService 的 Context 不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被呼叫。

3). 被啟動又被繫結的服務的生命週期:如果一個Service又被啟動又被繫結,則該Service將會一直在後臺執行。並且不管如何呼叫,onCreate始終只會呼叫一次,對應startService呼叫多少次,Service的onStart便會呼叫多少次。呼叫unbindService將不會停止Service,而必須呼叫 stopService 或 Service的 stopSelf 來停止服務。

4). 當服務被停止時清除服務:當一個Service被終止(1、呼叫stopService;2、呼叫stopSelf;3、不再有繫結的連線(沒有被啟動))時,onDestroy方法將會被呼叫,在這裡你應當做一些清除工作,如停止在Service中建立並執行的執行緒。

特別注意:

1、你應當知道在呼叫 bindService 繫結到Service的時候,你就應當保證在某處呼叫 unbindService 解除繫結(儘管 Activity 被 finish 的時候繫結會自      動解除,並且Service會自動停止);

2、你應當注意 使用 startService 啟動服務之後,一定要使用 stopService停止服務,不管你是否使用bindService;

3、同時使用 startService 與 bindService 要注意到,Service 的終止,需要unbindService與stopService同時呼叫,才能終止 Service,不管 startService 與 bindService 的呼叫順序,如果先呼叫 unbindService 此時服務不會自動終止,再呼叫 stopService 之後服務才會停止,如果先呼叫 stopService 此時服務也不會終止,而再呼叫 unbindService 或者 之前呼叫 bindService 的 Context 不存在了(如Activity 被 finish 的時候)之後服務才會自動停止;

4、當在旋轉手機螢幕的時候,當手機螢幕在“橫”“豎”變換時,此時如果你的 Activity 如果會自動旋轉的話,旋轉其實是 Activity 的重新建立,因此旋轉之前的使用 bindService 建立的連線便會斷開(Context 不存在了),對應服務的生命週期與上述相同。

5、在 sdk 2.0 及其以後的版本中,對應的 onStart 已經被否決變為了 onStartCommand,不過之前的 onStart 任然有效。這意味著,如果你開發的應用程式用的 sdk 為 2.0 及其以後的版本,那麼你應當使用 onStartCommand 而不是 onStart。

下面開始上一個很簡單的程式碼哈~裡頭的註釋也要注意哦,有在上面沒有講到的會在註釋裡提到哇(尤其適用Bind方法的時候的資料傳輸哇)~

首先,因為要再Manifest檔案裡對服務進行註冊,所以就先來Manifest的程式碼吧~

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="com.test.localservice" android:versionCode="1"
	android:versionName="1.0">
	<uses-sdk android:minSdkVersion="8" />

	<application android:icon="@drawable/icon" android:label="@string/app_name">
		<activity android:name=".LocalServiceTestActivity"
			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=".MyService">
			<intent-filter>
				<action android:name="com.test.SERVICE_TEST" />
				<category android:name="android.intent.category.default" />
			</intent-filter>
		</service>
	</application>
</manifest>

然後然後,是服務實現類~
package com.test.service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {

	public class LocalBinder extends Binder {
		String stringToSend = "I'm the test String";
		MyService getService() {
			Log.i("TAG", "getService ---> " + MyService.this);
			return MyService.this;
		}
	}

	private final IBinder mBinder = new LocalBinder();

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Log.i("TAG", "onBind~~~~~~~~~~~~");
//		IBinder myIBinder = null;
//		if ( null == myIBinder ) 
//			myIBinder = new LocalBinder() ; 
//		return myIBinder;
		return mBinder;		//也可以像上面幾個語句那樣重新new一個IBinder
		//如果這邊不返回一個IBinder的介面例項,那麼ServiceConnection中的onServiceConnected就不會被呼叫
		//那麼bind所具有的傳遞資料的功能也就體現不出來~\(≧▽≦)/~啦啦啦(這個返回值是被作為onServiceConnected中的第二個引數的)
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();

		Log.i("TAG", "onCreate~~~~~~~~~~");
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		Log.i("TAG", "onDestroy~~~~~~~~~~~");
	}

	@Override
	public void onStart(Intent intent, int startId) {
		// TODO Auto-generated method stub
		super.onStart(intent, startId);
		Log.i("TAG", "onStart~~~~~~");
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		Log.i("TAG", "onStartCommand~~~~~~~~~~~~");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public boolean onUnbind(Intent intent) {
		// TODO Auto-generated method stub
		Log.i("TAG", "onUnbind~~~~~~~~~~~~~~~~");
		return super.onUnbind(intent);
	}
}


再來,就是我們的Activity的測試類啦~
package com.test.service;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class ServiceTestActivity extends Activity {
	private Button startButton, bindButton;
	private Button stopButton, unbindButton;
	private ServiceConnection sc;
	private MediaPlayer mediaPlayer = null;
	private MyService myService;// 類似於MediaPlayer mPlayer = new
								// MediaPlayer();只不過這邊的服務是自定義的,不是系統提供好了的

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		startButton = (Button) findViewById(R.id.startbutton_id);
		stopButton = (Button) findViewById(R.id.stopbutton_id);
		bindButton = (Button) findViewById(R.id.bindbutton_id);
		unbindButton = (Button) findViewById(R.id.unbindbutton_id);

		sc = new ServiceConnection() {
			/*
			 * 只有在MyService中的onBind方法中返回一個IBinder例項才會在Bind的時候
			 * 呼叫onServiceConnection回撥方法
			 * 第二個引數service就是MyService中onBind方法return的那個IBinder例項,可以利用這個來傳遞資料
			 */
			@Override
			public void onServiceConnected(ComponentName name, IBinder service) {
				// TODO Auto-generated method stub
				myService = ((MyService.LocalBinder) service).getService();
				String recStr = ((MyService.LocalBinder) service).stringToSend;
				//利用IBinder物件傳遞過來的字串資料(其他資料也可以啦,哪怕是一個物件也OK~~)
				Log.i("TAG","The String is : " + recStr);
				Log.i("TAG", "onServiceConnected : myService ---> " + myService);
			}

			@Override
			public void onServiceDisconnected(ComponentName name) {
				/* SDK上是這麼說的:
				 * This is called when the connection with the service has been unexpectedly disconnected
				 * that is, its process crashed. Because it is running in our same process, we should never see this happen.
				 * 所以說,只有在service因異常而斷開連線的時候,這個方法才會用到*/
				// TODO Auto-generated method stub
				sc = null;
				Log.i("TAG", "onServiceDisconnected : ServiceConnection --->"
						+ sc);
			}

		};
		startButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Intent intent = new Intent(ServiceTestActivity.this,
						MyService.class);
				startService(intent);
				Log.i("TAG", "Start button clicked");
			}
		});

		stopButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub

				/*
				 * Intent intent = new
				 * Intent(LocalServiceTestActivity.this,MyService.class);
				 * stopService(intent); 這種方法也是可以的哈~
				 */

				Intent intent = new Intent();
				intent.setAction("com.test.SERVICE_TEST");
				stopService(intent);
				Log.i("TAG", "Stop Button clicked");
			}
		});

		bindButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
//				Intent intent = new Intent(LocalServiceTestActivity.this,
//						MyService.class);//這樣也可以的
				Intent intent = new Intent();
				intent.setAction("com.test.SERVICE_TEST");
				bindService(intent, sc, Context.BIND_AUTO_CREATE);//bind多次也只會呼叫一次onBind方法
				Log.i("TAG", "Bind button clicked");
			}
		});

		unbindButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				unbindService(sc);
				// 這邊如果重複unBind會報錯,提示該服務沒有註冊的錯誤——IllegalArgumentException:
				// Service not registered: null
				// 所以一般會設定一個flag去看這個service
				// bind後有沒有被unBind過,沒有unBind過才能呼叫unBind方法(這邊我就不設定了哈~\(≧▽≦)/~啦啦啦)
				Log.i("TAG", "Unbind Button clicked");
			}
		});
	}
}



相信開頭的介紹和程式碼裡的註釋應該對大家理解和使用Service有所幫助哈~不過這邊就先只講LocalService吧,剩下的RemoteService就等下次在說嘍~

相關推薦

AndroidService的使用注意LocalService

開始,先稍稍講一點android中Service的概念和用途吧~ Service分為本地服務(LocalService)和遠端服務(RemoteService): 1、本地服務依附在主程序上而不是獨立的程序,這樣在一定程度上節約了資源,另外Local服務因為是在同一程序因此

JavaScript的小技巧注意

tip:自己收集的一些前端注意事項 1.函式優先 函式宣告和變數宣告都會被提升,但是一個值得注意的細節是函式會首先被提升,然後才是變數 foo();//1 var foo; function foo(){ console.log(1);

android WebView,常見漏洞安全原始碼

  這篇部落格主要來介紹 WebView 的相關使用方法,常見的幾個漏洞,開發中可能遇到的坑和最後解決相應漏洞的原始碼,以及針對該原始碼的解析。   由於部落格內容長度,這次將分為上下兩篇,上篇詳解 WebView 的使用,下篇講述 WebView 的漏洞和

【CronExpression表達式案例】轉載

強烈 簡單的 exce 初始 每分鐘 第三周 normal orm 以及 原文地址:https://www.cnblogs.com/pipi-changing/p/5697481.html 找了下Cron的資料,這篇作者寫的比較清晰,轉載記錄一下,方便後面使用的時候在g

Tomcat記錄-tomcat常用配置優化方法轉載

常用配置詳解  1 目錄結構 /bin:指令碼檔案目錄。 /common/lib:存放所有web專案都可以訪問的公共jar包(使用Common類載入器載入)。 /conf:存放配置檔案,最重要的是server.xml。 /logs:存放日誌檔案。 

vuemixins的使用方法注意

mixins基礎概況 vue中的解釋是這樣的,如果覺得語言枯燥的可以自行跳過嘿~ 混入 (mixins): 是一種分發 Vue 元件中可複用功能的非常靈活的方式。混入物件可以包含任意元件選項。當元件使用混入物件時,所有混入物件的選項將被混入該元件本身的選項。 怎麼用? 舉個栗子: 定義一個混入

AndroidImageSwitcher注意與圖片瀏覽器的區別

先看看繼承關係,ImageSwitcher和TextSwitcher的繼承關係是一樣的。兩個重要的父類:ViewSwitcher和ViewAnimator 繼承於ViewSwitcher,說明具備了切換功能 繼承於ViewAnimator,說明具備了動畫功能 Image

專案遇到的坑注意 總結 持續更新

gitHub地址: 傳送門 工作中遇到的坑和思考 有不同意見歡迎指正交流 前排推薦 https://github.com/topics/javascript 關注JS開源框架動態 勤於總結和思考 1. ajax請求的結果要和後端約定好返回的資料格式。

AndroidAlarmManager以及利用PendingIntent設定鬧鐘

AlarmManager是提供一種訪問系統鬧鐘服務的方式,允許你去設定在將來的某個時間點去執行你的應用程式。當你的鬧鐘響起(時間到)時,在它上面註冊的一個意圖(Intent)將會被系統以廣播發出,然後自動啟動目標程式,如果它沒有正在執行。註冊的鬧鐘會被保留即使裝置處於休眠中

AndroidContext ---- 你所不知道的Context

             前言:本文是我讀《Android核心剖析》第7章 後形成的讀書筆記 ,在此向欲瞭解Android框架的書籍推薦此書。         大家好,  今天給大家介紹下我們在應用開發中最熟悉而陌生的朋友-----Context類 ,說它熟

AndroidWindowManager

最近看到 關於 WindowManager的介紹,感到新奇下,瞭解了一下,並整理,以供備用: 一、WindowManager是什麼        WindowManager是Android中一個重要的

tcp連線關閉注意事項

注:tcp關閉連線不區分客戶端和服務端,哪一埠可以主動發起關閉連線請求。所以為了描述方便,描述中的“主動方”表示主動發起關閉連線一方,“被動方”表示被動關閉連線一方。 1. tcp關閉連線狀態轉換   上圖是tcp連線主動關閉端的狀態轉換圖: (1)應用層呼叫close函式發起關閉連線請求 (2

JAVA C/C++ string 的區別注意

所有的字串類都起源於C語言的字串,而C語言字串則是字元的陣列。C語言中是沒有字串的,只有字元陣列。       談一下C++的字串:C++提供兩種字串的表示:C風格的字串和標準C++引入的string型別。一般建議用string型別,但是實際情況中還是要使用老式C風格的字串。       1.C風格的字串:C

AndroidIntent(二)之使用Intent廣播事件及Broadcast Receiver簡介

通過第一篇的講解,我們已經看到了如何使用Intent來啟動新的應用程式元件,但是實際上他們也可以使用sendBroadcast方法來在元件間匿名的廣播訊息。 作為一個系統級別的訊息傳遞機制,Intent可以在程序之間傳送結構化的訊息。因此,通過實現Broadcast Rec

Android SQLite使用多執行緒併發訪問

Android中資料持久化技術包括檔案儲存、SharedPreferences以及資料庫儲存,對於大量複雜的關係型資料,資料庫無疑是最合適的選擇。SQLite是一個輕量級的關係型資料庫,運算速度快,佔用資源少,適合在移動裝置上使用。SQLite不僅支援SQL語法,還遵循資料庫

React生命週期函式注意事項

React生命週期函式 生命週期函式是指在某一個週期自動執行的函式。 React中的生命週期執行過程 以下是React中的常用的生命週期函式,按個部分中按照自動執行順序列出,這幾個過程

Netty 4.1的新變化注意

本文帶你瞭解Netty 4.0到Netty 4.1的值得注意的改變和新特性. 題外話 儘管我們儘量保持向下相容,4.1 還是有一些和4.0不完全相容的地方. 請確保使用新的Netty版本重新編譯你的應用. 當你重新編譯你的應用時,你可以能看到一些棄用警告. 請依照修改建議

Android WebView技術經驗分享

WebView和JS相互通訊 WebView呼叫JS函式 通用方式,不能獲取JS函式的返回值: webView.loadUrl("javascript:alert('hello world');"); Android 4.4.及以上系統的WebView專用方式,可以獲取J

AndroidService

1. 簡介 與前一篇Android之Activity的細枝末節是同一系列的文章,是自己在學習和研發過程中,對Service的一些知識點的總結,彙總得到這篇文章。 這篇文章會從Service的一些小知識點,延伸到Android中幾種常用程序間通訊方法。

【計算機網絡】網絡層ARPRARP

博文 拆分 detail 動態 再次 tcp 將在 ont 關於 ARP ARP(Address Resolution Protocol,地址解析協議)是將IP地址解析為以太網MAC地址(物理地址)的協議。在局域網中,當主機或其他網絡設備有數據要發送給另一個主機或設備時,它