編輯:關於Android編程
一.Service簡介
service可以說是一個在後台運行的Activity,它不是一個單獨的進程,它只需要應用告訴它要在後台做什麼就可以了,它要實現和用戶的交互的話需要通過通知欄或則是發送廣播,UI去接收顯示。它的應用十分廣泛,尤其是在框架層,應用更多的是對系統服務的調用。它用於處理一些不干擾用戶使用的後台操作。如下載,網絡獲取。播放音樂,他可以通過INTENT來開啟,同時也可以綁定到宿主對象(調用者例如ACTIVITY上)來使用。服務是一個應用程序組件代表應用程序執行一個長時間操作的行為,雖然不與用戶交互或供應功能供其它應用程序使用。每個服務類必須有一個相應的包的AndroidManifest.xml中
二.服務的類型
按使用范圍分為本地服務個遠程服務兩種。
本地服務:用於應用程序內部,實現應用程序自己的一些耗時任務,比如查詢升級信息,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行,這樣用戶體驗比較好。
在Service可以調用Context.startService()啟動,調用Context.stopService()結束。在內部可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。無論調用了多少次startService(),都只需調用一次stopService()來停止。
遠程服務:用於android系統內部的應用程序之間,可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。可以定義接口並把接口暴露出來,以便其他應用進行操作。客戶端建立到服務對象的連接,並通過那個連接來調用服務。調用Context.bindService()方法建立連接,並啟動,以調用 Context.unbindService()關閉連接。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。
按運行類別分為前台服務和後台服務兩種。
前台服務:前台服務需要調用 startForeground ( android 2.0 及其以後版本 )或 setForeground (android 2.0 以前的版本)使服務成為 前台服務。
使用前台服務可以避免服務在後台運行的時候被系統KILL。
後台服務:後台服務就是處於後台運行的。
三.Service生命周期

注意:本地服務中,onStart已經被onStartCommand方法取代,Service和Activity都是由Context類派生的,可以通過getApplicationContext()方法獲取上下文對象,和Activity一樣,它有著自己的生命周期,可是和Activity相比,它所執行的過程略有不同,如上圖所示。
四.3種服務通信類型
3種服務通信類型,分別是通過startService()直接啟動服務,通過bindService()的方式啟動和使用AIDL方式的Service。
1. context.startService() 啟動流程(後台處理工作),只能實現啟動和停止服務
流程:UI ——>Service
操作:使用Intent進行數據傳遞,通過服務中的onStartCommand方法進行接受(和Activity間傳遞方式一樣)
啟動流程:
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()方法中。
使用方法:
(1)創建服務類
public class MyService extends Service {
@Override
public void onCreate() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//接受傳遞過來的intent的數據等
return START_STICKY;
}
@Override
public void onDestroy() {
}
}
(2)在AndroidManifest.xml文件中注冊Service
(3)啟動服務
Intent intent = new Intent(getApplicationContext(), MyService.class); startService(intent);
2. context.bindService()啟動流程(在本地同一進程內與Activity交互),單向交互
流程:UI ——>Service
啟動流程:
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組建在摧毀前應解除綁定,否則會造成內存洩漏。
使用方法:
(1)創建服務類
private LocalService extends Service{
@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);
}
}
(2)在AndroidManifest.xml文件中注冊Service
同startService的一樣。
(3)綁定服務
綁定服務需要需要設置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)
Activity中代碼如下:
/** 是否綁定 */
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();
}
};
3. 使用AIDL方式的Service(進行跨進程通信),雙向交互
流程:UI <——>Service
操作:注冊綁定廣播接受器,之後通過廣播來進行2者間通信
啟動流程:和bindService一樣,實現上是在bindService 的基礎上使用AIDL進行通信。
使用方法:
遠程綁定調用service主要是用來不同進程的信息共享。就比如服務器和客戶端,在服務器端設置好一個service提供方法或信息,然後客戶端可以直接調用服務器端service提供方法或信息。這裡有個前提是客戶端必須有和服務器端一份一樣的AIDL,然後服務器端在客戶端使用的系統上有注冊過(也就是安裝運行過一次),之後客戶端就可以遠程綁定調用服務器端的service了。
具體的步驟如下:
1.首先,是服務器的
1)創建一個android應用工程 ,命名為AidlServer
2) 在主Aitivity所在的包裡新建個AIDL文件,這是ADT會自動幫你在gen目錄下生成一個和AIDL文件同名的JAVA文件(這裡的AidlService.java是下一步驟生成的,這裡可以先忽略)

