AIDL
AIDL是進程間通訊機制,是基于Binder驅(qū)動的一種Android 應用 ,進程與進程之間通訊的IPC 方式。眾所周知,運行在不同進程里的程序想要通訊那是比較困難的,一般采用Socket 建立兩者的聯(lián)系,還有類似共享內(nèi)存的方式去讀取共享的配置,而Binder 是基于這2種的一種優(yōu)化,是屬于比較輕量級并且linux驅(qū)動也有相應的支持。Android是基于linux開發(fā)的一套視窗可操作系統(tǒng),當然也有相應的驅(qū)動支持。
IPC通訊原理
Linux環(huán)境下,進程地址空間相互獨立,每個進程各自有不同的用戶地址空間。任何一個進程的全局變量在另一個進程中都看不到,所以進程和進程之間不能相互訪問,要交換數(shù)據(jù)bi必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進程1把數(shù)據(jù)從用戶空間放至內(nèi)核緩沖區(qū),進程2再從內(nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機制稱為進程間通信(IPC InterProcess Communication)
二、進程間通信的7種方式
第一類:傳統(tǒng)的Unix通信機制
1. 管道/匿名管道(pipe)
管道是半雙工的,數(shù)據(jù)只能向一個方向流動;需要雙方通信時,需要建立起兩個管道。
只能用于父子進程或者兄弟進程之間(具有親緣關系的進程);
單獨構成一種獨立的文件系統(tǒng):管道對于管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬于某種文件系統(tǒng),而是自立門戶,單獨構成一種文件系統(tǒng),并且只存在與內(nèi)存中。
-
數(shù)據(jù)的讀出和寫入:一個進程向管道中寫的內(nèi)容被管道另一端的進程讀出。寫入的內(nèi)容每次都添加在管道緩沖區(qū)的末尾,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)。
image
管道的實質(zhì):
管道的實質(zhì)是一個內(nèi)核緩沖區(qū),進程以先進先出的方式從緩沖區(qū)存取數(shù)據(jù),管道一端的進程順序的將數(shù)據(jù)寫入緩沖區(qū),另一端的進程則順序的讀出數(shù)據(jù)。
該緩沖區(qū)可以看做是一個循環(huán)隊列,讀和寫的位置都是自動增長的,不能隨意改變,一個數(shù)據(jù)只能被讀一次,讀出來以后在緩沖區(qū)就不復存在了。
當緩沖區(qū)讀空或者寫滿時,有一定的規(guī)則控制相應的讀進程或者寫進程進入等待隊列,當空的緩沖區(qū)有新數(shù)據(jù)寫入或者滿的緩沖區(qū)有數(shù)據(jù)讀出來時,就喚醒等待隊列中的進程繼續(xù)讀寫。
管道的局限:
管道的主要局限性正體現(xiàn)在它的特點上:
- 只支持單向數(shù)據(jù)流;
- 只能用于具有親緣關系的進程之間;
- 沒有名字;
- 管道的緩沖區(qū)是有限的(管道制存在于內(nèi)存中,在管道創(chuàng)建時,為緩沖區(qū)分配一個頁面大小);
- 管道所傳送的是無格式字節(jié)流,這就要求管道的讀出方和寫入方必須事先約定好數(shù)據(jù)的格式,比如多少字節(jié)算作一個消息(或命令、或記錄)等等;
2. 有名管道(FIFO)
匿名管道,由于沒有名字,只能用于親緣關系的進程間通信。為了克服這個缺點,提出了有名管道(FIFO)。
有名管道不同于匿名管道之處在于它提供了一個路徑名與之關聯(lián),以有名管道的文件形式存在于文件系統(tǒng)中,這樣,即使與有名管道的創(chuàng)建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過有名管道相互通信,因此,通過有名管道不相關的進程也能交換數(shù)據(jù)。值的注意的是,有名管道嚴格遵循先進先出(first in first out),對匿名管道及有名管道的讀總是從開始處返回數(shù)據(jù),對它們的寫則把數(shù)據(jù)添加到末尾。它們不支持諸如lseek()等文件定位操作。有名管道的名字存在于文件系統(tǒng)中,內(nèi)容存放在內(nèi)存中。
匿名管道和有名管道總結:
(1)管道是特殊類型的文件,在滿足先入先出的原則條件下可以進行讀寫,但不能進行定位讀寫。
(2)匿名管道是單向的,只能在有親緣關系的進程間通信;有名管道以磁盤文件的方式存在,可以實現(xiàn)本機任意兩個進程通信。
(3)無名管道阻塞問題:無名管道無需顯示打開,創(chuàng)建時直接返回文件描述符,在讀寫時需要確定對方的存在,否則將退出。如果當前進程向無名管道的一端寫數(shù)據(jù),必須確定另一端有某一進程。如果寫入無名管道的數(shù)據(jù)超過其最大值,寫操作將阻塞,如果管道中沒有數(shù)據(jù),讀操作將阻塞,如果管道發(fā)現(xiàn)另一端斷開,將自動退出。
(4)有名管道阻塞問題:有名管道在打開時需要確實對方的存在,否則將阻塞。即以讀方式打開某管道,在此之前必須一個進程以寫方式打開管道,否則阻塞。此外,可以以讀寫(O_RDWR)模式打開有名管道,即當前進程讀,當前進程寫,不會阻塞。
3. 信號(Signal)
- 信號是Linux系統(tǒng)中用于進程間互相通信或者操作的一種機制,信號可以在任何時候發(fā)給某一進程,而無需知道該進程的狀態(tài)。
- 如果該進程當前并未處于執(zhí)行狀態(tài),則該信號就有內(nèi)核保存起來,知道該進程回復執(zhí)行并傳遞給它為止。
- 如果一個信號被進程設置為阻塞,則該信號的傳遞被延遲,直到其阻塞被取消是才被傳遞給進程。
Linux系統(tǒng)中常用信號:
(1)SIGHUP:用戶從終端注銷,所有已啟動進程都將收到該進程。系統(tǒng)缺省狀態(tài)下對該信號的處理是終止進程。
(2)SIGINT:程序終止信號。程序運行過程中,按Ctrl+C鍵將產(chǎn)生該信號。
(3)SIGQUIT:程序退出信號。程序運行過程中,按Ctrl+\\鍵將產(chǎn)生該信號。
(4)SIGBUS和SIGSEGV:進程訪問非法地址。
(5)SIGFPE:運算中出現(xiàn)致命錯誤,如除零操作、數(shù)據(jù)溢出等。
(6)SIGKILL:用戶終止進程執(zhí)行信號。shell下執(zhí)行kill -9發(fā)送該信號。
(7)SIGTERM:結束進程信號。shell下執(zhí)行kill 進程pid發(fā)送該信號。
(8)SIGALRM:定時器信號。
(9)SIGCLD:子進程退出信號。如果其父進程沒有忽略該信號也沒有處理該信號,則子進程退出后將形成僵尸進程。
信號來源
信號是軟件層次上對中斷機制的一種模擬,是一種異步通信方式,,信號可以在用戶空間進程和內(nèi)核之間直接交互,內(nèi)核可以利用信號來通知用戶空間的進程發(fā)生了哪些系統(tǒng)事件,信號事件主要有兩個來源:
- 硬件來源:用戶按鍵輸入
Ctrl+C退出、硬件異常如無效的存儲訪問等。 - 軟件終止:終止進程信號、其他進程調(diào)用kill函數(shù)、軟件異常產(chǎn)生信號。
信號生命周期和處理流程
(1)信號被某個進程產(chǎn)生,并設置此信號傳遞的對象(一般為對應進程的pid),然后傳遞給操作系統(tǒng);
(2)操作系統(tǒng)根據(jù)接收進程的設置(是否阻塞)而選擇性的發(fā)送給接收者,如果接收者阻塞該信號(且該信號是可以阻塞的),操作系統(tǒng)將暫時保留該信號,而不傳遞,直到該進程解除了對此信號的阻塞(如果對應進程已經(jīng)退出,則丟棄此信號),如果對應進程沒有阻塞,操作系統(tǒng)將傳遞此信號。
(3)目的進程接收到此信號后,將根據(jù)當前進程對此信號設置的預處理方式,暫時終止當前代碼的執(zhí)行,保護上下文(主要包括臨時寄存器數(shù)據(jù),當前程序位置以及當前CPU的狀態(tài))、轉(zhuǎn)而執(zhí)行中斷服務程序,執(zhí)行完成后在回復到中斷的位置。當然,對于搶占式內(nèi)核,在中斷返回時還將引發(fā)新的調(diào)度。
調(diào)度。

