編輯:關於Android編程
服務作為Android的四大組件之一,它並不像Activity那樣高調(使用頻繁),它就是那個默默無聞的工作者。
由於不怎麼用到,所以關於它的使用很容易忘記,現在有空就將它記錄下來,方便後期查看。
服務(Service)是Android中實現程序後台運行的解決方案,它非常適合用於去執行那些不需要和用戶交互而且還要求長期運行的任務。服務的運行不依賴於任何用戶界面,即使當程序被切換到後台,或者用戶打開了另外一個應用程序,服務仍然能夠保持正常運行。
不過需要注意的是,服務並不是運行在一個獨立的進程當中的,而是依賴於創建服務時所在的應用程序進程。當某個應用程序進程被殺掉時,所有依賴於該進程的服務也會停止運行。
另外,也不要被服務的後台概念所迷惑,實際上服務並不會自動開啟線程,所有的代碼都是默認運行在主線程當中的。也就是說,我們需要在服務的內部手動創建子線程,並在這裡執行具體的任務,否則就有可能出現主線程被阻塞住的情況。
1.創建一個服務
新建一個服務測試類,讓其繼承自Service
public class MyService extends Service { @Override public IBinder onBind(Intent intent) { return null; } }onBind()方法是Service中唯一的一個抽象方法,所以必須要在子類裡實現。後面會講到它的用處。
創建服務,想必我們也是會讓其做一些事情的,那麼處理事情的邏輯代碼段放置在哪裡呢?這時候就得需要重寫Service中的另外一些方法了。代碼段如下所示:
public class MyService extends Service { @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); } }
上面代碼中我們重寫了onCreate(), onStartCommand(), onDestroy()方法,它們是每個服務中最常用的三個方法。其中onCreate()方法會在服務創建的時候調用,onStartCommand()方法會在每次服務啟動的時候調用,onDestroy()方法會在服務銷毀的時候調用。那麼前2個方法有什麼區別了?
onCreate()方法是在服務第一次創建的時候調用的,而onStartCommand()方法則在每次啟動服務的時候都會調用。
通常情況下,如果我們希望服務一旦啟動就立刻去執行某個動作,就可以將邏輯寫在onStartCommand()方法裡。服務中的代碼都是默認運行在主線程當中的,如果直接在服務裡去處理一些耗時的邏輯,就很容易出現ANR(Application Not Responding)的情況。所以在onStartCommand()方法裡寫邏輯代碼時,最好開啟一個子線程,讓其在子線程中執行。
@Override public int onStartCommand(Intent intent, int flags, int startId) { new Thread(new Runnable() { @Override public void run() { // 處理具體的邏輯 } }).start(); return super.onStartCommand(intent, flags, startId); }
而當服務銷毀時,我們又應該在onDestroy()方法中去回收那些不再使用的資源。
注意:每一個服務都需要在AndroidManifest.xml文件中進行注冊才能生效,這是Android四大組件共有的特點,所以平常我們也要養成創建組件即注冊的習慣。
2.啟動和停止服務
服務的啟動和停止的方法和Activity類似,主要是借助Intent來實現的。
在Activity中啟動服務:
Intent startIntent = new Intent(this, MyService.class); startService(startIntent); // 啟動服務
在Activity中停止服務:
Intent stopIntent = new Intent(this, MyService.class); stopService(stopIntent); // 停止服務
注意:上面的停止服務,完全是由Activity來決定服務何時停止的,如果沒有上面的stopService()方法,服務就會一直處於運行狀態。
那服務有沒有什麼辦法讓自已停止下來呢?當然可以,只需要在MyService的任何一個位置調用stopSelf()方法就能讓這個服務停止下來了。
3.活動與服務的通信
雖然服務是在活動中啟動的,但是當在活動中startService()後,好像就變得不那麼容易控制我們的服務了,我們在活動裡調用了startService()方法來啟動MyService這個服務,然後MyService的onCreate()和onStartCommand()方法就會得到執行。之後服務會一直處於運行狀態,但具體運行的是什麼邏輯,活動就控制不了了,活動並不知道服務到底去做了什麼事情,以及完成的如何。
這個時候前面說的那個抽象方法onBind()就可以幫助我們讓Activity和Service的聯系更緊密一點。
下面以在MyService裡實現下載功能為例,做到在活動中可以決定何時開始下載,以及隨時查看下載進度。
為此,我們要創建一個專門的Binder對象來對下載功能進行管理,修改MyService中的代碼:
public class MyService extends Service { private DownloadBinder mBinder = new DownloadBinder(); class DownloadBinder extends Binder { public void startDownload() { Log.d("MyService", "startDownload executed"); } public int getProgress() { Log.d("MyService", "getProgress executed"); return 0; } } @Override public IBinder onBind(Intent intent) { return mBinder; } …… }
可以看到,這裡我們新建了一個DownloadBinder類,並讓它繼承自Binder,然後在它的內部提供了開始下載以及查看下載進度的方法。具體的邏輯代碼就不寫了。
接著,在MyService中創建了DownloadBinder的實例,然後在onBind()方法裡返回了這個實例,這樣MyService中的工作就全部完成了。
下面是如何在Activity中去調用服務裡的這些方法呢?
比如在MainActivity的布局文件中增加2個按鈕。一個用於綁定服務,一個用於取消服務。
activity_main.xml
……
當一個活動和服務綁定了之後,就可以調用該服務裡的Binder提供的方法了。
public class MainActivity extends Activity implements OnClickListener { private Button startService; private Button stopService; private Button bindService; private Button unbindService; private MyService.DownloadBinder downloadBinder; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { downloadBinder = (MyService.DownloadBinder) service; downloadBinder.startDownload(); downloadBinder.getProgress(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); …… bindService = (Button) findViewById(R.id.bind_service); unbindService = (Button) findViewById(R.id.unbind_service); bindService.setOnClickListener(this); unbindService.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { …… case R.id.bind_service: Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE); // 綁定服務 break; case R.id.unbind_service: unbindService(connection); // 解綁服務 break; default: break; } } }
這裡我們首先創建了一個ServiceConnection的匿名類,在裡面重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法分別會在活動與服務成功綁定以及解除綁定的時候調用。在onServiceConnected()方法中,我們又通過向下轉型得到了DownloadBinder的實例,有了這個實例,活動和服務之間的關系就變得非常緊密了。現在我們可以在活動中根據具體的場景來調用DownloadBinder中的任何public方法,即實現了指揮服務干什麼,服務就去干什麼的功能。這裡仍然只是做了個簡單的測試,在onServiceConnected()方法中調用了DownloadBinder的startDownload()和getProgress()方法。
當然,現在活動和服務其實還沒進行綁定,這個功能是在Bind Service按鈕的點擊事件裡完成的。可以看到,這裡我們仍然是構建出了一個Intent對象,然後調用bindService()方法將MainActivity和MyService進行綁定。bindService()方法接收三個參數,第一個參數就是剛剛構建出的Intent對象,第二個參數是前面創建出的ServiceConnection的實例,第三個參數則是一個標志位,這裡傳入BIND_AUTO_CREATE表示在活動和服務進行綁定後自動創建服務。這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行。
然後如果我們想解除活動和服務之間的綁定該怎麼辦呢?調用一下unbindService()方法就可以了,這也是Unbind Service按鈕的點擊事件裡實現的功能。
注意:任何一個服務在整個應用程序范圍內都是通用的,即MyService不僅可以和MainActivity綁定,還可以和任何一個其他的活動進行綁定,而且在綁定完成後它們都可以獲取到相同的DownloadBinder實例。
4.服務的生命周期
服務也有自己的生命周期,前面我們使用到的onCreate()、onStartCommand()、onBind()和onDestroy()等方法都是在服務的生命周期內可能回調的方法。
一旦在項目的任何位置調用了Context的startService()方法,相應的服務就會啟動起來,並回調onStartCommand()方法。如果這個服務之前還沒有創建過,onCreate()方法會先於onStartCommand()方法執行。服務啟動了之後會一直保持運行狀態,直到stopService()或stopSelf()方法被調用。注意雖然每調用一次startService()方法,onStartCommand()就會執行一次,但實際上每個服務都只會存在一個實例(單例模式)。所以不管你調用了多少次startService()方法,只需調用一次stopService()或stopSelf()方法,服務就會停止下來了。
另外,還可以調用Context的bindService()來獲取一個服務的持久連接,這時就會回調服務中的onBind()方法。類似地,如果這個服務之前還沒有創建過,onCreate()方法會先於onBind()方法執行。之後,調用方可以獲取到onBind()方法裡返回的IBinder對象的實例,這樣就能自由地和服務進行通信了。只要調用方和服務之間的連接沒有斷開,服務就會一直保持運行狀態。
當調用了startService()方法後,又去調用stopService()方法,這時服務中的onDestroy()方法就會執行,表示服務已經銷毀了。類似地,當調用了bindService()方法後,又去調用unbindService()方法,onDestroy()方法也會執行,這兩種情況都很好理解。但是需要注意,我們是完全有可能對一個服務既調用了startService()方法,又調用了bindService()方法的,這種情況下該如何才能讓服務銷毀掉呢?根據Android系統的機制,一個服務只要被啟動或者被綁定了之後,就會一直處於運行狀態,必須要讓以上兩種條件同時不滿足,服務才能被銷毀。所以,這種情況下要同時調用stopService()和unbindService()方法,onDestroy()方法才會執行。
5.盡量使用IntentService
為什麼要盡量使用IntentService呢?主要是由於以下2個原因:
1.服務是運行在主線程中的,當我們做一些比較耗時的操作時,容易造成程序ANR,前面說的,可以手動創建子線程,讓其處理耗時的邏輯;
2.當服務執行完後,我們要手動停止服務調用stopService()或者stopSelf()方法
使用IntentService的話,可以不用這麼麻煩,IntentService默認是開啟在子線程中的且會完成處理邏輯後自動停止服務
public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); // 調用父類的有參構造函數 } @Override protected void onHandleIntent(Intent intent) { // 打印當前線程的id Log.d("MyIntentService", "Thread id is " + Thread.currentThread(). getId()); } @Override public void onDestroy() { super.onDestroy(); Log.d("MyIntentService", "onDestroy executed"); } }
首先是要提供一個無參的構造函數,並且必須在其內部調用父類的有參構造函數。然後要在子類中去實現onHandleIntent()這個抽象方法,在這個方法中可以去處理一些具體的邏輯,而且不用擔心ANR的問題,因為這個方法已經是在子線程中運行的了。這裡為了證實一下,我們在onHandleIntent()方法中打印了當前線程的id。另外根據IntentService的特性,這個服務在運行結束後應該是會自動停止的,所以我們又重寫了onDestroy()方法,在這裡也打印了一行日志,以證實服務是不是停止掉了。
在MainActivity中增加一個按鈕,並添加點擊事件,添加如下代碼:
// 打印主線程的id Log.d("MainActivity", "Thread id is " + Thread.currentThread(). getId()); Intent intentService = new Intent(this, MyIntentService.class); startService(intentService);
查看log輸出,你會發現,不僅MyIntentService和MainActivity所在的線程id不一樣,而且onDestroy()方法也得到了執行,說明MyIntentService在運行完畢後確實自動停止了。IntentService和普通的service一樣,但是集開啟線程和自動停止於一身,比較實用。
6.服務的實戰
該Demo運行後,會顯示所有你sd卡上的音樂文件列表, 並可以點擊列表選擇某一首歌曲進行播放。 運行效果: 點擊download出現: 然後點擊歌曲調用系統播放器播
最近做了一個demo——功能是給自己的應用加鎖,訪問的時候需要輸入密碼,並且每次鎖屏再開之後都需要重新輸入密碼。 主要思路: 設置一個全局變量表示當前應用的狀態(布爾&
模糊搜索框。APP需要一個該控件,安卓端。先上個圖,看起來不錯的效果。圖一為未點擊狀態,圖二為點擊之後的狀態。圖三為輸入之後的狀態。主要的功能點有 :1、點擊直接懸浮層,
緒論最近一直比較忙,也沒抽出時間來寫博客,也不得不說是自己犯了懶癌,人要是一懶就什麼事都不想做了,如果不能堅持下來的話,那麼估計就廢了,��。