Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的Service與進程間通信(IPC)詳解

Android中的Service與進程間通信(IPC)詳解

編輯:關於Android編程

Service

什麼是Service

在後台長期運行的沒有界面的組件。其他組件可以啟動Service讓他在後台運行,或者綁定Service與它進行交互,甚至實現進程間通信(IPC)。例如,可以讓服務在後台處理網絡交互,播放音樂,文件I/O,或者與ContentProvider交互。

創建一個Service

新建一個類,繼承Service,重寫相關方法,如onBind,onUnBind,onCreate,onDestorey。 在AndroidManifest.xml中配置Service和相關權限。

  ...
  
      
      ...
  

開啟服務:

Intent service = new Intent(this,Service.class);
startService(service);

停止服務:

Intent service = new Intent(this,Service.class);
stopService(service);

綁定服務:

private boolean mIsBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
       // 服務連接成功回調
       @Override
       public void onServiceConnected(ComponentName name, IBinder service) {
           MyService.MyBinder binder = (MyService.MyBinder) service;

       }
       // 服務失去連接回調
       @Override
       public void onServiceDisconnected(ComponentName name) {

       }
   };

@Event(value = R.id.btn_bind_service)
private void onBindServiceClick(View view) {
    bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);// 綁定時如果沒有創建Service則自動創建Service。
    mIsBound = true;
}

解綁服務:

@Event(value = R.id.btn_unbind_service)
private void onUnbindServiceClick(View view) {
    if (!mIsBound) {
        ToastUtil.show("未綁定服務");
        return;
    }
    try {
        unbindService(mConnection);//注意:ServiceConnection要傳綁定時的ServiceConnection對象,否則會報錯。
    } catch (Exception e) {
        ToastUtil.show("解除綁定服務失敗");
        e.printStackTrace();
    }
    mIsBound = false;
}

Service的生命周期

這裡寫圖片描述

public class MyService extends Service {

    // 服務創建
    @Override
    public void onCreate() {
        super.onCreate();
    }

    // 每次startService都會調用;通過bindService方式啟動服務,該方法不會被調用
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    // 服務銷毀
    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    // bindService時調用,返回一個IBinder對象,用於與Service交互,IBinder就是Service的代理
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    // unbindService時調用
    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }
}

startService:onCreate——>onStartCommand
stopService:onDestory

注意:服務只會被創建一次,如果服務已經創建,並且沒有銷毀,多次調用startService方法,只會執行onStartCommand方法。

bindService:onCreate——>onBind
unbindService:onUnbind——>onDestory

注意:

如果多次bindService,onBind方法只會在第一次綁定時被調用;同樣,多次startService,onCreate方法也只會在第一次創建時被調用; 服務只能被解綁一次,服務需要先綁定才能解除綁定,多次解綁會報錯。 通過bindService方式啟動的Service,在調用unbindService時就會自動銷毀。 服務只會停止一次,多次調用stopService()的方法無效,但不報錯。 每次調用startService()開啟服務都會執行onStartCommand()方法。

如何調用Service裡的方法

由於系統框架在創建服務的時候會創建與之對應的上下文,直接new出來的服務對象是沒有上下文的,所以直接new服務對象調用其方法會報異常。

與Service之間交互都是通過其代理人(IBinder)來間接調用Service裡的方法的。

這裡寫圖片描述

這樣設計主要出於安全考慮,有限的暴露出一些方法,而不是直接返回服務對象,因為服務對象裡可能有一些成員變量和方法不允許外界直接訪問,需要保護起來。

一般IBinder(代理人)也應該設計成私有的,因為是IBinder中的一些數據也需要保護起來,只需要暴露出一些指定的方法,那麼外界如何引用IBinder對象呢?通過接口引用代理人,在接口定義供外界調用的方法,讓IBinder類實現該接口。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="bindservice與startservice">bindService與startService

bindService與startService的區別:

綁定服務:可以間接調用服務裡面的方法;如果綁定的Activity被銷毀了,服務也會跟著銷毀。 開啟服務:不可以調用服務裡面的方法;如果開啟服務的Activity銷毀,服務還可以長期的在後台運行。

既要保證服務長期在後台運行,又想去調用服務裡面的方法。

步驟:
1. startService(),保證服務在後台長期的運行;
2. bindService(),獲取中間人(IBinder對象),間接的調用服務裡面的方法;

這時,解綁服務並不會導致服務銷毀,服務可長期在後台運行。

注意:如果服務已經被綁定,直接調用stopService()是停不掉的,必須先解除綁定服務再調stopService(),服務才會被銷毀。

示例代碼

