參考《Professional Android 4 Development》android
Service是invisible的,所以其優先級不高於visible的Activity,之因此說不高於,是由於咱們能夠設置Service爲在前臺運行。app
Android提供了Service抽象類,繼承它即可以建立一個Service類:異步
import android.app.Service; import android.content.Intent; import android.os.IBinder; public class MyService extends Service { @Override public void onCreate() { super.onCreate(); // TODO: Actions to perform when service is created. } @Override public IBinder onBind(Intent intent) { // TODO: Replace with service binding implementation. return null; } }
建立Service類以後,還須要在Manifest裏面註冊:async
<service android:enabled=」true」 android:name=」.MyService」 android:permission=」com.paad.MY_SERVICE_PERMISSION」/>
若要默認只有本身的程序可使用這個Service,須要添加Android:permission屬性。其餘Application若要使用這個服務,須要加入這個permission。ide
當Service由startService()方法調用時,便可引發onStartCommand方法的調用,所以須要重寫Service中的onStartCommand方法,而且onStartCommand方法可能會被屢次調用。onStartCommand()方法將返回一個int值,用於指定當Service被runtime殺掉又重啓的時,系統該如何響應:post
@Override public int onStartCommand(Intent intent, int flags, int startId) { startBackgroundTask(intent, startId); return Service.START_STICKY; }
onStartCommand()方法能夠返回這些參數,它們的意義是:ui
START_STICKY:若是service進程被kill掉,保留service的狀態爲開始狀態,但不保留遞送的intent對象。隨後系統會嘗試從新建立service,因爲服務狀態爲開始狀態,因此建立服務後必定會調用onStartCommand(Intent,int,int)方法。若是在此期間沒有任何啓動命令被傳遞到service,那麼參數Intent將爲null。this
START_NOT_STICKY:「非粘性的」。若是在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啓該服務。
START_REDELIVER_INTENT:使用這個返回值時,若是未執行完onStartCommand,服務在調用stopSelf以前被kill掉,系統會自動重啓該服務,並將Intent的值傳入。spa
參考:http://www.krislq.com/2013/05/android-class-return-value-of-onstartcommand/線程
調用startService方法能夠啓動服務:
private void explicitStart() { // Explicitly start My Service Intent intent = new Intent(this, MyService.class); // TODO Add extras if required. startService(intent); } private void implicitStart() { // Implicitly start a music Service Intent intent = new Intent(MyMusicService.PLAY_ALBUM); intent.putExtra(MyMusicService.ALBUM_NAME_EXTRA, 「United」); intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA, 「Pheonix」); startService(intent); }
調用stopService方法能夠中止服務:
// Stop a service explicitly. stopService(new Intent(this, MyService.class)); // Stop a service implicitly. Intent intent = new Intent(MyMusicService.PLAY_ALBUM); stopService(intent);
服務內部調用stopSelf方法,能夠中止該服務。
首先,Service要實現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的綁定,每一個綁定都須要建立一個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; } };
最後,調用bindService方法,傳入用於啓動Service的Intent,ServiceConnection和標誌位:
// Bind to the service Intent bindIntent = new Intent(MyActivity.this, MyMusicService.class); bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
一般狀況下,Android應用在本身的內存空間中運行,並不共享內存。若要與其餘進程通訊,可使用廣播,或在Intent中添加Bundle參數啓動其餘Service的方法。若是須要更緊密的通訊,可使用Android Interface Defi nition Language(AIDL)。AIDL使用OS級別的簡單變量定義了接口,能夠跨應用傳遞對象。
使用startForeground方法啓動服務,可使服務得到與Visible Activity相同的優先級,例如:
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 parameters Notification notification = new Notification(R.drawable.icon, 「Starting Playback」, System.currentTimeMillis()); notification.setLatestEventInfo(this, album, artist, pi); // Set the Notification as ongoing notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT; // Move the Service to the Foreground startForeground(NOTIFICATION_ID, notification); }
使用stopForeground方法能夠取消Service的前臺屬性:
public void pausePlayback() { // Move to the background and remove the Notification stopForeground(true); }
Service和Activity同樣,也是在主線程中運行的,爲了更好地響應用戶,咱們須要使用後臺線程的方式執行Service。Android提供了兩個抽象類來幫助咱們實現:AsyncTask和IntentService。
AsyncTask不只能幫助咱們將費時操做放到後臺執行,還能夠實現和UI線程的同步。AsyncTask適合執行那些耗時比較短而且和UI線程有交互的任務,對於耗時較久的任務(例以下載),使用Service更爲合適。須要注意的是AsyncTask在應用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,首先要建立一個AsyncTask的子類類並實現doInBackground,onProgressUpdate,onPostExecute方法。這三個方法的說明:
String input = 「redrum ... redrum」; new MyAsyncTask().execute(input);
IntentService能夠經過傳入Intent參數調用,傳入的Intent將進入一個隊列中被異步執行。IntentService封裝了消息的異步處理,後臺線程建立以及與UI線程的同步。繼承IntentService類並實現onHandleIntent方法,便可建立一個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. } @Override public void onCreate() { super.onCreate(); // TODO: Actions to perform when service is created. } @Override protected 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是一個抽象類,封裝了異步加載數據的最佳實踐,最典型的就是CursorLoader了。Android中建立Loader類的簡單方法是繼承AsyncTaskLoader類,並實現這兩個功能:
儘管AsyncTask和Intent Service提供了簡單易用的異步類封裝,但咱們也能夠建立自定義的異步線程:
// 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 ... ] }
runOnUiThread方法會在UI線程執行:
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實現推遲運行和推遲指定時間運行:
// 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);
與Timer不太,Alarms屬於系統服務,獨立於應用程序。即便應用程序爲啓動,也可使用Alarms啓動應用程序並獲取其服務,這樣不只減小了耦合,也減小了系統資源的佔用。Android中Alarms常與Broadcast Receiver一塊兒使用。建立Alarm以前,首先要建立AlarmManager:
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
建立Alarm須要這些參數:alarm類型,觸發時間,Alarm將要觸發的Pending Intent。目前Alarm類型有這些:
下面是一個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);
alarmManager.cancel(alarmIntent);
這裏的alarmIntent是指使用Alarm啓動的Pending Intent。
使用setRepeating或setInexactRepeating方法替代前面的set方法,並傳遞響應的參數進去,就能夠實現可重複的Alarm。
相比setRepeating,setInexactRepeating更省電,但不能指定某個具體的時間間隔。
setInexactRepeating能夠接收的時間間隔參數:
下面這個例子指定半小時後啓動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);