第一章 四大組件 之 Service(二)

第一章 四大組件

第二組件 Service

(一)基礎知識

1.定義

服務,是Android四大組件之一, 屬於 計算型組件

2.作用

提供 需在後臺長期運行的服務,如:複雜計算、音樂播放、下載等

3.特點

無用戶界面、在後臺運行、生命週期長
一個運行在後臺執行長時間運行的操作組件,它不提供任何用戶界面,作爲與Activity同級的組件,它依舊是運行在主線程中(由於是在主線程,所以需開一個線程來執行耗時操作)。
一個組件還可以與一個Service進行綁定來實現組件之間的交互,甚至可以執行IPC(Inter-Process Communication)進程間通信。
Service可以在後臺執行很多任務,比如處理網絡事務,播放音樂,文件讀寫或者與一個內容提供者交互,等等。

(二)生命週期

在這裏插入圖片描述

1.生命週期常用方法

1、4個手動調用方法
startService() 啓動服務
stopService() 關閉服務
bindService() 綁定服務
unbindService() 解綁服務
2、5個內部自動調用方法
onCreat() 創建服務
onStartCommand() 開始服務
onDestroy() 銷燬服務
onBind() 綁定服務
onUnbind() 解綁服務

2.生命週期方法具體介紹

1、startService()

(1)作用:啓動Service服務
(2)自動調用方法:onCreate()、onStartCommand()
a)一個Service被sartService多次啓用,onCreate()只會調用一次,onStartCommand()可以多次調用(=startService()調用次數)
b)onStartCommand()必須返回一個整數=描述系統因異常(1.內存不足2.進程關閉等)在殺死onStartCommand()後的服務後應該如何繼續運行

Service中onStartCommand回調四種返回值的區別
  • START_NOT_STICKY:系統在onStartCommand()返回後終止服務,不會重新啓動服務。除非有掛起 Intent 要傳遞,否則系統不會重建服務。這是最安全的選項,可以避免在不必要時服務自動重啓以及應用能夠輕鬆重啓所有未完成的作業時運行服務。
  • START_STICKY:系統在onStartCommand()返回後終止服務,會重新啓動服務&調用onStartCommand(),但不保留已傳入的intent。用null intent調用onStartCommand()。除非有掛起的未發送完的啓動服務的intent,會依此繼續傳入intent。適用於媒體播放器類似服務,不執行命令,但要一直執行並隨時待命。
  • START_REDELIVER_INTENT:系統在onStartCommand()返回後終止服務,會重新啓動service&通過傳遞給服務最後一個intent調用onstartCommand()。任何掛起的未發送完的intent,會依此傳入。適用於主動執行應該立即恢復工作的活躍服務,比如下載文件。
    實例:
    服務
public class MyService extends Service {

    private static final String TAG = MyService.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate");
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "postDelayed");
                // 製造異常,kill 掉該 Service
                int a = 1 / 0;
            }
        }, 3000L);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand, intent: " + intent + ", startId: " + startId);
        return Service.START_STICKY_COMPATIBILITY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
    }
}

測試

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService(new Intent(MainActivity.this, MyService.class));
    }
}

測試結果:
1、使用 START_STICKY 作爲返回值

// 服務重新創建並重啓,但是 intent 對象被置空了
03-22 20:43:42.199 11233-11233/com.example.test E/MyService: onCreate
03-22 20:43:42.209 11233-11233/com.example.test E/MyService: onStartCommand, intent: null, startId: 2
03-22 20:43:45.209 11233-11233/com.example.test E/MyService: postDelayed

2、使用 START_NOT_STICKY 作爲返回值

// 服務沒有重新創建

3、使用 START_REDELIVER_INTENT 作爲返回值

// 服務重新創建並啓動,intent 對象沒有被置空
03-22 20:46:00.879 14792-14792/com.example.test E/MyService: onCreate
03-22 20:46:00.879 14792-14792/com.example.test E/MyService: onStartCommand, intent: Intent { cmp=com.example.test/.MyService }, startId: 1
03-22 20:46:03.889 14792-14792/com.example.test E/MyService: postDelayed

(3)示意圖:
在這裏插入圖片描述

2、stopService()

