第一行代碼 讀筆⑦ (Service 多線程基礎)

服務 android多線程編程 服務的基本用法 服務的生命週期 服務技巧

服務(Service): 在Android中實現程序後臺運行的解決方案,本身的運行並不依賴於用戶可視的UI界面。
適用:

  • 並不依賴於用戶可視的UI界面(不一定,前臺Service就是與Notification界面結合使用的)
  • 不需要和用戶交互而且要求長時間運行
    服務依賴於創建服務的所在的應用程序進程。(進程被殺死,依賴該進程的服務停止)
    服務並不會自動開啓線程,因此需要在Service內部創建子線程。

服務基本用法:

Service分類:

  • Started Service:被一個組件調用startService()開啓的Service。

    • 擴展Service:
      這是所有服務的基類。擴展這個類的時候,特別重要的一點是,需要創建一個新的線程來做服務任務,因爲service默認是運行在你的主線程(UI線程)中的,它會使你的主線程運行緩慢。
    • 擴展IntentService:
      service的子類,在一個工作線程(子線程)中處理所有的啓動請求。需要是實現onHandlerIntent()方法,通過這個函數處理接受的每一個啓動請求。 (Android提供的便於創建異步,能自動停止的service)

service的onStartCommand()方法被調,根據傳遞intent開啓Service,擁有一個獨立於開始它的組件(不是進程)的生命週期。
默認service運行在聲明它的應用的同一個進程裏面,而且在應用的主線程裏面。
- Bound Service:
與Activity相綁定的service。

定義一個服務:

  • AndroidManifest.xml 註冊:Service屬於四大組件之一,無論是Started Service和Bound Service,都有Service基類繼承而來,都需要在AndroidManifest.xml中聲明.
<service android:enabled=["true" | "false"]    
    android:exported=["true" | "false"]        //是否能被其他程序組件調用或交互
    android:icon="drawable resource"
    android:isolatedProcess=["true" | "false"]
    android:label="string resource"
    android:name="string"                      //Service類名
    android:permission="string"                //權限聲明
    android:process="string" >                 //設置具體的進程名稱
    . . .
</service>
  • 完成Service類的繼承,自定義MyService類,重寫相應辦法:
public class MyService extends Service {

    @Override
    public IBinder onBind(Intent intent) {        //綁定方法,唯一抽象方法
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("MyService" , "in onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("MyService", "in onStartCommand");
        return super.onStartCommand(intent,flags,startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyService", "in onDestroy");
    }
}

onCreate(…),onStartCommand(…),onDestroy()爲Service相應生命週期階段的回調函數.
onBind(…)函數是Service基類中的唯一抽象方法,子類都必須重寫實現,對於Bound Service,其返回值才具有意義。

啓動和停止服務:
通過startservice() stopservice(),傳遞intent,開啓,停止相應服務。
在service內部任一位置,通過調用stopSelf()方法停止服務。

case R.id.start_service:
     Intent startIntent  = new Intent(this,MyService.class);
     startService(startIntent);
break;
case R.id.stop_service:
     Intent stopIntent = new Intent(this,MyService.class);
     stopService(stopIntent);
break;

活動與服務進行通信(Bound Service):
通過擴展Binder類創建Bound Service的步驟如下:

  • 在Service類中,創建一個Binder實例,包含客戶端能調用的公共方法,返回當前服務對象,在onBind()方法中返回Binder實例
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;
   }
}
  • 自定義的ServiceConnection中實現onServiceConnected(ComponentName name, IBinder binder)方法,獲取Service端Binder實例,通過獲取的Binder實例進行Service端其他公共方法的調用
private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName namee) {
        }

        @Override
        public void onServiceConnected(ComponentName name,IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;   //獲取實例
            downloadBinder.startDownload();                     //完成對於公共方法的調用
            downloadBinder.getProgress();
        }
    };
  • 通過bindService(intent,connection,flag)方法,實現Activity與Service的通信。
    intent : 連接相應的ActivityyuService
    connection:已完成重寫onServiceConnected()方法的ServiceConnection內部類對象。
    flag:關聯模式,採用BIND_AUTO_CREATE即可。
       Intent bindIntent = new Intent(this,MyService.class);
       bindService(bindIntent,connection,BIND_ABOVE_CLIENT);
       break;
  • 通過調用unbindService()方法,解除綁定:
  unbindService(connection); 

服務的生命週期:
生命週期回調方法:

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

這裏寫圖片描述
注:

  • onCreate()方法在service沒有被創建的情況下,啓動服務,纔會被調用。
  • onStartCommand()方法在每一次調用startService()方法時,都會被執行一次,但是並不會創建新的實例。
  • 通過bindService()方法獲取了持久連接,則必須調用unbindService()方法,解除綁定,纔可以調用stopService()方法,結束服務。

前臺服務:
由於Service的系統優先級比較低,在內存不足的情況下,可能被系統回收。爲了避免被系統回收正在運行的Service,使Service可以一直保持運行狀態——前臺服務
前臺服務將會在狀態欄有一個正在運行的圖標,類似於Notification效果。