@ContentView(value = R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    private boolean mIsBound = false;
    private ServiceConnection mConnection = new ServiceConnection() {
        // 服務連接成功回調
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            LogUtil.d(name + " onServiceConnected");
            PayService.PayBinder binder = (PayService.PayBinder) service;
            binder.pay(100);
        }
        // 服務失去連接回調
        @Override
        public void onServiceDisconnected(ComponentName name) {
            LogUtil.d(name + " onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        x.view().inject(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //當Activity被銷毀時解綁服務,因為如果已經綁定服務不顯式解綁會報異常。
        onUnbindServiceClick(null);
    }

    private Intent getServiceIntent() {
        return new Intent(this, PayService.class);
    }

    // 啟動服務
    @Event(value = R.id.btn_start_service)
    private void onStartServiceClick(View view) {
        startService(getServiceIntent());
    }

    // 綁定服務
    @Event(value = R.id.btn_bind_service)
    private void onBindServiceClick(View view) {
        bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);// 綁定時如果沒有創建服務則自動創建Service。
        mIsBound = true;
    }

    // 解綁服務
    @Event(value = R.id.btn_unbind_service)
    private void onUnbindServiceClick(View view) {
        if (!mIsBound) {
            ToastUtil.show("未綁定服務");
            return;
        }
        try {
            unbindService(mConnection);//注意:ServiceConnection要傳綁定時的ServiceConnection對象,否則會報錯。
        } catch (Exception e) {
            e.printStackTrace();
        }
        mIsBound = false;
    }

    // 停止服務
    @Event(value = R.id.btn_stop_service)
    private void onStopServiceClick(View view) {
        stopService(getServiceIntent());
    }

}

參考文檔:
https://developer.android.com/reference/android/app/Service.html
https://developer.android.com/guide/components/services.html

使用AIDL實現進程間通信

AIDL(Android Interface Definition Language)用於進程間通信接口的定義,是一種進程間通訊的規范 。

Service端:

1.New一個aidl文件在src目錄下

 

這裡寫圖片描述

 

2.在aidl文件中定義Service中對外開放的接口

// IPayService.aidl
package linchaolong.android.aidldemo.service;

// Declare any non-default types here with import statements

/**
 * AIDL Demo
 *
 * Created by linchaolong on 2016/4/22.
 */
interface IPayService {

    void pay(int price);

    void startTimer();

    void stopTimer();

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     *
     * 翻譯:
     *
     * 展示一些可以在AIDL中用作參數和返回值的基本類型。
     *
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

aidl語言中沒有權限修飾符,因為進程間通信接口權限肯定是public的。

3.aidl編寫完成後,make一下工程,在build目錄下就會生成該aidl文件對應的java文件,比如我這是IPayService.java。
這裡寫圖片描述

4.在IPayService中有一個Stub靜態類,繼承了Binder和實現了IPayService接口,定義一個Binder類繼承IPayService.Stub並實現相關接口。

    @Override
    public IBinder onBind(Intent intent) {
        if (mBinder == null) {
            mBinder = new PayBinder(); 
        }
        return mBinder; // 其他應用綁定服務時返回binder對象
    }

    // Binder
    public class PayBinder extends IPayService.Stub {

        public void pay(int price) {
            PayService.this.pay(price);
        }

        public void startTimer() {
            PayService.this.startTimer();
        }

        public void stopTimer() throws RemoteException {
            PayService.this.stopTimer();
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            // Does nothing
        }
    }

5.在AndroidManifest.xml配置Service


    
        
    

到這裡Service端就完成了,其他應用需要調用該Service只需要把aidl文件拷貝到自己工程的src目錄下(make一下),並綁定服務即可得到IBinder對象,通過IBinder對象可以實現與Service的交互。

調用示例:

在onServiceConnected回調裡,調用YourServiceInterface.Stub.asInterface(service)把IBinder對象轉換為YourServiceInterface類型。

    private IPayService iPayService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 獲取遠程接口實例
            iPayService = IPayService.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "Service has unexpectedly disconnected");
            iPayService = null;
        }
    };

綁定遠程服務並調用IPC方法

    /**
     * 判斷是否已經綁定遠程服務
     *
     * @return
     */
    private boolean isBinded() {
        return mIsBound && iPayService != null;
    }

    private Intent getServiceIntent() {
        return new Intent("linchaolong.android.aidldemo.service.PayService");
    }

    // 綁定遠程服務
    @Event(value = R.id.btn_bind_remote_service)
    private void bindRemoteService(View view) {
        if (isBinded()) {
            showToast("已綁定遠程服務");
            return;
        }
        bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    // 調用遠程服務方法
    @Event(value = R.id.btn_call_service_pay)
    private void callServicePay(View view) {
        if (!isBinded()) {
            showToast("未綁定遠程服務");
            return;
        }
        try {
            // 通過IBinder對象調用遠程服務中方法
            iPayService.pay(100);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved