1. 程式人生 > >Android中的Service與程序間通訊(IPC)詳解

Android中的Service與程序間通訊(IPC)詳解

Service

什麼是Service

在後臺長期執行的沒有介面的元件。其他元件可以啟動Service讓他在後臺執行,或者繫結Service與它進行互動,甚至實現程序間通訊(IPC)。例如,可以讓服務在後臺處理網路互動,播放音樂,檔案I/O,或者與ContentProvider互動。

建立一個Service

  1. 新建一個類,繼承Service,重寫相關方法,如onBind,onUnBind,onCreate,onDestorey。
  2. 在AndroidManifest.xml中配置Service和相關許可權。
<manifest ... >
  ...
  <application ...
> <service android:name=".MyService" /> ... </application> </manifest>

開啟服務:

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類實現該介面。

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

}

使用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
    android:name=".service.PayService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="linchaolong.android.aidldemo.service.PayService" />
    </intent-filter>
</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();
        }
    }

相關推薦

AndroidService程序通訊IPC

Service 什麼是Service 在後臺長期執行的沒有介面的元件。其他元件可以啟動Service讓他在後臺執行,或者繫結Service與它進行互動,甚至實現程序間通訊(IPC)。例如,可以讓服務在後臺處理網路互動,播放音樂,檔案I/O,或者與Cont

【Linux】程序通訊IPC之訊號量測試用例

學習環境centos6.5 Linux核心2.6 程序間通訊概述 1. 程序通訊機制 一般情況下,系統中執行著大量的程序,而每個程序之間並不是相互獨立的,有些程序之間經常需要互相傳遞訊息。但是每個程序在系統中都有自己的地址空間,作業系統通過頁表

Android程序通訊IPC機制Binder簡要介紹和學習計劃

轉載:http://blog.csdn.net/luoshengyang/article/details/6618363/在Android系統中,每一個應用程式都是由一些Activity和Service組成的,這些Activity和Service有可能執行在同一個程序中,也有

【Linux】程序通訊IPC之共享記憶體測試用例

學習環境centos6.5 Linux核心2.6 什麼是共享記憶體 共享記憶體允許兩個或更多程序訪問同一塊記憶體。當一個程序改變了這塊記憶體中的內容的的時候,其他程序都會察覺到這個更改。 效率: 因為所有程序共享同一塊記憶體,共享記憶體在各種程序

Linux程序通訊IPC方式總結

程序間通訊概述 程序通訊的目的 資料傳輸  一個程序需要將它的資料傳送給另一個程序,傳送的資料量在一個位元組到幾M位元組之間 共享資料  多個程序想要操作共享資料,一個程序對共享資料 通知事件 一個程序需要向另一個或一組程序傳送訊息,通知它(它們)

linux程序通訊IPC小結

linux IPC型別 1、匿名管道 2、命名管道 3、訊號 4、訊息佇列 5、共享記憶體 6、訊號量 7、Socket 1、匿名管道 過程: 1、管道實質是一個核心緩衝區,先進先出(佇列)讀取緩衝區記憶體資料 2、一個數據只能讀一次,讀完後在緩衝區就不存在

深刻理解Linux程序通訊IPC(轉)

序linux下的程序通訊手段基本上是從Unix平臺上的程序通訊手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力 AT&T的貝爾實驗室及BSD(加州大學伯克利分校的伯克利軟體釋出中心)在程序間通訊方面的側重點有所不同。前者對Unix早期的程序間通訊手 段進行了系統的改進和擴充,形成了“system

程序通訊IPC介紹

程序間通訊(IPC,InterProcess Communication)是指在不同程序之間傳播或交換資訊。 IPC的方式通常有管道(包括無名管道和命名管道)、訊息佇列、訊號量、共享儲存、Socket、Streams等。其中 Socket和Streams支援不同主機上的兩個程序IPC。 以Linux中的C

程序通訊IPC——管道通訊

IPC(Inter-Process Communication)程序間通訊,提供了各種程序間通訊的方法。在Linux C程式設計中有幾種方法 (1) 半雙工Unix管道 (2) FIFOs(命

QT之程序程序通訊IPC

程序是作業系統的基礎之一。一個程序可以認為是一個正在執行的程式。我們可以把程序當做計算機執行時的一個基礎單位。關於程序的討論已經超出了本章的範疇,現在我們假定你是瞭解這個概念的。 在 Qt 中,我們使用QProcess來表示一個程序。這個類可以允許我們的應用程式開啟一個新的外部程式

程序通訊IPC——訊息佇列

IPC(Inter-Process Communication)程序間通訊,提供了各種程序間通訊的方法。在Linux C程式設計中有幾種方法 (1) 半雙工Unix管道 (2) FIFOs(命名管道) (3) 訊息佇列 (4) 訊號量 (5) 共

【Qt】Qt之程序通訊IPC【轉】

簡述 程序間通訊,就是在不同程序之間傳播或交換資訊。那麼不同程序之間存在著什麼雙方都可以訪問的介質呢?程序的使用者空間是互相獨立的,一般而言是不能互相訪問的,唯一的例外是共享記憶體區。但是,系統空間卻是“公共場所”,所以核心顯然可以提供這樣的條件。除此以外,那就是雙方都可以訪問的外設了。在這個意義上,兩

程序通訊IPC-管道、訊息佇列、共享記憶體、訊號、訊號量、套接字

多程序:首先,先來講一下fork之後,發生了什麼事情。由fork建立的新程序被稱為子程序(child process)。該函式被呼叫一次,但返回兩次。兩次返回的區別是子程序的返回值是0,而父程序的返回值則是新程序(子程序)的程序 id。將子程序id返回給父程序的理由是:因為一

Linux系統下-程序通訊訊息佇列-

Linux下程序間通訊方式: # 管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。 # 有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序

程序通訊IPC

    程序間通訊IPC,也就是Inter-Process Communication的縮寫。     首先我們明白一個程序其實就是一個狹義上的程式。     一個伺服器也就是一個程序。比如客戶端和伺服器的連線就是兩個程序在通訊,只是這兩個程序並不在同一臺計算機上,它們程序間

【Linux】程序通訊IPC之訊息佇列及測試用例

學習環境 Centos6.5 Linux 核心 2.6 什麼是訊息佇列? 訊息佇列是SystemV版本中三種程序通訊機制之一,另外兩種是訊號量和共享儲存段。訊息佇列提供了程序間傳送資料塊的方法,而且每個資料塊都有一個型別標識。訊息佇列是基於訊息的,而管

Linux程序11——程序通訊IPC概述

以下內容源於朱有鵬《物聯網大講堂》的課程學習整理,如有侵權,請告知刪除。 1、為什麼需要程序間通訊? (1)程序間通訊(IPC) 指的是2個任意程序之間的通訊。 (2)同一個程序在一個地址空間中 同一個程序的不同模組(不同函式、不同檔案)之間的通訊很簡單很多時候都是全

深刻理解Linux程序通訊IPC

序 linux下的程序通訊手段基本上是從Unix平臺上的程序通訊手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力AT&T的貝爾實驗室及BSD(加州大學伯克利分校的伯克利軟體釋出中心)在程序間通訊方面的側重點有所不同。前者對Unix早期的程序間通訊手段進行了

使用 AIDL 實現客戶端和服務的程序通訊IPC

1. 使用步驟 AIDL 的意思是 Android 介面定義語言。利用它來定義程序間通訊時雙方認可的程式設計介面。 第一步:建立 .aidl 檔案 AIDL 介面方法中支援的引數型別: 8 個基本資料型別 String CharSequence Lis

Andorid程序通訊IPC——Messenger

Messenger可以翻譯為信使,通過它可以在不同的程序中傳遞Message物件,在Message中放入我們需要傳遞的資料, 這樣就可以輕鬆的實現程序間通訊了 Android中的 IPC 方式 1.使用Bundle 2.使用檔案共享 3.使用Mess