編輯:關於Android編程
上一篇說到了通知欄Notification,提起通知欄,不得讓人想到Service以及BroadcastReceive,作為android的4大組建的2個重要成員,我們沒少和它們打交道。它們可以在無形中使我們的軟件和網絡、數據庫、系統等進行交互,之後通過UI(Notification就是一種展示方式)把結果展現在我們面前。可以說,他們是android生命體系裡面的神經系統,通過反射條件讓身體展現不同的狀態。在整個系統中,廣播接收器充當著是傳輸者和監聽者的角色,它把系統的一點點變化都反饋上去,之後做出改變。
通過這幾篇博文講講BroadcastReceiver(廣播接收器)和Service(服務),以及他們之間的聯系。這一篇通過開發文檔、源碼和書籍中筆記總結分析下service這個類。
service可以說是一個在後台運行的Activity,它不是一個單獨的進程,它只需要應用告訴它要在後台做什麼就可以了。
它要實現和用戶的交互的話需要通過通知欄或則是發送廣播,UI去接收顯示。它的應用十分廣泛,尤其是在框架層,應用更多的是對系統服務的調用。
它用於處理一些不干擾用戶使用的後台操作。如下載,網絡獲取。播放音樂,他可以通過INTENT來開啟,同時也可以綁定到宿主對象(調用者例如ACTIVITY上)來使用。
服務是一個應用程序組件代表應用程序執行一個長時間操作的行為,雖然不與用戶交互或供應功能供其它應用程序使用。每個服務類必須有一個相應的包的AndroidManifest.xml中
它繼承至ContextWrapper,再上去就是Context,它的直接子類用很多,間接子類是InputMethodService,下面就隨便說幾個
1.InputMethodService
這個類提供了一個輸入法的標准實現,一般的開發者是不會去考慮這個,輸入法公司和ODM廠商則需要去考慮。
2.IntentService
它作為Service的子類,主要用於處理異步請求,防止線程的阻塞,所有的請求將在一個工作線程(HandlerThread)中處理,工作完成了,線程也就結束了。
3.MediaRouteProviderService
它主要用於設備啟動和SD卡掛載時候執行多媒體文件的掃描工作。
4. NotificationListenerService
上一篇博文主要就是將通知欄( Android 通知欄Notification的整合 全面學習 ),這個類就是和通知欄有關,它主要用於接收來自系統調用的服務及新通知發布或刪除。
5. RecognitionService
它是一個抽象服務類,如果開發者希望實現一個新的語音識別器時候,可以用到它。
功能:用於實現應用程序自己的一些耗時任務,比如查詢升級信息,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行,這樣用戶體驗比較好。
使用:在Service可以調用Context.startService()啟動,調用Context.stopService()結束。在內部可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。無論調用了多少次startService(),都只需調用一次stopService()來停止。
功能:可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。
使用:可以定義接口並把接口暴露出來,以便其他應用進行操作。客戶端建立到服務對象的連接,並通過那個連接來調用服務。調用Context.bindService()方法建立連接,並啟動,以調用 Context.unbindService()關閉連接。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。
前台服務需要調用 startForeground ( android 2.0 及其以後版本 )或 setForeground (android 2.0 以前的版本)使服務成為 前台服務。
使用前台服務可以避免服務在後台運行的時候被系統KILL。
android官方描述如下:
A foreground service is a service that's considered to be something the user is actively aware of and thus not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading, which means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
For example, a music player that plays music from a service should be set to run in the foreground, because the user is explicitly aware of its operation. The notification in the status bar might indicate the current song and allow the user to launch an activity to interact with the music player.
To request that your service run in the foreground, call startForeground()
. This method takes two parameters: an integer that uniquely identifies the notification and the Notification
for the status bar. For example:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis()); Intent notificationIntent = new Intent(this, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent); startForeground(ONGOING_NOTIFICATION, notification);
To remove the service from the foreground, call stopForeground()
. This method takes a boolean, indicating whether to remove the status bar notification as well. This method does not stop the service. However, if you stop the service while it's still running in the foreground, then the notification is also removed.
Note: The methods startForeground()
and stopForeground()
were introduced in Android 2.0 (API Level 5). In order to run your service in the foreground on older versions of the platform, you must use the previoussetForeground()
method—see the startForeground()
documentation for information about how to provide backward compatibility.
For more information about notifications, see Creating Status Bar Notifications.
使用:可以看出,我們只需要在onStartCommand裡面調用 startForeground方法讓服務前台運行,然後再onDestroy裡面調用stopForeground解除前台運行既可!
所以,例如手機中的音樂播放器,不管手機是否是在休眠狀態,只要開始播放了,系統就不會去KILL這個服務,只有當停止播放音樂時,服務才可能會回收清除。
後台服務就是處於後台運行的
圖:左圖:啟動方式的生命周期 右圖:綁定方式的生命周期
注意:本地服務中,onStart已經被onStartCommand方法取代,Service和Activity都是由Context類派生的,可以通過getApplicationContext()方法獲取上下文對象,和Activity一樣,它有著自己的生命周期,可是和Activity相比,它所執行的過程略有不同,如上圖所示。
在服務分類中,提到了3種服務通信類型,一種是通過startService()直接啟動服務,一種是通過bindService()的方式啟動,2種啟動方式對應的生命周期如上圖所示。3.使用AIDL方式的Service
下面就說說2種服務的啟動流程:
1.context.startService() 啟動流程(後台處理工作):
context.startService() -> onCreate() -> onStartCommand() -> Service running -> context.stopService() -> onDestroy() -> Service stop
所以調用startService的生命周期大致為:
onCreate(只在創建的時候調用一次直到被摧毀) --> onStartCommand (服務開啟後,可多次調用) --> onDestroy
服務中的onStartCommand(Intent intent, int flags, int startId)方法會返回一個唯一的整數標識符來識別啟動請求,啟動請求可以是START_STICKY、START_STICKY_COMPATIBILITY、START_NOT_STICKY、START_REDELIVER_INTENT等,標志位可以是START_FLAG_REDELIVERY、START_FLAG_RETRY。
通過這種方式,服務並不會隨著綁定組建的摧毀而摧毀,而是服務自我摧毀。(所以這種方式適用於文件下載,上傳等請求自行運行的場景)。
從圖中我們可以看出,onCreate方法只在創建時候被調用了一次,這說明:Service被啟動時只調用一次onCreate()方法,如果服務已經被啟動,在次啟動的Service組件將直接調用onStartCommand()方法,通過這樣的生命周期,可以根據自身需求將指定操作分配進onCreate()方法或onStartCommand()方法中。
2.context.bindService()啟動流程(在本地同一進程內與Activity交互):
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop
bindService的生命周期簡化為為:onCreate --> onBind --> onUnbind --> onDestory。
通過該方法,服務啟動時會調用onCreate()來啟動服務,可是它不會調用onStartCommand() 方法,並且只有在所有的服務都接觸了後,服務才會自動停止運行。通過服務的onBind()方法,可以獲的一個客戶端與服務器進行通信的IBdiner接口。IBind允許客戶端回調服務的方法,比如得到Service的實例、運行狀態或其他操作。這個時候把調用者(Context,例如Activity)會和Service綁定在一起,Context退出了,Srevice就會調用onUnbind->onDestroy相應退出。
注:綁定服務的Android組建在摧毀前應解除綁定,否則會造成內存洩漏。
3.使用AIDL方式的Service(進行跨進程通信)(這塊不是很懂,這裡就不提了)
public class MyService extends Service {}
Intent intent = new Intent(getApplicationContext(), MyService.class); startService(intent);
服務類中:
@Override public void onCreate() { } @Override public int onStartCommand(Intent intent, int flags, int startId) { //接受傳遞過來的intent的數據等 return START_STICKY; } @Override public void onDestroy() { }
綁定一個服務,需要設置ServiceConnection和標志位,方法如下:
bindService(Intent service, ServiceConnection conn, int flags)
ServiceConnection可以監聽服務的狀態,在進行服務綁定的時,其標志位可以為以下幾種(這裡列出3種):
1).Context.BIND_AUTO_CREATE
說明:表示收到綁定請求的時候,如果服務尚未創建,則即刻創建,在系統內存不足需要先摧毀優先級組件來釋放內存,且只有駐留該服務的進程成為被摧毀對象時,服務才被摧毀
2).Context.BIND_DEBUG_UNBIND
說明:通常用於調試場景中判斷綁定的服務是否正確,但容易引起內存洩漏,因此非調試目的的時候不建議使用
3).Context.BIND_NOT_FOREGROUND
說明:表示系統將阻止駐留該服務的進程具有前台優先級,僅在後台運行,該標志位位於Froyo中引入。
注意:綁定服務的以異步方式運行的。綁定服務必須在當前的上下文環境中運行,某些場景中,通過上下文進行添加綁定接觸方法如下:
getApplicationContext().bindService(service, conn, flags)
代碼如下:
/** 是否綁定 */ boolean mIsBound = false; /** 綁定服務 */ public void doBindService() { bindService(new Intent(MainActivity.this, LocalService.class), mConnection,Context.BIND_AUTO_CREATE); mIsBound = true; } /** 解除綁定服務 */ public void doUnbindService() { if (mIsBound) { // Detach our existing connection. unbindService(mConnection); mIsBound = false; } } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mBoundService = ((LocalService.LocalBinder) service).getService(); Toast.makeText(MainActivity.this, 服務連接, Toast.LENGTH_SHORT) .show(); } @Override public void onServiceDisconnected(ComponentName name) { mBoundService = null; Toast.makeText(MainActivity.this, 服務未連接, Toast.LENGTH_SHORT) .show(); } };
服務類中:
@Override public void onCreate() { } /** 綁定的IBinder */ private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { public LocalService getService() { return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public boolean onUnbind(Intent intent) { // TODO Auto-generated method stub return super.onUnbind(intent); }
(1)啟動服務停止有2種方法:
1)stopSelf() 自我停止服務
2)stopService(Intent name) 被動停止服務
(2)綁定服務的解除綁定方法如下:
unbindService(ServiceConnection conn)
1.在注冊服務的時候,為了將service納入編譯系統,必須在AndroidMainfest.xml中對Service進行顯式聲明。
2.計算量較大的又不是UI層的工作的話,可以選擇放置在Service中進行工作。
3.通過開發文檔你會發現,Android中的Service與宿主(調用者)在同一線程,而不是專門起一條線程,這意味著,如果你的服務要CPU密集型操作(如:MP3播放)或則阻塞操作(如網絡)時,必須產生它自己的線程來完成這個工作,否則會造成線程阻塞。在Service的子類裡面,IntentService類服務可以作為一個標准的實施,它的工作有其自己的線程。
4.如果在使用Service的時候又使用了廣播接收器配合工作,廣播如果是動態注冊的話,在服務停止的時候記得調用unregisterReceiver(receiver);這個方法來注銷掉接收器
Android系統自己提供了一個函數ActivityManager.getRunningServices,可以列出當前正在運行的後台服務線程
private boolean isServiceRunning() { ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (com.example.MyService.equals(service.service.getClassName())) { return true; } } return false; }
(1)使用直接啟動的startService實現信息傳遞
流程:UI ——>Service
操作:使用Intent進行數據傳遞,通過服務中的onStartCommand方法進行接受(和Activity間傳遞方式一樣)
(2)使用綁定啟動的bindservice實現信息傳遞(
流程:UI ——>Service
(3)使用Broadcast(廣播)進行信息的雙向傳遞
流程:UI <——>Service
操作:注冊綁定廣播接受器,之後通過廣播來進行2者間通信
注意:在服務退出的時候記得unregisterReceiver(receiver);注銷廣播接收器
我們拿服務來進行一個後台長時間的動作,為了不阻塞線程,然而,Thread就可以達到這個效果,為什麼我們不直接使用Thread去代替服務呢?(這個問題摘抄至網上,原文地址不是是哪個,所以沒寫上)
這裡提下,
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服務的總結就到這裡,如果有什麼錯誤或者需要補充之處可以提出來。
XMPP協議(Extensible Messaging and PresenceProtocol,可擴展消息處理現場協議)是一種基於XML的協議,目的是為了解決及時通信
package com.example.jreduch08.DataBaseHelpp;import android.content.Contex
前言:最近公司C輪融資成功了,移動團隊准備擴大一下,需要招聘Android開發工程師,陸陸續續面試了幾位Android應聘者,面試過程中聊到性能優化中如何避免內存洩漏問題
AndroidProgressLayout實現為界面添加圓形進度條。調用setprogress()方法顯示和隱藏進度條在Android的開發中,往往有這種需求,比如一個耗