實現:

  • 構建Notification對象,對其顯示方式進行設置
  • PendingIntent的設置,實現對於點擊意圖的設置
  • 通過startForeground(Id, notification); ID爲通知的Id,notification爲構建的Notification對象,將Service轉變爲前臺服務。
Notification notification = new Notification(R.drawable.ic_launcher,
            "Notification comes", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
            notificationIntent, 0);
            notification.setLatestEventInfo(this, "This is title", "This is content",
            pendingIntent);
startForeground(1, notification);

Android多線程基礎:基本與java多線程編程使用相同的語法。

  • 創建子線程:
class MyThread extends Thread{
      @Override
      public void run(){
      //耗時邏輯
      }
}
---------------------------------------------------
//使用實現Runnable接口定義線程    (耦合性高?)
class MyThread implements Runnable(){
      @Override
      public void run(){
      //耗時邏輯
      }
}
-----------------------------------------------------
//匿名類方式
new Thread (new Runnable(){
    @Override
    public void run(){
    //耗時邏輯
    }
}).start();
  • 啓動線程:
new Thread().start();
----------------------------------------------------------
MyThread myThread = new MyThread();
new Thread(myThread).start;

Android的UI操作屬於線程不安全,即必須在主線程中進行對於UI的操作。
使用異步消息機制,在子線程中執行耗時操作,根據任務結果更新UI。
方法一:異步消息處理:Message,Handle,MessageQueue,Looper構成,詳細解釋
基礎練習
方法二:AsyncTask: Android提供的輕量級的異步類,在類中實現異步操作,並提供接口反饋當前異步執行的程度(可以通過接口實現UI進度更新),最後反饋執行的結果給UI主線程.
同樣基於異步消息處理機制,Android進行了較好的封裝。
優點:

  • 簡單,快捷
  • 過程可控

缺點:

  • 在使用多個異步操作和並需要進行Ui變更時,就變得複雜起來.

AsyncTask直接繼承於Object類,位置爲android.os.AsyncTask。要使用AsyncTask工作我們要提供三個泛型參數,並重載幾個方法(至少重載一個)。
AsyncTask定義了三種泛型類型 ParamsProgressResult

  • Params: 啓動任務執行的輸入參數,比如HTTP請求的URL。
  • Progress: 後臺任務執行的百分比。
  • Result: 後臺執行任務最終返回的結果,比如String。

最少寫以下這兩個方法:

  • doInBackground(Params…) 後臺執行,比較耗時的操作都可以放在這裏。注意這裏不能直接操作UI。此方法在後臺線程執行,完成任務的主要工作,通常需要較長的時間。在執行過程中可以調用publicProgress(Progress…)來更新任務的進度。
  • onPostExecute(Result) 相當於Handler 處理UI的方式,在這裏面可以使用在doInBackground 得到的結果處理操作UI。 此方法在主線程執行,任務執行的結果作爲此方法的參數返回。

補充方法:

  • nProgressUpdate(Progress…) 可以使用進度條增加用戶體驗度。 此方法在主線程執行,用於顯示任務執行的進度。
  • onPreExecute() 這裏是最終用戶調用Excute時的接口,當任務執行之前開始調用此方法,可以在這裏顯示進度對話框。
  • onCancelled() 用戶調用取消時,要做的操作

使用AsyncTask類,以下是幾條必須遵守的準則:

  • Task的實例必須在UI thread中創建;
  • execute方法必須在UI thread中調用;
  • 不可以手動調用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法;
  • 該task只能被執行一次,否則多次調用時將會出現異常;
    AsyncTask基礎練習

IntentService:
由於Serice默認運行與主線程,在處理耗時邏輯情況下,可能出現ANR情況。
可以通過多線程編程,在Service的具體方法內開啓線程,在子線程中處理耗時邏輯,通過stopSelf停止。

public itn onStartCommand(Intent intent,int flags,int startId){
new Thread(new Runnable() {           //開啓子線程
                @Override
                public void run() {
                    //耗時邏輯
                    stopSelf();
                }
            }).start();
        return super.onStartCommand(intent,flags,startId);
        }
}

InentService:
已由Android封裝,自動開啓線程,自動調用stopSelf()方法的InentService類。
IntentService通過worker thread處理每個Intent對象,執行完所有工作後自動停止Service。
實現:通過複寫onHandleIntent()方法,構造。

  • 創建一個與應用程序主線程分開worker thread用來處理所有通過傳遞過來的Intent請求
  • 創建一個work queue,一次只傳遞一個intent到onHandleIntent()方法中,從而不用擔心多線程帶來的問題
  • 當處理完所有請求後自動停止服務,而不需要我們自己調用stopSelf()方法
  • 默認實現了onBind()方法,返回值爲null
  • 默認實現了onStartCommand()方法,這個方法將會把我們的intent放到work queue中,然後在onHandleIntent()中執行
public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d("MyIntentService", "Thread id is " + Thread.currentThread().getId());
    }              //耗時操作 在子線程中運行

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService", "onDestroy executed");
    }
}

使用上與一般Service沒有很大差別。

服務的最佳實踐:
後臺執行的定時任務 (通過定時任務,實現循環)

發佈了68 篇原創文章 · 獲贊 23 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章