Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 之 IPC 進程通信全解析

Android 之 IPC 進程通信全解析

編輯:關於Android編程

本篇博客的框架

這裡寫圖片描述

什麼是IPC

IPC(Inter-Process Communication) 進程間通信,是指兩個不同進程之間數據交換的過程。

在明確其之前,需要先搞懂幾個概念:

線程:CPU可調度的最小單位,是程序執行流的最小單元;線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的全部資源。 進程: 一個執行單元,在PC 和移動設備上一般指一個程序或者應用,一個進程可以包含多個線程。每一個進程都有它自己的地址空間,一般情況下,包括文本區域(text region)、數據區域(data region)和堆棧(stack region)。

在Android程序中,一般情況下一個程序就是一個進程(在無特別的代碼實現下),UI線程即主線程。如果有耗時操作,則會導致主線程堵死。而在Android中主線程負責UI,和用戶交互,如果堵死肯定影響用戶的使用度。所以Android要求不能再主線程中有耗時操作,這時就要將耗時操作放在子線程中。

IPC 的使用場景

程序因為自身原因,需要采用多進程模式來實現。
有些模塊由於特殊原因需要運行運行在單獨的進程中。 為了加大一個應用可使用的內存所以需要通過多進程來獲取內存空間。 當前應用需要向其他應用獲取數據。由於是兩個應用,即兩個進程。

在Android 中,每一個應用可使用的內存大小有限制,早起的一些版本在16M左右,不同的設備有不同的大小。可以通過多進程獲取多份內存空間。

Android中的多進程

如何開啟多進程

Android中開啟多進程只有一種方法,便是給四大組件(ActivityReceiverContentProviderService)指定android
:process
屬性,初次之外沒有其他方法。請注意,不能指定某一個線程或者實體類指定其所運行的進程

通過jni調用底層去開啟多進程也是一種方法,但屬於特殊情況,不進行考慮;

首先編寫三個Activity,並在清單文件中做如下注冊:

 
            
                

                
            
        
        

        

MainActivity不進行指定,則默認為當前進程。

SecondActivity指定屬性android:process=":remote"

ThirdActivity指定屬性android:process=".remote"

注意SencodActivityThirdActivity的進程參數不同。

把三個頁面都打開,通過DDMS可以看到三個進程的開啟

這裡寫圖片描述

啟動了三個進程:分別是

com.example.ipc:默認的程序進程。和包名相同。 com.example.ipc:remoteSecondActivity所在的進程。 .remoteThirdActivity所在的進程。

那麼2和3 ,進程名不同有什麼區別嗎;

如果進程名以:開始,表示是要在當前的進程名前附加上當前的包名,表示該進程是本應用的私有進程,其他應用不可以和其跑在同一個進程。 如果進程名不以:開始,表示不需附加包名信息,是一個完全的命名。同時該進程是全局進程,其他應用可以通過ShareUID和其跑在同一個進程中。

開啟多進程存在的問題

通過如上方式,很簡單的變開啟了多進程,但是,如果僅僅這樣的話,會有大問題。

看下面一個例子:

添加一個公有的類,添加靜態字段:

/**
 * 類似平常開發中的工具類等
 * @author MH
 *
 */
public class PublicContant {

    public static int m = 1;

}

MainActivity中Log一下並修改字段


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


        Log.i("info", PublicContant.m+"");

        PublicContant.m++;
    }

SecondActivity中Log一下:

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

        Log.i("info", PublicContant.m+"");      
    }

根據上面的邏輯,Log信息應該是1,和2 。但是呢,不是這樣的。

這裡寫圖片描述
兩個都是1,我靠。。先不問原因,看結果就知道是錯的。多進程這麼不靠譜,腫麼回事??