4. 消息(Message)隊列
- 消息隊列是存放在內(nèi)核中的消息鏈表,每個消息隊列由消息隊列標識符表示。
- 與管道(無名管道:只存在于內(nèi)存中的文件;命名管道:存在于實際的磁盤介質(zhì)或者文件系統(tǒng))不同的是消息隊列存放在內(nèi)核中,只有在內(nèi)核重啟(即,操作系統(tǒng)重啟)或者顯示地刪除一個消息隊列時,該消息隊列才會被真正的刪除。
- 另外與管道不同的是,消息隊列在某個進程往一個隊列寫入消息之前,并不需要另外某個進程在該隊列上等待消息的到達。延伸閱讀:消息隊列C語言的實踐
消息隊列特點總結:
(1)消息隊列是消息的鏈表,具有特定的格式,存放在內(nèi)存中并由消息隊列標識符標識.
(2)消息隊列允許一個或多個進程向它寫入與讀取消息.
(3)管道和消息隊列的通信數(shù)據(jù)都是先進先出的原則。
(4)消息隊列可以實現(xiàn)消息的隨機查詢,消息不一定要以先進先出的次序讀取,也可以按消息的類型讀取.比FIFO更有優(yōu)勢。
(5)消息隊列克服了信號承載信息量少,管道只能承載無格式字 節(jié)流以及緩沖區(qū)大小受限等缺。
(6)目前主要有兩種類型的消息隊列:POSIX消息隊列以及System V消息隊列,系統(tǒng)V消息隊列目前被大量使用。系統(tǒng)V消息隊列是隨內(nèi)核持續(xù)的,只有在內(nèi)核重起或者人工刪除時,該消息隊列才會被刪除。
5. 共享內(nèi)存(share memory)
使得多個進程可以可以直接讀寫同一塊內(nèi)存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。
為了在多個進程間交換信息,內(nèi)核專門留出了一塊內(nèi)存區(qū),可以由需要訪問的進程將其映射到自己的私有地址空間。進程就可以直接讀寫這一塊內(nèi)存而不需要進行數(shù)據(jù)的拷貝,從而大大提高效率。
-
由于多個進程共享一段內(nèi)存,因此需要依靠某種同步機制(如信號量)來達到進程間的同步及互斥。
延伸閱讀:Linux支持的主要三種共享內(nèi)存方式:mmap()系統(tǒng)調(diào)用、Posix共享內(nèi)存,以及System V共享內(nèi)存實踐image
6. 信號量(semaphore)
信號量是一個計數(shù)器,用于多進程對共享數(shù)據(jù)的訪問,信號量的意圖在于進程間同步。
為了獲得共享資源,進程需要執(zhí)行下列操作:
(1)創(chuàng)建一個信號量:這要求調(diào)用者指定初始值,對于二值信號量來說,它通常是1,也可是0。
(2)等待一個信號量:該操作會測試這個信號量的值,如果小于0,就阻塞。也稱為P操作。
(3)掛出一個信號量:該操作將信號量的值加1,也稱為V操作。
為了正確地實現(xiàn)信號量,信號量值的測試及減1操作應當是原子操作。為此,信號量通常是在內(nèi)核中實現(xiàn)的。Linux環(huán)境中,有三種類型:Posix(可移植性操作系統(tǒng)接口)有名信號量(使用Posix IPC名字標識)、Posix基于內(nèi)存的信號量(存放在共享內(nèi)存區(qū)中)、System V信號量(在內(nèi)核中維護)。這三種信號量都可用于進程間或線程間的同步。



