Android 4 學習(19):Services

參考《Professional Android 4 Developmentandroid

 

Services

Serviceinvisible的,所以其優先級不高於visibleActivity,之因此說不高於,是由於咱們能夠設置Service爲在前臺運行。app

建立Service

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若要使用這個服務,須要加入這個permissionide

執行Service

ServicestartService()方法調用時,便可引發onStartCommand方法的調用,所以須要重寫Service中的onStartCommand方法,而且onStartCommand方法可能會被屢次調用。onStartCommand()方法將返回一個int值,用於指定當Serviceruntime殺掉又重啓的時,系統該如何響應:post

 

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  startBackgroundTask(intent, startId);
  return Service.START_STICKY;
}

 

重啓Service

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方法,能夠中止該服務。

綁定ServiceActivity

首先,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類用於表示ServiceActivity的綁定,每一個綁定都須要建立一個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方法,傳入用於啓動ServiceIntentServiceConnection和標誌位:

 

// 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);
}

 

使用後臺線程

ServiceActivity同樣,也是在主線程中運行的,爲了更好地響應用戶,咱們須要使用後臺線程的方式執行ServiceAndroid提供了兩個抽象類來幫助咱們實現:AsyncTaskIntentService

使用AsyncTask

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的子類類並實現doInBackgroundonProgressUpdateonPostExecute方法。這三個方法的說明:

  • doInBackground: 這個方法用於執行須要在後臺線程中長期運行的操做,能夠經過publishProgress方法傳遞參數給onProgressUpdate;在這個方法執行完畢以後,此方法將返回值傳遞給onProgressUpdate。
  • onProgressUpdate: 接收publishProgress方法傳入的參數,更新UI
  • onPostExecute: doInBackground執行結束後,將返回值傳入此方法。

執行AsyncTask

String input = 「redrum ... redrum」;
new MyAsyncTask().execute(input);

Intent Service簡介

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簡介

Loader是一個抽象類,封裝了異步加載數據的最佳實踐,最典型的就是CursorLoader了。Android中建立Loader類的簡單方法是繼承AsyncTaskLoader類,並實現這兩個功能:

  • 異步加載數據
  • 監測數據源的變化並及時更新

手動建立線程及GUI線程同步

儘管AsyncTaskIntent 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 ... ]
}

 

GUI線程同步:

 

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類還可使用postDelayedpostAtTime實現推遲運行和推遲指定時間運行:

 

// 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屬於系統服務,獨立於應用程序。即便應用程序爲啓動,也可使用Alarms啓動應用程序並獲取其服務,這樣不只減小了耦合,也減小了系統資源的佔用。AndroidAlarms常與Broadcast Receiver一塊兒使用。建立Alarm以前,首先要建立AlarmManager

 

AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

 

建立,設置和取消ALARMS

建立Alarm須要這些參數:alarm類型,觸發時間,Alarm將要觸發的Pending Intent。目前Alarm類型有這些:

  • RTC_WAKEUP:在指定時間啓動指定Pending Intent,能夠喚醒sleep中的設備。
  • RTC在指定時間啓動指定Pending Intent,但不能喚醒sleep中的設備。
  • ELAPSED_REALTIME:在某段時間後啓動指定的Pending Intent,某段時間是從設備啓動但尚未喚醒設備算起。
  • ELAPSED_REALTIME_WAKEUP: 這個和ELAPSED_REALTIME的區別沒搞明白,之後遇到了再查吧。

下面是一個10秒後啓動Pending IntentAlarm示例:

 

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

 

建立可重複的Alarm

 

使用setRepeating或setInexactRepeating方法替代前面的set方法,並傳遞響應的參數進去,就能夠實現可重複的Alarm

 

相比setRepeating,setInexactRepeating更省電,但不能指定某個具體的時間間隔。

 

setInexactRepeating能夠接收的時間間隔參數:

 

  • 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);
相關文章
相關標籤/搜索