(1)作用:關閉Service服務
(a)未啓動的服務(本身關閉)(b)啓動&綁定&未解綁的服務無法調用onDestroy()——>無法關閉服務
(2)自動調用的方法:onDestroy()
(3)示意圖:
在這裏插入圖片描述

3、bindService()

(1)作用:綁定Service服務
(2)自動調用的方法:onCreate()、onBind()
(3)示意圖:
在這裏插入圖片描述

4、unbindService()

(1)作用:解綁Service服務
(2)自動調用的方法:onUnbind()、onDestroy()
(3)示意圖:
在這裏插入圖片描述

3.應用場景

1、啓動服務——只使用startService

(1)流程圖:
在這裏插入圖片描述
(2)備註:
1、一個Service的onCreate()只調用一次(只有一個Service實例),onStartCommand()調用次數=startService調用次數
2、startService與stopService配對(必須通過stopService關閉Service)
3、只使用startService(),無法與Activity交互,綁定者無法操作Service

2、綁定服務——只使用BindService

(1)流程圖:
在這裏插入圖片描述
(2)備註:
1、一個Service的onCreate()只調用一次(只有一個Service實例),onBind()調用次數=BindService調用次數,多個綁定者可以綁定到同一個服務上
2、bindService與unbindService配對,當該Service所有綁定者解綁後,系統會自動銷燬服務(不需手動stop)
3、bindService能讓Activity與Service交互,綁定者通過一個iBinder接口與服務通信

3、啓動服務後綁定——startService()+bindService

(1)流程圖: 在這裏插入圖片描述
(2)備註:
1、關於操作Service:
(1)startService()、stopService()只能開啓&關閉Service,但無法操作Service
(2)bindService()、unbindService()除了綁定Service還能操作Service
2、關於Service銷燬:
(1)startService()開啓的Service,綁定者退出後Service仍存在
(2)bindService()開啓的Service,綁定者退出後,Service隨着調用者退出銷燬

(三)Service分類

1.類型

1、按運行地點分(1)本地服務(2)遠程服務
2、按運行類型分(1)前臺服務(2)後臺服務
3、按功能分(1)可通信服務(2)不可通信服務

2.詳細介紹

1、按運行地點分
(1)本地服務

1、特點:(1)運行在主線程(2)主線程被終止後,服務也會終止(3)節約資源,且不需要AIDL/IPC,簡便
2、場景:(最常用)需要依附某個進程,不需要常駐的服務,如音樂播放
3、使用:
步驟1:新建子類繼承Service類——需重寫父類的onCreate()、onStartCommand()、onDestroy()和onBind()方法

public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("執行了onCreat()");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

步驟2:構建用於啓動Service的Intent對象
步驟3:調用startService()啓動Service、調用stopService()停止服務

//構建啓動服務的Intent對象
Intent startIntent = new Intent(this, MyService.class);
//調用startService()方法-傳入Intent對象,以此啓動服務
startService(startIntent);

//構建停止服務的Intent對象
Intent stopIntent = new Intent(this, MyService.class);
//調用stopService()方法-傳入Intent對象,以此停止服務
stopService(stopIntent);

步驟4:在AndroidManifest.xml裏註冊Service

<application
    //註冊Service服務
    <service android:name=".MyService">
    </service>
</application>
(2)遠程服務

2.1)特點:

  1. 運行在獨立進程(佔用一定資源)
  2. 服務常駐後臺,不受其他Activity影響(Activity所在進程被kill時,服務仍在進行)
  3. 使用AIDL進行IPC

2.2)應用場景:系統級別服務(常駐),多個應用程序共享同一個後臺服務(跨進程通信)
2.3)遠程服務Service(含AIDL&IPC講解)
2.4)IntentService用法&源碼(客戶端遠程調用服務器端的遠程Service)
1.AIDL與IPC(使用AIDL進行IPC)
IPC:Inter-Process Communication,即跨進程通信
AIDL:Android Interface Definition Language,即Android接口定義語言;用於讓某個Service與多個應用程序組件之間進行跨進程通信,從而可以實現多個應用程序共享同一個Service的功能。
2.多進程通信中2個角色:
服務端:
步驟1:新建定義AIDL文件,並聲明該服務需要向客戶端提供的接口

// 在新建的AIDL_Service1.aidl裏聲明需要與Activity進行通信的方法
package scut.carson_ho.demo_service;
interface AIDL_Service1 {
    void AIDL_Service();
}

步驟2:在Service子類中實現AIDL中定義的接口方法,並定義生命週期的方法(onCreat、onBind()、blabla)

public class MyService extends Service {
    // 實例化AIDL的Stub類(Binder的子類)
    AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {
        //重寫接口裏定義的方法,用於客戶端調用
        @Override
        public void AIDL_Service() throws RemoteException {
            System.out.println("客戶端通過AIDL與遠程後臺成功通信");
        }
    };
    //重寫與Service生命週期的相關方法
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("執行了onCreat()");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("執行了onBind()");
        //在onBind()返回繼承自Binder的Stub類型的Binder,非常重要
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("執行了onUnbind()");
        return super.onUnbind(intent);
    }
}

步驟3:在AndroidMainfest.xml中註冊服務 & 聲明爲遠程服務

 <service
        android:name=".MyService"
        android:process=":remote"  //將本地服務設置成遠程服務
    android:exported="true"      //設置可被其他進程調用
    >
    //該Service可以響應帶有scut.carson_ho.service_server.AIDL_Service1這個action的Intent。
    //此處Intent的action必須寫成“服務器端包名.aidl文件名”
    <intent-filter>
        <action android:name="scut.carson_ho.service_server.AIDL_Service1"/>
    </intent-filter>
</service>

客戶端:
步驟1:拷貝服務端的AIDL文件到客戶端目錄下,並進行編譯
步驟2:使用Stub.asInterface接口獲取服務器的Binder,根據需要調用服務提供的接口方法

//定義aidl接口變量
private AIDL_Service1 mAIDL_Service;
//創建ServiceConnection的匿名類
private ServiceConnection connection = new ServiceConnection() {
    //重寫onServiceConnected()方法和onServiceDisconnected()方法
    //在Activity與Service建立關聯和解除關聯的時候調用
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //使用AIDLService1.Stub.asInterface()方法獲取服務器端返回的IBinder對象
        //將IBinder對象傳換成了mAIDL_Service接口對象
        mAIDL_Service = AIDL_Service1.Stub.asInterface(service);
        try {
            //通過該對象調用在MyAIDLService.aidl文件中定義的接口方法,從而實現跨進程通信(客戶端調用服務端方法)
            mAIDL_Service.AIDL_Service();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
};

步驟3:通過Intent指定服務端的服務名稱和所在包,綁定遠程Service

//通過Intent指定服務端的服務名稱和所在包,與遠程Service進行綁定
//參數與服務器端的action要一致,即"服務器包名.aidl接口文件名"
Intent intent = new Intent("scut.carson_ho.service_server.AIDL_Service1");
//Android5.0後無法只通過隱式Intent綁定遠程Service
//需要通過setPackage()方法指定包名
intent.setPackage("scut.carson_ho.service_server");
//綁定服務,傳入intent和ServiceConnection對象
bindService(intent, connection, Context.BIND_AUTO_CREATE);
2、按運行類型分
(1)前臺服務

1.1)特點:在通知欄顯示通知(用戶可以看到)
1.2)應用場景:服務使用時需要讓用戶知道並進行相關操作,如音樂播放服務(服務被終止時,通知欄的通知也會消失)
1.3)使用方法:
Service類onCreate()增加通知欄顯示功能

//添加下列代碼將後臺Service變成前臺Service
//構建"點擊通知後打開MainActivity"的Intent對象
Intent notificationIntent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);

//新建Builer對象
Notification.Builder builer = new Notification.Builder(this);
builer.setContentTitle("前臺服務通知的標題");//設置通知的標題
builer.setContentText("前臺服務通知的內容");//設置通知的內容
builer.setSmallIcon(R.mipmap.ic_launcher);//設置通知的圖標
builer.setContentIntent(pendingIntent);//設置點擊通知後的操作

Notification notification = builer.getNotification();//將Builder對象轉變成普通的notification
startForeground(1, notification);//讓Service變成前臺Service,並在系統的狀態欄顯示出來
(2)後臺服務

後臺:後臺任務運行完全不依賴UI,即時Activity被銷燬/程序被關閉,只要進程還在,後臺任務就可繼續運行
2.1)特點:處於後臺的服務(用戶無法看到)
2.2)應用場景:服務使用時不需要讓用戶知道並進行相關操作,如天氣更新,日期同步(服務被終止時,用戶是無法知道的)