信號量與普通整型變量的區(qū)別:
(1)信號量是非負整型變量,除了初始化之外,它只能通過兩個標準原子操作:wait(semap) , signal(semap) ; 來進行訪問;
(2)操作也被成為PV原語(P來源于荷蘭語proberen"測試",V來源于荷蘭語verhogen"增加",P表示通過的意思,V表示釋放的意思),而普通整型變量則可以在任何語句塊中被訪問;
信號量與互斥量之間的區(qū)別:
(1)互斥量用于線程的互斥,信號量用于線程的同步。這是互斥量和信號量的根本區(qū)別,也就是互斥和同步之間的區(qū)別。
互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。
同步:是指在互斥的基礎上(大多數(shù)情況),通過其它機制實現(xiàn)訪問者對資源的有序訪問。
在大多數(shù)情況下,同步已經(jīng)實現(xiàn)了互斥,特別是所有寫入資源的情況必定是互斥的。少數(shù)情況是指可以允許多個訪問者同時訪問資源
(2)互斥量值只能為0/1,信號量值可以為非負整數(shù)。
也就是說,一個互斥量只能用于一個資源的互斥訪問,它不能實現(xiàn)多個資源的多線程互斥問題。信號量可以實現(xiàn)多個同類資源的多線程互斥和同步。當信號量為單值信號量是,也可以完成一個資源的互斥訪問。
(3)互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量可以由一個線程釋放,另一個線程得到。
7. 套接字(socket)
套接字是一種通信機制,憑借這種機制,客戶/服務器(即要進行通信的進程)系統(tǒng)的開發(fā)工作既可以在本地單機上進行,也可以跨網(wǎng)絡進行。也就是說它可以讓不在同一臺計算機但通過網(wǎng)絡連接計算機上的進程進行通信。