Android 為每一個進程都分配一個獨立的虛擬機,不同的虛擬機在內存分配上有不同的地址空間,這就導致在不同虛擬機中訪問同一個類對象會產生多個副本。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrbU09q1scewwLTLtaOsvfizzDxjb2RlPmNvbS5leGFtcGxlLmlwYzwvY29kZT66zTxjb2RlPmNvbS5leGFtcGxlLmlwYzpyZW1vdGU8L2NvZGU+try05tTa0ru49jxjb2RlPlB1YmxpY0NvbnRhbnQ8L2NvZGU+wOCjrLKix9LV4sG9uPbA4MrHz+C7pbK7uMnIxbXEo6zSu7j2vfizzNbQ0N64xMHLuMPWtbXEttTP86OsttTG5Mv7vfizzNbQtcS4w9a1sru74dTss8nIzrrO07DP7KGjPC9wPg0KPGJsb2NrcXVvdGU+DQoJPHA+1MvQ0NTazazSu7j2vfizzNbQtcTX6bz+ysfK9NPazazSu7j20OnE4rv6us3NrNK7uPY8Y29kZT5BcHBsaWNhdGlvbjwvY29kZT61xKGjzazA7aOs1MvQ0NTasrvNrL34s8zW0LXE1+m8/srHyvTT2sG9uPayu82stcTQ6cTiu/q6zTxjb2RlPkFwcGxpY2F0aW9uPC9jb2RlPrXEoaM8L3A+DQo8L2Jsb2NrcXVvdGU+DQo8cD64+b7dyOfJz8v5yvajrLbgvfizzMv51OyzybXEzsrM4rfWzqrI58/CvLi49re9w+ajujwvcD4NCr6yzKyzydSxus21pcD9xKPKvc3qyKvKp9CnPGJyIC8+DQrI58nPt9bO9qOstLS9qMHLsrvNrLXExNq05qOstuC49rbUz/OjrLWxyLu1pcD9yrLDtLXEtrzO3tCnwcuhoyDP37PMzayyvbv61sbN6siryqfQpzxiciAvPg0KsrvKx9K7v+nE2rTmx/jT8qOsz9+zzMv4tbHIu87e0KfBy6GjIDxjb2RlPlNoYXJlZFByZWZlcmVuY2U8L2NvZGU+tcS/yb+/0NTPwr21PGJyIC8+DQo8Y29kZT5zaGFyZWRQcmVmZXJlbmNlPC9jb2RlPrXEtdey48q1z9bKx82ouf22wdC0WE1MzsS8/qOswb249r34s8zIpbbB0LSjrLKit6LP1Mi7yse/ycTcs/bP1s7KzOK1xKGjIDxjb2RlPkFwcGxpY2F0aW9uPC9jb2RlPrvhtuC0zrS0vagNCjxoMyBpZD0="序列化和反序列化">序列化和反序列化

在了解多進程通信之前,我們需要了解兩個基礎的概念,序列化和反序列化。

序列化:將對象轉化為可保存的字節序列。(注意,是對象)。 反序列:將字節序列恢復為對象的過程。

序列化和反序列的用途:

以某種存儲形式使自定義對象序列化。 將對象從一個地方傳遞到另一個地方。 通過序列化在進程間傳遞對象。

Android中實現序列化的方式有兩種,SerializableParcelable

Serializable

Serializable是Java提供的一個序列化接口,他是一個空接口,是類實現該接口即可實現序列化。

/**
 * Serializable 序列化對象
 * @author MH
 *
 */
public class Book implements Serializable {
    /**
     *  序列化和反序列的關鍵
     */
    private static final long serialVersionUID = 1L;


    public int bookId;

    public String bookName;

}

在實現Serializable時候,編譯器會提示,讓我們添加serialVersionUID字段,該字段是一個關鍵的字段,後面會說。

相應的實現好了,那麼如何寫入和讀取呢?

寫入
public void writeSerializable() {
        try {
            // 構造對象
            Book book = new Book();
            // 構造序列化輸出字節流
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xxx.txt"));
            // 序列化對象
            oos.writeObject(book);
            // 關閉流
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
讀取
    public void readSerializable() {
        try {
            // 創建序列化讀取字節流
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                    "xxx.txt"));
            // 反序列化(讀取)對象
            Book book = (Book) ois.readObject();
            // 關閉流
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在序列化時,如果我們序列化對象之後,改變了我們的類結構(添加或改變字段),甚至是修改了字段的類型,修改了類名,那麼我們能反序列化成功嗎。

那麼關鍵就在於serialVersionUID字段。

如果我們不指定的話。在序列化時,會計算當前類結構的hash值並將該值賦給serialVersionUID,當反序列時,會比對該值是否相同,如果不相同,則無法序列化成功。

我們也可以手動指定,手動指定的好處是在類結構發生變化時,能夠最大程度的反序列,當然前提是只是刪除或添加了字段,如果是變量類型發生了變化,則依然無法反序列成功。

serialVersionUID 的工作機制:序列化時系統會把當前類的serialVersionUID寫入序列化文件中,當反序列化時候系統會去檢測文件中的serialVersionUID,看它是否和當前類的serialVersionUID一致,如果一致說明序列化類的版本和當前類的版本是相同的,這個時候可以成功反序列化,否則就說明當前類和序列化的類相比發生了某些變化。所以,我們最好指定serialVersionUID,避免他自定生成。

Parcelable

Parcelable是Android中特有的一種序列化方式,在intent傳值時,通常使用該方式。

該方式實現序列化,依然實現Parcelable,然後實現一些該接口的方法。

/**
 * Parcelable 對象的使用方式
 * @author MH
 *
 */
public class Book implements Parcelable {

    public int bookId;

    public String bookName;

    @Override
    public int describeContents() {
        // 返回當前對象的內容描述。幾乎所有情況下都是返回0
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 將當前對象寫入到序列化結構中       
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }


    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

        @Override
        public Book createFromParcel(Parcel source) {
            //從序列化後的對象中創建原始的值
            Book book = new Book();
            book.bookId = source.readInt();
            book.bookName = source.readString();

            return book;
        }

        @Override
        public Book[] newArray(int size) {
            //創建指定長度的原始對象數組
            return new Book[size];
        }
    };


}

Parcelable實現兩個方法,創建一個字段:

實現describeContents():返回當前對象的內容描述。幾乎所有情況下都是返回0。 實現public void writeToParcel(Parcel dest, int flags):// 將當前對象寫入到序列化結構中 構造Parcelable.Creator字段,該對象需要實現兩個方法:
public Book createFromParcel(Parcel source):從序列化後的對象中創建原始的值。 public Book[] newArray(int size):創建指定長度的原始對象數組

Serializable和Parcelable的比較

Serializable是Java中的序列化接口,其使用起來簡單但是開銷較大,序列化和反序列化需要大量的I/O操作。 Parcelable是Android中的序列化方式,更適用於Android的平台上,他的缺點是使用起來稍微麻煩,但是效率很高。 Parcelable適合進程間的通信,運行期。Serializable適合文件存儲即網絡傳輸。

Android 進程間通信的方式

使用Bundle 傳輸數據

Android中的四大組件中,其中有三大組件(Activity,Service,Receiver)都支持Intent中傳遞Bundle數據,如果看其源碼,會發現其也是實現了Parcelable接口,所以其能夠在不同進程中傳輸。

當然在傳輸的過程中,其所傳輸的數據必須支持序列化。比如基本數據類型,字符串,Parcelable的實現類,Serializable的實現類。由於該方法非常常用,不在多說。

文件共享

文件共享: 將對象序列化之後保存到文件中,在通過反序列,將對象從文件中讀取。

MainActvity中寫寫入對象


    /**
     * 寫入序列化對象
     */
    public void wirte() {
        Book book = new Book();
        book.bookId = 1;
        book.bookName = "si";
        try {

            // 構造序列化輸出字節流
            ObjectOutputStream oos = new ObjectOutputStream(
                    new FileOutputStream(PATH));
            // 序列化對象
            oos.writeObject(book);
            // 關閉流
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(book);

    }

SecondActivity中,讀取文件(反序列化)


public void read() {
        Book book = null;
        try {
            // 創建序列化讀取字節流
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                    MainActivity.PATH));
            // 反序列化(讀取)對象
            book = (Book) ois.readObject();
            // 關閉流
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(book);
    }

LOG 結果

06-28 09:20:47.916: com.example.ipc(進程名) I/System.out(12399): Book [bookId=1, bookName=si]
06-28 09:20:53.376: com.example.ipc:remote(進程名) I/System.out(12866): Book [bookId=1, bookName=si]

分屬不同的進程成功的獲取到了共享的數據。

通過共享文件這種方式來共享數據對文件的格式是沒有具體的要求的。比如可以是文件,也可以是Xml,JSON 等。只要讀寫雙方約定一定的格式即可。

同文件共享方式也存在著很大的局限性。即並發讀/ 寫的問題。讀/寫會造成數據不是最新。同寫很明顯會出現錯誤。

文件共享適合在對數據同步要求不高的進程之間進行通信。並且要妥善處理並發讀寫的問題。

SharedPreference 底層文件的方式。不適合在多進程中共享數據。

Messenger

Messenger 可以翻譯為信使,通過該對象,可以在不同的進程中傳遞Message對象。注意,兩個單詞不同。

下面就通過服務端(Service)和客戶端(Activity)的方式進行演示。

客戶端向服務端發送消息,可分為以下幾步。

服務端

創建Service 構造Handler對象,實現handlerMessage方法。 通過Handler對象構造Messenger信使對象。 通過ServiceonBind()返回信使中的Binder對象。

客戶端

創建Actvity 綁定服務 創建ServiceConnection,監聽綁定服務的回調。 通過onServiceConnected()方法的參數,構造客戶端Messenger對象 通過Messenger向服務端發送消息。

實現服務端


/**
 *  Messenger 的使用  服務端
 * @author MH
 *
 */
public class MessengerService extends Service {

    /**
     * 構建handler 對象
     */
    public static Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            // 接受客戶端發送的消息

            String msgClient = msg.getData().getString("msg");

            Log.i("messenger","接收到客戶端的消息--"+msgClient);

        };
    };


    // 通過handler 構建Mesenger 對象
    private final Messenger messenger = new Messenger(handler);

    @Override
    public IBinder onBind(Intent intent) {
        // 返回binder 對象
        return messenger.getBinder();
    }
}