3、按功能分
(1)可通信服務

1.1)特點:a)用bindServiceb)調用者退出後,隨調用者銷燬c)只有綁定Service服務(Binder類、bindService()、onBind()、unbindService()、onUnbind())才能與Activity通信
1.2)應用場景:後臺服務需要與Activity進行通信
1.3)使用方法:
步驟1:在新建子類繼承Service類,並新建一個子類繼承自Binder類、寫入與Activity關聯需要的方法、創建實例

public class MyService extends Service {
    private MyBinder mBinder = new MyBinder();
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("執行了onCreat()");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("執行了onBind()");
        //返回實例
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("執行了onUnbind()");
        return super.onUnbind(intent);
    }
    //新建一個子類繼承自Binder類
    class MyBinder extends Binder {

        public void service_connect_Activity() {
            System.out.println("Service關聯了Activity,並在Activity執行了Service的方法");
        }
    }
}

步驟2:在Activity通過調用MyBinder類中的public方法來實現Activity與Service的聯繫(Activity指揮Service執行的功能)
1、構建綁定服務的Intent對象

//構建綁定服務的Intent對象
Intent bindIntent = new Intent(this, MyService.class);
//調用bindService()方法,以此停止服務
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//參數說明
//第一個參數:Intent對象
//第二個參數:上面創建的Serviceconnection實例
//第三個參數:標誌位
//這裏傳入BIND_AUTO_CREATE表示在Activity和Service建立關聯後自動創建Service
//這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行
//調用unbindService()解綁服務
//參數是上面創建的Serviceconnection實例
unbindService(connection);

2、創建ServiceConnection匿名類,,在Activity和Service建立和解除關聯時調用

//創建ServiceConnection的匿名類
private ServiceConnection connection = new ServiceConnection() {
    //重寫onServiceConnected()方法和onServiceDisconnected()方法
    //在Activity與Service建立關聯和解除關聯的時候調用
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
    //在Activity與Service解除關聯的時候調用
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //實例化Service的內部類myBinder
        //通過向下轉型得到了MyBinder的實例
        myBinder = (MyService.MyBinder) service;
        //在Activity調用Service類的方法
        myBinder.service_connect_Activity();
    }
};
(2)不可通信服務

2.1)特點:a)用startService啓動b)調用者退出後,隨調用者銷燬
2.2)應用場景:該後臺服務不進行任何通信

(四)Service使用解析【見Service分類】

1.本地服務(普通服務、不可通信服務)
2.可通信服務
3.前臺服務
4.遠程服務

(五)AndroidManifest.xml中Service元素常見屬性

1、android:name: 服務類名。可以是完整的包名+類名。也可使用. 代替包名。
2、android:exported: 其他應用能否訪問該服務,如果不能,則只有本應用或有相同用戶ID的應用能訪問。默認爲false。
3、android:process: 服務所運行的進程名。默認是在當前進程下運行,與包名一致。
4、android:permission: 申請使用該服務的權限,如果沒有配置下相關權限,服務將不執行

(六)對比

1.Service和Thread區別

(1)運行線程

Service運行在主線程(不能處理耗時操作,否則會出現ANR),Thread運行在工作線程

(2)運行範圍

Service在進程運行

  1. 完全不依賴UI/Activity,只要進程還在,Service就可繼續運行
  2. 所有的Activity都可與Service關聯,獲得Service實例&操作其中方法
  3. 若要處理耗時操作,則在Service裏創建Thread子線程執行

Thread在Activity運行

  1. 依賴於某個Activity
  2. 在一個Activity中創建子線程,另一個Activity無法對其操作
  3. Activity很難控制Thread,且Activity被銷燬後,無法再獲取之前創建的子線程實例

2.Service和IntentService區別

(1)什麼是IntentService?爲什麼要使用IntentService?

Service是用於後臺服務的,保證程序掛到後臺時某些組件仍能正常工作。然而Service不是獨立的進程,是默認運行在主線程中的。所以如果直接在服務裏去處理一些耗時的邏輯,就很容易出現ANR(Application Not Responding)的情況。
所以這個時候就需要用到Android多線程編程的技術了,我們可以在服務內開啓線程,採用thread+handler方式處理耗時操作。但服務一旦啓動,就會一直處於運行狀態,必須調用stopSelf()/stopService()才能讓服務停止。編寫邏輯較複雜。
我們可以引用IntentService。
IntentService是繼承Service的,那麼它包含了Service的全部特性,當然也包含service的生命週期,那麼與service不同的是,IntentService在執行onCreate操作的時候,內部開了一個線程,去執行耗時操作。
IntentService是一個通過Context.startService(Intent)啓動可以處理異步請求的Service,使用時你只需要繼承IntentService和重寫其中的onHandleIntent(Intent)方法接收一個Intent對象,工作完成後會自動停止服務。所有的請求的處理都在一個工作線程中完成,它們會交替執行(但不會阻塞主線程的執行),一次只能執行一個請求。

這是一個基於消息的服務,每次啓動該服務並不是馬上處理你的工作,而是首先會創建對應的Looper,Handler並且在MessageQueue中添加的附帶客戶Intent的Message對象,當Looper發現有Message的時候接着得到Intent對象通過在onHandleIntent((Intent)msg.obj)中調用你的處理程序,處理完後即會停止自己的服務,意思是Intent的生命週期跟你的處理的任務是一致的,所以這個類用下載任務中非常好,下載任務結束後服務自身就會結束退出。

(2)特點
  • 會創建獨立的worker線程來處理所有的Intent請求;
  • 會創建獨立的worker線程來處理onHandleIntent()方法實現的代碼,無需處理多線程問題;
  • 所有請求處理完成後,IntentService會自動停止,無需調用stopSelf()方法停止Service;
(3)實例
(3.1)新建一個MyIntentService類繼承自IntentService

1、提供一個無參的構造函數,並且必須在其內部調用父類的有參構造函數。
2、在其子類中去實現onHandlerIntent()這個抽象方法,在這個方法中可以去處理一些具體的邏輯。(用於處理耗時操作,該方法在子線程中執行)

public class MyIntentService extends IntentService{
   public MyIntentService(){
      super("MyIntentService"); //調用父類的有參構造函數
   }
   @Override
   protected void onHandlerIntent(Intent intent){
       //打印當前線程的id
       Log.d("MyIntentService","Thread id is"+       Thread.currentThread().getId());
       }
       @Override
       public void onDestroy() {
          super.onDestroy();
          Log.d("MyIntentService","onDestroy executed");//運行結束後服務自動停止
   }       
 }  
(3.2)在Activity啓動服務
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.id.activity_main);
    Button startIntentService=(Button)findViewById(R.id.start_intent_service);
 startIntentService.setOnClickListener(this);
 }
 @Override
 public void onClick(View v){
    switch(v.getId()) {
    ...
    case R.id.start_intent_service:
    //打印主線程的id
    Log.d("MainActivity","Thread id        is"+Thread.currentThread().getId());
Intent intentService=new Intent(this,MyIntentService.class);
startService(intentService);//啓動服務
      break;
  default:
   break;
}
}
}    
(3.3)在AndroidManifest註冊服務
<application
<service android:name=".MyIntentService" />
</application>
(4)IntentService與service的區別

IntentService是繼承並處理異步請求的一個類,在IntentService內有一個工作線程來處理耗時操作,啓動IntentService的方式和啓動傳統的Service一樣,同時,當任務執行完後,IntentService會自動停止,而不需要我們手動去控制或stopSelf()。
另外,可以啓動IntentService多次,而每一個耗時操作會以工作隊列的方式在IntentService的onHandleIntent回調方法中執行,並且,每次只會執行一個工作線程,執行完第一個再執行第二個,以此類推。它本質上就是一個封裝了HandlerThread+Handler的異步框架

(七)Activity與Service通信方式總結

*方法1:binder+回調(listener)

當Activity通過調用bindService(Intent service, ServiceConnection conn,int flags),我們可以得到一個Service的一個對象實例,然後我們就可以訪問Service中的方法,同時利用回調接口實現Service中進度變化主動通知Activity更新UI
(1)新建一個回調接口,通過回調接口實現當Service中進度發生變化主動通知Activity更新UI
(回調接口的理解見:一個經典例子讓你徹徹底底理解java回調機制

public interface OnProgressListener {
    void onProgress(int progress);
}

(2)新建一個Service類

public class MsgService extends Service {

    public static final int MAX_PROGRESS = 100;//進度條最大值
    private int progress = 0;//進度條進度值

    private OnProgressListener onProgressListener;//更新進度的回調接口
    
    public void setOnProgressListener(OnProgressListener onProgressListener) {
     	//註冊回調接口的方法,供外部調用
        this.onProgressListener = onProgressListener;
    }

    public int getProgress() {
    //增加get()方法,供Activity調用,返回progress
        return progress;
    }

    /**
     * 模擬下載任務,每秒鐘更新一次
     */
    public void startDownLoad(){
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                while(progress < MAX_PROGRESS){
                    progress += 5;
                    
                    //進度發生變化通知調用方
                    if(onProgressListener != null){
                        onProgressListener.onProgress(progress);
                    }
                    
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                }
            }
        }).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MsgBinder();//返回Binder對象
    }
    
    public class MsgBinder extends Binder{
        public MsgService getService(){
            return MsgService.this;//返回當前service對象
        }
    }

}

(3)新建一個ServiceConnection對象,它是一個接口,Activity與Service綁定後,在onServiceConnected回調方法中返回服務對象。
onServiceConnected用於執行Activity與Service綁定後執行相關操作。

ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //返回一個MsgService對象
            msgService = ((MsgService.MsgBinder)service).getService();
        }
    };

(4)Activity代碼

public class MainActivity extends Activity {
    private MsgService msgService;
    private ProgressBar mProgressBar;
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //綁定Service
        Intent intent = new Intent("com.example.communication.MSG_ACTION");
        bindService(intent, conn, Context.BIND_AUTO_CREATE);

        mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
        Button mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                //開始下載
                msgService.startDownLoad();
            }
        });
    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //返回一個MsgService對象
            msgService = ((MsgService.MsgBinder)service).getService();
            
            //註冊回調接口來接收下載進度的變化
            msgService.setOnProgressListener(new OnProgressListener() {
                
                @Override
                public void onProgress(int progress) {
                    mProgressBar.setProgress(progress);
                }
            });
        }
    };

    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }
}

*方法2:廣播(推薦LocalBroadcaseManager)

利用系統的LocalBroadcastManager,Service send message, Activity receive message;Service向Activity發送消息,可以使用廣播,當然Activity要註冊相應的接收器。適用於Service要向多個Activity發送同樣的消息。
當進度變化時發送一條廣播,然後在Activity的註冊廣播接收器,接受廣播後更新progressbar

public class MainActivity extends Activity {
    private ProgressBar mProgressBar;
    private Intent mIntent;
    private MsgReceiver msgReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //動態註冊廣播接收器
        msgReceiver = new MsgReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.communication.RECEIVER");
        registerReceiver(msgReceiver, intentFilter);

        mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
        Button mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                //啓動服務
                mIntent = new Intent("com.example.communication.MSG_ACTION");
                startService(mIntent);
            }
        });
    }

    @Override
    protected void onDestroy() {
        //停止服務
        stopService(mIntent);
        //註銷廣播
        unregisterReceiver(msgReceiver);
        super.onDestroy();
    }


    /**
     * 廣播接收器
     * @author len
     *
     */
    public class MsgReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //拿到進度,更新UI
            int progress = intent.getIntExtra("progress", 0);
            mProgressBar.setProgress(progress);
        }  
    }
}
public class MsgService extends Service {
    /**
     * 進度條的最大值
     */
    public static final int MAX_PROGRESS = 100;
    /**
     * 進度條的進度值
     */
    private int progress = 0;
    private Intent intent = new Intent("com.example.communication.RECEIVER");

    /**
     * 模擬下載任務,每秒鐘更新一次
     */
    public void startDownLoad(){
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                while(progress < MAX_PROGRESS){
                    progress += 5;
                    
                    //發送Action爲com.example.communication.RECEIVER的廣播
                    intent.putExtra("progress", progress);
                    sendBroadcast(intent);
                    
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                }
            }
        }).start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startDownLoad();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

方法3:binder+Handler

Service 持有 Activity的Handler 對象,Service 通過往該Handler send message 的方式,達到通信的目的。

方法4:開源組件(EventBus,otto)

利用反射或者註釋的方式實現對註冊類的註冊,然後遍歷當前的註冊表,通過key進行查詢,然後dispatch,調用相應的事件處理方法。(不同的開源框架實現有所區別)

方法5:AIDL

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章