套接字是支持TCP/IP的網(wǎng)絡通信的基本操作單元,可以看做是不同主機之間的進程進行雙向通信的端點,簡單的說就是通信的兩方的一種約定,用套接字中的相關函數(shù)來完成通信過程。
套接字特性
套接字的特性由3個屬性確定,它們分別是:域、端口號、協(xié)議類型。
(1)套接字的域
它指定套接字通信中使用的網(wǎng)絡介質(zhì),最常見的套接字域有兩種:
一是AF_INET,它指的是Internet網(wǎng)絡。當客戶使用套接字進行跨網(wǎng)絡的連接時,它就需要用到服務器計算機的IP地址和端口來指定一臺聯(lián)網(wǎng)機器上的某個特定服務,所以在使用socket作為通信的終點,服務器應用程序必須在開始通信之前綁定一個端口,服務器在指定的端口等待客戶的連接。
另一個域AF_UNIX,表示UNIX文件系統(tǒng),它就是文件輸入/輸出,而它的地址就是文件名。
(2)套接字的端口號
每一個基于TCP/IP網(wǎng)絡通訊的程序(進程)都被賦予了唯一的端口和端口號,端口是一個信息緩沖區(qū),用于保留Socket中的輸入/輸出信息,端口號是一個16位無符號整數(shù),范圍是0-65535,以區(qū)別主機上的每一個程序(端口號就像房屋中的房間號),低于256的端口號保留給標準應用程序,比如pop3的端口號就是110,每一個套接字都組合進了IP地址、端口,這樣形成的整體就可以區(qū)別每一個套接字。
(3)套接字協(xié)議類型
因特網(wǎng)提供三種通信機制,
一是流套接字,流套接字在域中通過TCP/IP連接實現(xiàn),同時也是AF_UNIX中常用的套接字類型。流套接字提供的是一個有序、可靠、雙向字節(jié)流的連接,因此發(fā)送的數(shù)據(jù)可以確保不會丟失、重復或亂序到達,而且它還有一定的出錯后重新發(fā)送的機制。
二個是數(shù)據(jù)報套接字,它不需要建立連接和維持一個連接,它們在域中通常是通過UDP/IP協(xié)議實現(xiàn)的。它對可以發(fā)送的數(shù)據(jù)的長度有限制,數(shù)據(jù)報作為一個單獨的網(wǎng)絡消息被傳輸,它可能會丟失、復制或錯亂到達,UDP不是一個可靠的協(xié)議,但是它的速度比較高,因為它并一需要總是要建立和維持一個連接。
三是原始套接字,原始套接字允許對較低層次的協(xié)議直接訪問,比如IP、 ICMP協(xié)議,它常用于檢驗新的協(xié)議實現(xiàn),或者訪問現(xiàn)有服務中配置的新設備,因為RAW SOCKET可以自如地控制Windows下的多種協(xié)議,能夠?qū)W(wǎng)絡底層的傳輸機制進行控制,所以可以應用原始套接字來操縱網(wǎng)絡層和傳輸層應用。比如,我們可以通過RAW SOCKET來接收發(fā)向本機的ICMP、IGMP協(xié)議包,或者接收TCP/IP棧不能夠處理的IP包,也可以用來發(fā)送一些自定包頭或自定協(xié)議的IP包。網(wǎng)絡監(jiān)聽技術很大程度上依賴于SOCKET_RAW。
原始套接字與標準套接字的區(qū)別在于:
原始套接字可以讀寫內(nèi)核沒有處理的IP數(shù)據(jù)包,而流套接字只能讀取TCP協(xié)議的數(shù)據(jù),數(shù)據(jù)報套接字只能讀取UDP協(xié)議的數(shù)據(jù)。因此,如果要訪問其他協(xié)議發(fā)送數(shù)據(jù)必須使用原始套接字。
套接字通信的建立