注冊服務別忘了 ,同時對服務修改其進程。

實現客戶端

/**
 * Messenger 的使用 客戶端
 * 
 * @author MH
 * 
 */
public class MessengerActivity extends AppCompatActivity {

    /**
     * Messenger 對象
     */
    private Messenger mService;

    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // IBinder 對象
            // 通過服務端返回的Binder 對象 構造Messenger 
            mService = new Messenger(service);

            Log.i("messenger", "客戶端以獲取服務端Messenger對象");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

    };

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

        // 啟動服務
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);
    }

    /**
     *  布局文件中添加了一個按鈕,點擊該按鈕的處理方法
     * @param view
     */
    public void send(View view) {
        try {
            // 向服務端發送消息
            Message message = Message.obtain();

            Bundle data = new Bundle();

            data.putString("msg", "lalala");

            message.setData(data);
            // 發送消息
            mService.send(message);

            Log.i("messenger","向服務端發送了消息");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

看一下結果:

這裡寫圖片描述

注釋很清楚,不在多說,按照流程實現即可。 其中有一點需要注意:

我們是通過Message作為媒介去攜帶數據的。但是,Message的obj 並沒有實現序列化(實現SerializableParcelable),也就是其不能保存數據。必須使用message.setData()方法去傳入一個Bundle對象,Bundle中保存需要傳入的數據。

傳遞時使用的是Messenger.send(Message)方法。

服務端向客戶端發送了消息,那麼服務端向客戶端發送消息也類似:

關鍵點: 客戶端向服務端發送消息是,通過msg.replyTo將客戶端Messenger對象傳給服務端。

客戶端代碼進行修改:

創建客戶端HandlerMessenger對象。 修改send()方法。

/**
     * 構建handler 對象
     */
    public static Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            // 接受服務端發送的消息

            String msgService = msg.getData().getString("msg");

            Log.i("messenger","接收到服務端的消息--"+msgService);

        };
    };


    // 通過handler 構建Mesenger 對象
    private final Messenger messengerClient = new Messenger(handler);


    /**
     *  布局文件中添加了一個按鈕,點擊該按鈕的處理方法
     * @param view
     */
    public void send(View view) {
        try {
            // 向服務端發送消息
            Message message = Message.obtain();

            Bundle data = new Bundle();

            data.putString("msg", "lalala");

            message.setData(data);

            // ----- 傳入Messenger 對象
            message.replyTo = messengerClient;

            // 發送消息
            mService.send(message);

            Log.i("messenger","向服務端發送了消息");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

服務端代碼修改

/**
     * 構建handler 對象
     */
    public static Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            // 接受客戶端發送的消息

            String msgClient = msg.getData().getString("msg");

            Log.i("messenger", "接收到客戶端的消息--" + msgClient);

            // 獲取客戶端Messenger 對象

            Messenger messengetClient = msg.replyTo;

            // 向客戶端發送消息
            Message message = Message.obtain();

            Bundle data = new Bundle();

            data.putString("msg", "ccccc");

            message.setData(data);

            try {
                // 發送消息
                messengetClient.send(message);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        };
    };

結果不在演示了。

AIDL

AIDL是一種接口定義語言,用於約束兩個進程間的通訊規則,供編譯器生成代碼,實現Android設備上的兩個進程間通信(IPC)。

進程之間的通信信息,首先會被轉換成AIDL協議消息,然後發送給對方,對方收到AIDL協議消息後再轉換成相應的對象。

AIDL的關鍵便是Binder,關於Binder,後面的博客會分析。在這裡之將如何使用它。

因為需要服務端和客戶端共用aidl文件,所以最好單獨建一個包,適合拷貝到客戶端。

服務端:

添加如下包名:com.example.ipc.aidl 創建BookAidl.java,該對象需要作為傳輸。所以需要實現Parcelable
public class BookAidl implements Parcelable {

    public int bookId;

    public String bookName;

    public BookAidl() {
        super();

    }

    public BookAidl(int bookId, String bookName) {
        super();
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator CREATOR = new Creator() {

        @Override
        public BookAidl[] newArray(int size) {
            return new BookAidl[size];
        }

        @Override
        public BookAidl createFromParcel(Parcel source) {

            BookAidl book = new BookAidl();
            book.bookId = source.readInt();
            book.bookName = source.readString();
            return book;
        }
    };

    @Override
    public String toString() {
        return "BookAidl [bookId=" + bookId + ", bookName=" + bookName + "]";
    }

}

Parcelable在前面已經說過,不在多說。

創建.aidl文件。因為需要用到BookAidl對象,所以需要先聲明。

創建BookAidl.aidl文件,並手動添加

package com.example.ipc.aidl;

parcelable BookAidl;

創建IBookManager.aidl文件,接口文件,面向客戶端調用

package com.example.ipc.aidl;

import com.example.ipc.aidl.BookAidl;

interface IBookManager{
    List getBookList();
    void addBook(in BookAidl book);
}

寫完之後clean一下工程,之後會在gen目錄下生成對應的java文件。此java中的具體含義後面會解釋,在此不做多述。

繼續編寫服務端,創建Service類。
public class BookService extends Service {

    /**
     * 支持線程同步,因為其存在多個客戶端同時連接的情況
     */
    private CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();


    /**
     * 構造 aidl中聲明的接口的Stub對象,並實現所聲明的方法
     */
    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List getBookList() throws RemoteException {
            return list;
        }

        @Override
        public void addBook(BookAidl book) throws RemoteException {
            list.add(book);
            Log.i("aidl", "服務端添加了一本書"+book.toString());
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        //加點書
        list.add(new BookAidl(1, "java"));
        list.add(new BookAidl(2, "android"));

    }

    @Override
    public IBinder onBind(Intent intent) {
        // 返回給客戶端的Binder對象
        return mBinder;
    }
}

Service中,主要干了兩件事情:

實現aidl文件中的接口的Stub對象。並實現方法。 將Binder對象通過onBinder返回給客戶端。

為了省事,在這裡不在另起一個工程了,直接將Service在另一個進程中運行。


開始編寫客戶端

因為在同一個工程中,不需要拷貝aidl包中的文件。如果不在同一個工程,需要拷貝。


public class BookActivity extends AppCompatActivity{

    /**
     * 接口對象
     */
    private IBookManager mService;

    /**
     * 綁定服務的回調
     */
    private ServiceConnection conn = new ServiceConnection(){

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            // 獲取到書籍管理的對象
            mService = IBookManager.Stub.asInterface(service);

            Log.i("aidl", "連接到服務端,獲取IBookManager的對象");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_book);

        // 啟動服務
        Intent intent = new Intent(this,BookService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);

    }
    /**
     * 獲取服務端書籍列表
     * @param view
     */
    public void getBookList(View view){

        try {
            Log.i("aidl","客戶端查詢書籍"+mService.getBookList().toString());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /**
     * 添加書籍
     */
    public void add(View view){

        try {
            // 調用服務端添加書籍
            mService.addBook(new BookAidl(3,"ios"));
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

客戶端的代碼和之前的Messenger很類似:

綁定服務,監聽回調。 將回調中的IBinder service通過IBookManager.Stub.asInterface()轉化為借口對象。 調用借口對象的方法。

效果

這裡寫圖片描述

總結來說可分為如下幾步

服務端:

服務端創建.aidl文件和聲明接口 創建類,繼承Service,並實現onBind方法 在Service類中定義aidl中聲明接口的Stub對象,並實現aidl接口中聲明的方法 在onBind方法中返回Stub對象 在AndroidManifest.xml中注冊Service並聲明其Action

客戶端

使用服務端提供的aidl文件 在Activity定義aidl接口對象 定義ServiceConnection對象,監聽綁定服務的回調 回調中通過方法獲取借口對象

ContentProvider

作為android 四大組件之一,雖然用的地方不是太多。但是其確實是多進程通信的一種方式。例如,獲取通訊錄信息,這明顯跨應用了,肯定是多進程通信啊。

其底層實現和Messenger一樣,都是通過Binder,後面會專門分析Binder對象。

ContentProvider很多介紹,在這不在多提。

Socket實現

Socket也稱為“套接字”,是網絡通信中的概念,它分為流式套接字和用戶數據報套接字,分別對應於網絡傳輸中的傳輸控制層的TCP和UDP。

該方面使用的是JAVA 方面的知識。該舉例只是說明一個思路。不做細致的實現。

服務端

public class SocketService extends Service {

    /**
     * 連接的狀態
     */
    private boolean isConnState = true;


    @Override
    public void onCreate() {
        super.onCreate();

        // 啟動TCP 服務
        new Thread(new TCPServer()).start();
    }

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


    @Override
    public void onDestroy() {
        // 結束TCP 服務
        isConnState = false;
        super.onDestroy();
    }

    /**
     * 服務端TCP 服務,相當於服務器,接受Socket 連接
     * @author MH
     *
     */
    class TCPServer implements Runnable{

        @Override
        public void run() {

            try {
                // 監聽本地的12345 端口
                ServerSocket ss = new ServerSocket(12345);

                while(isConnState){

                    // 獲取客戶端的Socket 對象
                    Socket socket = ss.accept();

                    // 獲取輸入流  --- 
                    BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    // 通過輸入流讀取客戶端的消息
                    //String line = br.readLine();
                    // 輸出流
                    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    // 通過輸出流向客戶端發送消息
                    //bw.write("....");

                    // 關閉連接
                    socket.close();
                }


            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

服務啟動時,在onCreate方法中啟動了TCPServer,該線程時刻接受客戶端的請求。

客戶端

    public void conn(){
        try {
            // 指定ip和端口
            Socket s = new Socket("localhost", 12345);

            // ----- 和服務端類似
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            //String line = br.readLine();
            // 輸出流
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            //bw.write("....");
            // 關閉連接
            s.close();
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

關於Socket,在此只是一個簡單的示范,具體的使用博大精深。知道能實現即可。

Android 進程間通信不同方式的比較

Bundle:四大組件間的進程間通信方式,簡單易用,但傳輸的數據類型受限。 文件共享: 不適合高並發場景,並且無法做到進程間的及時通信。 Messenger: 數據通過Message傳輸,只能傳輸Bundle支持的類型 ContentProvider:android 系統提供的。簡單易用。但使用受限,只能根據特定規則訪問數據。 AIDL:功能強大,支持實時通信,但使用稍微復雜。 Socket:網絡數據交換的常用方式。不推薦使用。

TO DO

Binder 的細致分析

在實現多進程通信時,其中Messenger,ContentProvider,AIDL的底層實現都是Binder,很有必要對其進行繼續分析。

該博客中的代碼以共享到github。 https://github.com/AlexSmille/Android-IPC-Example

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