Android 4 学习(19):Services
參考《Professional?Android?4?Development》
?
Services
Service是invisible的,因此其優(yōu)先級不高于visible的Activity,之所以說不高于,是因為我們可以設(shè)置Service為在前臺運(yùn)行。
創(chuàng)建Service
Android提供了Service抽象類,繼承它便可以創(chuàng)建一個Service類:
?
import android.app.Service; import android.content.Intent; import android.os.IBinder; public class MyService extends Service {@Overridepublic void onCreate() {super.onCreate();// TODO: Actions to perform when service is created.}@Overridepublic IBinder onBind(Intent intent) {// TODO: Replace with service binding implementation.return null;} }?
創(chuàng)建Service類之后,還需要在Manifest里面注冊:
?
<service android:enabled=”true” android:name=”.MyService” android:permission=”com.paad.MY_SERVICE_PERMISSION”/>?
若要默認(rèn)只有自己的程序可以使用這個Service,需要添加Android:permission屬性。其他Application若要使用這個服務(wù),需要加入這個permission。
執(zhí)行Service
當(dāng)Service由startService()方法調(diào)用時,即可引起onStartCommand方法的調(diào)用,因此需要重寫Service中的onStartCommand方法,并且onStartCommand方法可能會被多次調(diào)用。onStartCommand()方法將返回一個int值,用于指定當(dāng)Service被runtime殺掉又重啟的時,系統(tǒng)該如何響應(yīng):
?
@Override public int onStartCommand(Intent intent, int flags, int startId) {startBackgroundTask(intent, startId);return Service.START_STICKY; }?
重啟Service
onStartCommand()方法可以返回這些參數(shù),它們的意義是:
START_STICKY:如果service進(jìn)程被kill掉,保留service的狀態(tài)為開始狀態(tài),但不保留遞送的intent對象。隨后系統(tǒng)會嘗試重新創(chuàng)建service,由于服務(wù)狀態(tài)為開始狀態(tài),所以創(chuàng)建服務(wù)后一定會調(diào)用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到service,那么參數(shù)Intent將為null。
START_NOT_STICKY:“非粘性的”。如果在執(zhí)行完onStartCommand后,服務(wù)被異常kill掉,系統(tǒng)不會自動重啟該服務(wù)。
START_REDELIVER_INTENT:使用這個返回值時,如果未執(zhí)行完onStartCommand,服務(wù)在調(diào)用stopSelf之前被kill掉,系統(tǒng)會自動重啟該服務(wù),并將Intent的值傳入。
參考:http://www.krislq.com/2013/05/android-class-return-value-of-onstartcommand/
啟動和停止服務(wù)
調(diào)用startService方法可以啟動服務(wù):
?
private void explicitStart() {// Explicitly start My ServiceIntent intent = new Intent(this, MyService.class);// TODO Add extras if required.startService(intent); } private void implicitStart() {// Implicitly start a music ServiceIntent intent = new Intent(MyMusicService.PLAY_ALBUM);intent.putExtra(MyMusicService.ALBUM_NAME_EXTRA, “United”);intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA, “Pheonix”);startService(intent); }?
調(diào)用stopService方法可以停止服務(wù):
?
// Stop a service explicitly. stopService(new Intent(this, MyService.class)); // Stop a service implicitly. Intent intent = new Intent(MyMusicService.PLAY_ALBUM); stopService(intent);?
服務(wù)自殺
服務(wù)內(nèi)部調(diào)用stopSelf方法,可以停止該服務(wù)。
綁定Service到Activity
首先,Service要實現(xiàn)IBind接口:
?
@Override public IBinder onBind(Intent intent) {return binder; } public class MyBinder extends Binder {MyMusicService getService() {return MyMusicService.this;} } private final IBinder binder = new MyBinder();?
ServiceConnection類用于表示Service到Activity的綁定,每個綁定都需要創(chuàng)建一個ServiceConnection:
?
// Reference to the service private MyMusicService serviceRef; // Handles the connection between the service and activity private ServiceConnection mConnection = new ServiceConnection() {public void onServiceConnected(ComponentName className, IBinder service) {// Called when the connection is made.serviceRef = ((MyMusicService.MyBinder)service).getService();}public void onServiceDisconnected(ComponentName className) {// Received when the service unexpectedly disconnects.serviceRef = null;} };?
最后,調(diào)用bindService方法,傳入用于啟動Service的Intent,ServiceConnection和標(biāo)志位:
?
// Bind to the service Intent bindIntent = new Intent(MyActivity.this, MyMusicService.class); bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);?
通常情況下,Android應(yīng)用在自己的內(nèi)存空間中運(yùn)行,并不共享內(nèi)存。若要與其他進(jìn)程通信,可以使用廣播,或在Intent中添加Bundle參數(shù)啟動其他Service的方法。如果需要更緊密的通信,可以使用Android?Interface?Defi?nition?Language(AIDL)。AIDL使用OS級別的簡單變量定義了接口,可以跨應(yīng)用傳遞對象。
?
創(chuàng)建前臺服務(wù)
?
使用startForeground方法啟動服務(wù),可以使服務(wù)獲得與Visible?Activity相同的優(yōu)先級,例如:
?
private void startPlayback(String album, String artist) {int NOTIFICATION_ID = 1;// Create an Intent that will open the main Activity if the notification is clicked.Intent intent = new Intent(this, MyActivity.class);PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);// Set the Notification UI parametersNotification notification = new Notification(R.drawable.icon, “Starting Playback”, System.currentTimeMillis());notification.setLatestEventInfo(this, album, artist, pi);// Set the Notification as ongoingnotification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;// Move the Service to the ForegroundstartForeground(NOTIFICATION_ID, notification); }?
使用stopForeground方法可以取消Service的前臺屬性:
?
public void pausePlayback() {// Move to the background and remove the NotificationstopForeground(true); }?
使用后臺線程
Service和Activity一樣,也是在主線程中運(yùn)行的,為了更好地響應(yīng)用戶,我們需要使用后臺線程的方式執(zhí)行Service。Android提供了兩個抽象類來幫助我們實現(xiàn):AsyncTask和IntentService。
使用AsyncTask
AsyncTask不僅能幫助我們將費(fèi)時操作放到后臺執(zhí)行,還可以實現(xiàn)和UI線程的同步。AsyncTask適合執(zhí)行那些耗時比較短并且和UI線程有交互的任務(wù),對于耗時較久的任務(wù)(例如下載),使用Service更為合適。需要注意的是AsyncTask在應(yīng)用restart之后會被cancel掉。
?
private class MyAsyncTask extends AsyncTask<String, Integer, String> { @Override protected String doInBackground(String... parameter) { // Moved to a background thread. String result = “”; int myProgress = 0; int inputLength = parameter[0].length(); // Perform background processing task, update myProgress] for (int i = 1; i <= inputLength; i++) { myProgress = i; result = result + parameter[0].charAt(inputLength-i); try { Thread.sleep(100); } catch (InterruptedException e) { } publishProgress(myProgress); } // Return the value to be passed to onPostExecute return result; } @Override protected void onProgressUpdate(Integer... progress) { // Synchronized to UI thread. // Update progress bar, Notification, or other UI elements asyncProgress.setProgress(progress[0]); } @Override protected void onPostExecute(String result) { // Synchronized to UI thread. // Report results via UI update, Dialog, or notifications asyncTextView.setText(result); } }?
使用AsyncTask,首先要創(chuàng)建一個AsyncTask的子類類并實現(xiàn)doInBackground,onProgressUpdate,onPostExecute方法。這三個方法的說明:
- doInBackground:?這個方法用于執(zhí)行需要在后臺線程中長期運(yùn)行的操作,可以通過publishProgress方法傳遞參數(shù)給onProgressUpdate;在這個方法執(zhí)行完畢之后,此方法將返回值傳遞給onProgressUpdate。
- onProgressUpdate:?接收publishProgress方法傳入的參數(shù),更新UI。
- onPostExecute:?doInBackground執(zhí)行結(jié)束后,將返回值傳入此方法。
執(zhí)行AsyncTask
String input = “redrum ... redrum”; new MyAsyncTask().execute(input);Intent?Service簡介
IntentService可以通過傳入Intent參數(shù)調(diào)用,傳入的Intent將進(jìn)入一個隊列中被異步執(zhí)行。IntentService封裝了消息的異步處理,后臺線程創(chuàng)建以及與UI線程的同步。繼承IntentService類并實現(xiàn)onHandleIntent方法,即可創(chuàng)建一個Intent?Service:
?
import android.app.IntentService; import android.content.Intent; public class MyIntentService extends IntentService {public MyIntentService(String name) {super(name);// TODO Complete any required constructor tasks.}@Overridepublic void onCreate() {super.onCreate();// TODO: Actions to perform when service is created.}@Overrideprotected void onHandleIntent(Intent intent) {// This handler occurs on a background thread. TODO The time consuming task should be implemented here.// Each Intent supplied to this IntentService will be processed consecutively here. When all incoming Intents have been processed the Service will terminate itself.} }?
Loader簡介
Loader是一個抽象類,封裝了異步加載數(shù)據(jù)的最佳實踐,最典型的就是CursorLoader了。Android中創(chuàng)建Loader類的簡單方法是繼承AsyncTaskLoader類,并實現(xiàn)這兩個功能:
- 異步加載數(shù)據(jù)
- 監(jiān)測數(shù)據(jù)源的變化并及時更新
手動創(chuàng)建線程及GUI線程同步
盡管AsyncTask和Intent?Service提供了簡單易用的異步類封裝,但我們也可以創(chuàng)建自定義的異步線程:
?
// This method is called on the main GUI thread. private void backgroundExecution() {// This moves the time consuming operation to a child thread.Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);thread.start(); } // Runnable that executes the background processing method. private Runnable doBackgroundThreadProcessing = new Runnable() {public void run() {backgroundThreadProcessing();} }; // Method which does some processing in the background. private void backgroundThreadProcessing() { // [ ... Time consuming operations ... ] }?
與GUI線程同步:
?
runOnUiThread方法會在UI線程執(zhí)行:
?
runOnUiThread(new Runnable() {public void run() {// Update a View or other Activity UI element.} });?
此外,可以使用Handler類更新UI線程:
?
//This method is called on the main GUI thread. private void backgroundExecution() {// This moves the time consuming operation to a child thread.Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);thread.start(); } // Runnable that executes the background processing method. private Runnable doBackgroundThreadProcessing = new Runnable() {public void run() {backgroundThreadProcessing();} }; // Method which does some processing in the background. private void backgroundThreadProcessing() { // [ ... Time consuming operations ... ] // Use the Handler to post the doUpdateGUI // runnable on the main UI thread.handler.post(doUpdateGUI); } //Initialize a handler on the main thread. private Handler handler = new Handler(); // Runnable that executes the updateGUI method. private Runnable doUpdateGUI = new Runnable() {public void run() {updateGUI();} }; // This method must be called on the UI thread. private void updateGUI() { // [ ... Open a dialog or modify a GUI element ... ] }?
Handler類還可以使用postDelayed和postAtTime實現(xiàn)推遲運(yùn)行和推遲指定時間運(yùn)行:
?
// Post a method on the UI thread after 1sec. handler.postDelayed(doUpdateGUI, 1000); // Post a method on the UI thread after the device has been in use for 5mins. int upTime = 1000*60*5; handler.postAtTime(doUpdateGUI, SystemClock.uptimeMillis()+upTime);?
使用ALARMS
與Timer不太,Alarms屬于系統(tǒng)服務(wù),獨(dú)立于應(yīng)用程序。即使應(yīng)用程序為啟動,也可以使用Alarms啟動應(yīng)用程序并獲取其服務(wù),這樣不僅減少了耦合,也減少了系統(tǒng)資源的占用。Android中Alarms常與Broadcast?Receiver一起使用。創(chuàng)建Alarm之前,首先要創(chuàng)建AlarmManager:
?
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);?
創(chuàng)建,設(shè)置和取消ALARMS
創(chuàng)建Alarm需要這些參數(shù):alarm類型,觸發(fā)時間,Alarm將要觸發(fā)的Pending?Intent。目前Alarm類型有這些:
- RTC_WAKEUP:在指定時間啟動指定Pending?Intent,可以喚醒sleep中的設(shè)備。
- RTC:在指定時間啟動指定Pending?Intent,但不能喚醒sleep中的設(shè)備。
- ELAPSED_REALTIME:在某段時間后啟動指定的Pending?Intent,某段時間是從設(shè)備啟動但還沒有喚醒設(shè)備算起。
- ELAPSED_REALTIME_WAKEUP:?這個和ELAPSED_REALTIME的區(qū)別沒搞明白,以后遇到了再查吧。
下面是一個10秒后啟動Pending?Intent的Alarm示例:
?
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); // Set the alarm to wake the device if sleeping. int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP; // Trigger the device in 10 seconds. long timeOrLengthofWait = 10000; // Create a Pending Intent that will broadcast and action String ALARM_ACTION = “ALARM_ACTION”; Intent intentToFire = new Intent(ALARM_ACTION); PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0); // Set the alarm alarmManager.set(alarmType, timeOrLengthofWait, alarmIntent);?
取消Alarm
alarmManager.cancel(alarmIntent);這里的alarmIntent是指使用Alarm啟動的Pending?Intent。
?
創(chuàng)建可重復(fù)的Alarm
?
使用setRepeating或setInexactRepeating方法替代前面的set方法,并傳遞響應(yīng)的參數(shù)進(jìn)去,就可以實現(xiàn)可重復(fù)的Alarm。
?
相比setRepeating,setInexactRepeating更省電,但不能指定某個具體的時間間隔。
?
setInexactRepeating可以接收的時間間隔參數(shù):
?
- INTERVAL_FIFTEEN_MINUTES
- INTERVAL_HALF_HOUR
- INTERVAL_HOUR
- INTERVAL_HALF_DAY
- INTERVAL_DAY
?
下面這個例子指定半小時后啟動Alarm,然后每隔半小時啟動一次:
?
// Get a reference to the Alarm Manager AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); // Set the alarm to wake the device if sleeping. int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP; // Schedule the alarm to repeat every half hour. long timeOrLengthofWait = AlarmManager.INTERVAL_HALF_HOUR; // Create a Pending Intent that will broadcast and action String ALARM_ACTION = “ALARM_ACTION”; Intent intentToFire = new Intent(ALARM_ACTION); PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0); // Wake up the device to fire an alarm in half an hour, and every half-hour after that. alarmManager.setInexactRepeating(alarmType, timeOrLengthofWait, timeOrLengthofWait, alarmIntent);?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/jubincn/p/3521452.html
總結(jié)
以上是生活随笔為你收集整理的Android 4 学习(19):Services的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python测试开发django-44.
- 下一篇: Flask-SQLAlchemy的基本使