3)如上圖所示,創建一個用來使用service的類(AidlService.java)
具體每個文件的代碼如下:
IAidlService.idal文件:
package com.example.aidlserver;
interface IAidlService {
String getValue();
}
AidlService.java文件:
public class AidlService extends Service {
private class MyBinder extends IAidlService.Stub {
@Override
public String getValue() throws RemoteException {
// TODO Auto-generated method stub
return test remote service;
}
}
@Override
public void onCreate() {
super.onCreate();
Log.i(AidlService,onCreate);
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.i(AidlService,service on bind);
return new MyBinder();
}
@Override
public void onDestroy() {
Log.i(AidlService,service on destroy);
super.onDestroy();
}
}
MainActivity.java文件:
public class MainActivity extends Activity {
private TextView show;
private IAidlService iservice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (TextView)findViewById(R.id.show);
}
/*點擊第二次的時候次可以改變TextView的值,因為第一次只是綁定,返回的對象是空。*/
public void onGetAidlServerDataBtnClick(View v){
Intent service = new Intent(IAidlService.class.getName());
bindService(service, connection, BIND_AUTO_CREATE);
if (iservice != null) {
try {
show.setText(iservice.getValue());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
/*獲取Service對象*/
iservice = (IAidlService) IAidlService.Stub.asInterface(service);
Log.i(Client,Bind Success: + service);
}
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
iservice = null;
Log.i(Client,onServiceDisconnected);
}
};
}
AndroidManifest.xml
這裡,服務器端的工作做好了,接下來是客戶端的工作。
2.接著,是客戶端的:
1)首先,創建一個工程,命名為AidlClient
2)接著,把服務器端的一些文件拷貝過來(創建com.ds.server這個包,然後往裡面復制進入如下圖的服務器端的文件(IAidlService.java)

下面是個文件的內容
AidlClientActivity.java
public class MainActivity extends Activity {
private TextView show;
private IAidlService iservice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (TextView)findViewById(R.id.show);
}
/*點擊第二次的時候次可以改變TextView的值,因為第一次只是綁定,返回的對象是空。*/
public void onGetAidlServerDataBtnClick(View v){
Intent service = new Intent(IAidlService.class.getName());
bindService(service, connection, BIND_AUTO_CREATE);
if (iservice != null) {
try {
show.setText(iservice.getValue());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
/*獲取Service對象*/
iservice = (IAidlService) IAidlService.Stub.asInterface(service);
Log.i(Client,Bind Success: + service);
}
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
iservice = null;
Log.i(Client,onServiceDisconnected);
}
};
}
注意:使用廣播進行雙向交互的時候,在服務退出的時候記得unregisterReceiver(receiver);注銷廣播接收器。
五.停止服務
1. 啟動服務停止有2種方法:
(1)stopSelf() 自我停止服務
(2)stopService(Intent name) 被動停止服務
2. 綁定服務的解除綁定方法如下
unbindService(ServiceConnection conn)
六.拓展
1. 如何檢查Android後台服務線程(Service類)是否正在運行
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;
}
2. Service和Thread的區別
我們拿服務來進行一個後台長時間的動作,為了不阻塞線程,然而,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 做不到的。
Android中適用於ListView、GridView等組件的通用Adapter
今天隨便逛逛CSDN,看到主頁上推薦了一篇文章Android 快速開發系列 打造萬能的ListView GridView 適配器,剛好這兩天寫項目自己也封裝了類似的Com
Android 自定義ProgressDialog進度條對話框用法詳解
ProgressDialog的基本用法ProgressDialog為進度對話框。android手機自帶的對話框顯得比較單一,我們可以通過ProgressDialog來自己
android索引
項目需要,今天學習了一下索引涉及到的技術:繪制右側的索引條點擊某個字母,定位到ListView控件的指定位置 布局文件: 自
安卓播放音頻和視頻
先貼出本文程序運行結果的截圖,上面是播放/停止音頻,可用SeekBar來調進度,下面是播放/停止視頻,也是用SeekBar來調進度: main.xml的源碼: