編輯:關於Android編程
Service是一個應用程序組件,它能夠在後台執行一些耗時較長的操作,並且不提供用戶界面。服務能被其它應用程序的組件啟動,即使用戶切換到另外的應用時還能保持後台運行。此外,應用程序組件還能與服務綁定,並與服務進行交互,甚至能進行進程間通信(IPC)。 比如,服務可以處理網絡傳輸、音樂播放、執行文件I/O、或者與content provider進行交互,所有這些都是後台進行的。
服務僅僅是一個組件,即使用戶不再與你的應用程序發生交互,它仍然能在後台運行。因此,應該只在需要時才創建一個服務。
如果你需要在主線程之外執行一些工作,但僅當用戶與你的應用程序交互時才會用到,那你應該創建一個新的線程而不是創建服務。 比如,如果你需要播放一些音樂,但只是當你的activity在運行時才需要播放,你可以在onCreate()中創建一個線程,在onStart()中開始運行,然後在onStop()中終止運行。還可以考慮使用AsyncTask或HandlerThread來取代傳統的Thread類。
由於無法在不同的 Activity 中對同一 Thread 進行控制,這個時候就要考慮用服務實現。如果你使用了服務,它默認就運行於應用程序的主線程中。因此,如果服務執行密集計算或者阻塞操作,你仍然應該在服務中創建一個新的線程來完成(避免ANR)。
前台服務
前台服務是指那些經常會被用戶關注的服務,因此內存過低時它不會成為被殺的對象。 前台服務必須提供一個狀態欄通知,並會置於“正在進行的”(“Ongoing”)組之下。這意味著只有在服務被終止或從前台移除之後,此通知才能被解除。
例如,用服務來播放音樂的播放器就應該運行在前台,因為用戶會清楚地知曉它的運行情況。 狀態欄通知可能會標明當前播放的歌曲,並允許用戶啟動一個activity來與播放器進行交互。
要把你的服務請求為前台運行,可以調用startForeground()方法。此方法有兩個參數:唯一標識通知的整數值、狀態欄通知Notification對象。例如:
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);
要從前台移除服務,請調用stopForeground()方法,這個方法接受個布爾參數,表示是否同時移除狀態欄通知。此方法不會終止服務。不過,如果服務在前台運行時被你終止了,那麼通知也會同時被移除。
後台服務本地服務
用於應用程序內部,實現一些耗時任務,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行。
調用Context.startService()啟動,調用Context.stopService()結束。在內部可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。
遠程服務
用於Android系統內部的應用程序之間,可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。可以定義接口並把接口暴露出來,以便其他應用進行操作。客戶端建立到服務對象的連接,並通過那個連接來調用服務。調用Context.bindService()方法建立連接,並啟動,以調用 Context.unbindService()關閉連接。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。
Service生命周期方法:
public class ExampleService extends Service {
int mStartMode; // 標識服務被殺死後的處理方式
IBinder mBinder; // 用於客戶端綁定的接口
boolean mAllowRebind; // 標識是否使用onRebind
@Override
public void onCreate() {
// 服務正被創建
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 服務正在啟動,由startService()調用引發
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// 客戶端用bindService()綁定服務
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// 所有的客戶端都用unbindService()解除了綁定
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// 某客戶端正用bindService()綁定到服務,
// 而onUnbind()已經被調用過了
}
@Override
public void onDestroy() {
// 服務用不上了,將被銷毀
}
}
請注意onStartCommand()方法必須返回一個整數。這個整數是描述系統在殺死服務之後應該如何繼續運行。onStartCommand()的返回值必須是以下常量之一:
START_NOT_STICKY
如果系統在onStartCommand()返回後殺死了服務,則不會重建服務了,除非還存在未發送的intent。 當服務不再是必需的,並且應用程序能夠簡單地重啟那些未完成的工作時,這是避免服務運行的最安全的選項。
START_STICKY
如果系統在onStartCommand()返回後殺死了服務,則將重建服務並調用onStartCommand(),但不會再次送入上一個intent, 而是用null intent來調用onStartCommand() 。除非還有啟動服務的intent未發送完,那麼這些剩下的intent會繼續發送。 這適用於媒體播放器(或類似服務),它們不執行命令,但需要一直運行並隨時待命。START_REDELIVER_INTENT
如果系統在onStartCommand()返回後殺死了服務,則將重建服務並用上一個已送過的intent調用onStartCommand()。任何未發送完的intent也都會依次送入。這適用於那些需要立即恢復工作的活躍服務,比如下載文件。
服務的生命周期與activity的非常類似。不過,更重要的是你需密切關注服務的創建和銷毀環節,因為後台運行的服務是不會引起用戶注意的。
服務的生命周期——從創建到銷毀——可以有兩種路徑:
一個started服務
這類服務由其它組件調用startService()來創建。然後保持運行,且必須通過調用stopSelf()自行終止。其它組件也可通過調用stopService() 終止這類服務。服務終止後,系統會把它銷毀。
如果一個Service被startService 方法多次啟動,那麼onCreate方法只會調用一次,onStart將會被調用多次(對應調用startService的次數),並且系統只會創建Service的一個實例(因此你應該知道只需要一次stopService調用)。該Service將會一直在後台運行,而不管對應程序的Activity是否在運行,直到被調用stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。
一個bound服務
服務由其它組件(客戶端)調用bindService()來創建。然後客戶端通過一個IBinder接口與服務進行通信。客戶端可以通過調用unbindService()來關閉聯接。多個客戶端可以綁定到同一個服務上,當所有的客戶端都解除綁定後,系統會銷毀服務。(服務不需要自行終止。)
如果一個Service被某個Activity 調用 Context.bindService 方法綁定啟動,不管調用 bindService 調用幾次,onCreate方法都只會調用一次,同時onStart方法始終不會被調用。當連接建立之後,Service將會一直運行,除非調用Context.unbindService 斷開連接或者之前調用bindService 的 Context 不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被調用。
這兩條路徑並不是完全隔離的。也就是說,你可以綁定到一個已經用startService()啟動的服務上。例如,一個後台音樂服務可以通過調用startService()來啟動,傳入一個指明所需播放音樂的 Intent。 之後,用戶也許需要用播放器進行一些控制,或者需要查看當前歌曲的信息,這時一個activity可以通過調用bindService()與此服務綁定。在類似這種情況下,stopService()或stopSelf()不會真的終止服務,除非所有的客戶端都解除了綁定。
當在旋轉手機屏幕的時候,當手機屏幕在“橫”“豎”變換時,此時如果你的 Activity 如果會自動旋轉的話,旋轉其實是 Activity 的重新創建,因此旋轉之前的使用 bindService 建立的連接便會斷開(Context 不存在了)。
無論是什麼類型的服務都必須在manifest中申明,
Service 元素的屬性有:
android:name ————- 服務類名
android:label ————– 服務的名字,如果此項不設置,那麼默認顯示的服務名則為類名
android:icon ————– 服務的圖標
android:permission ——- 申明此服務的權限,這意味著只有提供了該權限的應用才能控制或連接此服務
android:process ———- 表示該服務是否運行在另外一個進程,如果設置了此項,那麼將會在包名後面加上這段字符串表示另一進程的名字
android:enabled ———- 如果此項設置為 true,那麼 Service 將會默認被系統啟動,不設置默認此項為 false
android:exported ——— 表示該服務是否能夠被其他應用程序所控制或連接,不設置默認此項為 false
android:name是唯一必需的屬性——它定義了服務的類名。與activity一樣,服務可以定義intent過濾器,使得其它組件能用隱式intent來調用服務。如果你想讓服務只能內部使用(其它應用程序無法調用),那麼就不必(也不應該)提供任何intent過濾器。
此外,如果包含了android:exported屬性並且設置為”false”, 就可以確保該服務是你應用程序的私有服務。即使服務提供了intent過濾器,本屬性依然生效。
從activity或其它應用程序組件中可以啟動一個服務,調用startService()並傳入一個Intent(指定所需啟動的服務)即可。
Intent intent = new Intent(this, MyService.class);
startService(intent);
服務類:
public class MyService extends Service {
/**
* onBind 是 Service 的虛方法,因此我們不得不實現它。
* 返回 null,表示客服端不能建立到此服務的連接。
*/
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//接受傳遞過來的intent的數據
return START_STICKY;
};
@Override
public void onDestroy() {
super.onDestroy();
}
}
一個started服務必須自行管理生命周期。也就是說,系統不會終止或銷毀這類服務,除非必須恢復系統內存並且服務返回後一直維持運行。 因此,服務必須通過調用stopSelf()自行終止,或者其它組件可通過調用stopService()來終止它。
當應用程序中的activity或其它組件需要與服務進行交互,或者應用程序的某些功能需要暴露給其它應用程序時,你應該創建一個bound服務,並通過進程間通信(IPC)來完成。
方法如下:
Intent intent=new Intent(this,BindService.class);
bindService(intent, ServiceConnection conn, int flags)
注意bindService是Context中的方法,當沒有Context時傳入即可。
在進行服務綁定的時,其flags有:
Context.BIND_AUTO_CREATE
表示收到綁定請求的時候,如果服務尚未創建,則即刻創建,在系統內存不足需要先摧毀優先級組件來釋放內存,且只有駐留該服務的進程成為被摧毀對象時,服務才被摧毀
Context.BIND_DEBUG_UNBIND
通常用於調試場景中判斷綁定的服務是否正確,但容易引起內存洩漏,因此非調試目的的時候不建議使用
Context.BIND_NOT_FOREGROUND
表示系統將阻止駐留該服務的進程具有前台優先級,僅在後台運行。
服務類:
public class BindService extends Service {
// 實例化MyBinder得到mybinder對象;
private final MyBinder binder = new MyBinder();
/**
* 返回Binder對象。
*/
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return binder;
}
/**
* 新建內部類MyBinder,繼承自Binder(Binder實現IBinder接口),
* MyBinder提供方法返回BindService實例。
*/
public class MyBinder extends Binder{
public BindService getService(){
return BindService.this;
}
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
return super.onUnbind(intent);
}
}
啟動服務的activity代碼:
public class MainActivity extends Activity {
/** 是否綁定 */
boolean mIsBound = false;
BindService mBoundService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doBindService();
}
/**
* 實例化ServiceConnection接口的實現類,用於監聽服務的狀態
*/
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
BindService mBoundService = ((BindService.MyBinder) service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBoundService = null;
}
};
/** 綁定服務 */
public void doBindService() {
bindService(new Intent(MainActivity.this, BindService.class), conn,Context.BIND_AUTO_CREATE);
mIsBound = true;
}
/** 解除綁定服務 */
public void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(conn);
mIsBound = false;
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
doUnbindService();
}
}
注意在AndroidMainfest.xml中對Service進行顯式聲明
判斷Service是否正在運行:
private boolean isServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
{
if ("com.example.demo.BindService".equals(service.service.getClassName())) {
return true;
}
}
return false;
}
在之前的篇章中,我們完成了Android平台開發環境的配置,也找到了剔除OpenCV Manager API的辦法,那麼接下來我們開始從零開始,完成一個個人的程序,實現功
ImageView的scaleType的屬性有好幾種,分別是matrix(默認)、center、centerCrop、centerInside、fitCenter、fi
在Android Support Library 23.2版本推出之後,我們可以看到一些新的特性,例如AppCompat DayNight主題,BottomSheet等,
ListView是每個app中都要使用的,所以今天我來總結下ListView的使用和一些簡單的優化。先看下運行效果:一、創建數據庫為了模擬數據,這裡將數據保存數據庫中,順