** 服務器端**
(1)首先服務器應用程序用系統(tǒng)調(diào)用socket來創(chuàng)建一個套接字,它是系統(tǒng)分配給該服務器進程的類似文件描述符的資源,它不能與其他的進程共享。
(2)然后,服務器進程會給套接字起個名字,我們使用系統(tǒng)調(diào)用bind來給套接字命名。然后服務器進程就開始等待客戶連接到這個套接字。
(3)接下來,系統(tǒng)調(diào)用listen來創(chuàng)建一個隊列并將其用于存放來自客戶的進入連接。
(4)最后,服務器通過系統(tǒng)調(diào)用accept來接受客戶的連接。它會創(chuàng)建一個與原有的命名套接不同的新套接字,這個套接字只用于與這個特定客戶端進行通信,而命名套接字(即原先的套接字)則被保留下來繼續(xù)處理來自其他客戶的連接(建立客戶端和服務端的用于通信的流,進行通信)。
客戶端
(1)客戶應用程序首先調(diào)用socket來創(chuàng)建一個未命名的套接字,然后將服務器的命名套接字作為一個地址來調(diào)用connect與服務器建立連接。
(2)一旦連接建立,我們就可以像使用底層的文件描述符那樣用套接字來實現(xiàn)雙向數(shù)據(jù)的通信(通過流進行數(shù)據(jù)傳輸)。
如何實現(xiàn)兩者之間的通訊
實現(xiàn)的思路如下: 新建一個Aidl 文件,客戶端和服務端 必須一樣,然后點擊syc同步一下代碼,生成相應的接口文件,例如:
新增的aid 文件 如下:
package com.jimdear.kotlindemo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
// void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
// double aDouble, String aString);
String getName();
void setName(String name);
}
然后點擊生成相應的interface 如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/huihui/Documents/jimPJ/kotlindemo/app/src/main/aidl/com/jimdear/kotlindemo/IMyAidlInterface.aidl
*/
package com.jimdear.kotlindemo;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jimdear.kotlindemo.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.jimdear.kotlindemo.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.jimdear.kotlindemo.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.jimdear.kotlindemo.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jimdear.kotlindemo.IMyAidlInterface))) {
return ((com.jimdear.kotlindemo.IMyAidlInterface)iin);
}
return new com.jimdear.kotlindemo.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getName:
{
data.enforceInterface(descriptor);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_setName:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.jimdear.kotlindemo.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.lang.String getName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setName(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.lang.String getName() throws android.os.RemoteException;
public void setName(java.lang.String name) throws android.os.RemoteException;
}
此時通過APT ,AS就會把相應的邏輯處理代碼給補全進去,生成基于IBinder 的一套通訊體系,當然這一套你也可以自己寫。接下來,我們在服務端APP中,新建一個Service 來處理 相應的操作,代碼如下:
package com.jimdear.kotlinservice
import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.jimdear.kotlindemo.IMyAidlInterface
class MyService : Service() {
lateinit var name: String;
override fun onBind(p0: Intent?): IBinder? {
TODO("Not yet implemented")
return MyBinder();
}
class MyBinder : IMyAidlInterface.Stub() {
override fun getName(): String {
TODO("Not yet implemented")
return name;
}
override fun setName(name: String?) {
TODO("Not yet implemented")
if (name != null) {
this.name = name
};
}
}
}
然后在Activity 中對其進行啟動
package com.jimdear.kotlinservice
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// bindService(Intent(), serviceConnection, Context.BIND_AUTO_CREATE)
startService(Intent(this,MyService::class.java));
}
}
當然,你也要在AndroidManifest 對Service 進行注冊啦,別忘記啦。
做完這些步驟之后,我們回到客戶端,進行一個鏈接,代碼如下:
package com.jimdear.kotlindemo
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
class MainActivity : Activity() {
lateinit var aidl: IMyAidlInterface;
val serviceConnection = object : ServiceConnection {
override fun onServiceDisconnected(p0: ComponentName?) {
TODO("Not yet implemented")
}
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
TODO("Not yet implemented")
aidl=IMyAidlInterface.Stub.asInterface(p1)
aidl.setName("Test")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindService(Intent(), serviceConnection, Context.BIND_AUTO_CREATE);
}
}

