Android開發藝術探索讀書筆記——進程間通訊

1. 多進程使用場景

1) 應用某些模塊因爲特殊需求需要執行在單獨進程中。java

如消息推送,使消息推送進程與應用進程能單獨存活,消息推送進程不會因爲應用程序進程crash而受影響。
2) 爲加大一個應用可以使用的內存。需要多進程來獲取多分內存空間。android

2. 怎樣開啓多進程

給四大組件(Activity、Service、Receiver、ContentProvider)在AndroidMainfest中指定android:process屬性指定。數組

  • 假設進程以」:」開頭的進程。表明應用的私有進程,其它應用的組件不可以和它跑在同一個進程中。
  • 進程不以」:」開頭的進程屬於全局進程。其它應用可經過shareUID可以和它跑在同一個進程中。
  • 若兩個不一樣應用設置了一樣的shareUID,它們之間想共享數據,還需要有一樣的簽名才幹夠。

3. 多進程會形成的問題

(1) 靜態成員和單例失效,數據同步失敗;
Android會爲每一個應用/每一個進程分配一個獨立的虛擬機。不一樣虛擬機在內存分配上有不一樣的地址空間,這就致使不一樣虛擬機中訪問同一個類對象會產生多個副本。緩存


(2) 線程同步機制失效。
因爲不一樣進程不是同一塊內存,不一樣進程鎖的不是同一對象。
(3) SharedPreferences可靠性降低;
SharedPreferences底層是經過讀/寫XML文件實現的。併發寫可能會出問題。因此它不支持多個進程同一時候去執行寫操做。不然會致使必定概率的數據丟失。
(4) Application會建立屢次。
當一個組件跑在一個新進程中,系統會給它又一次分配獨立虛擬機,這事實上就是啓動一個應用的過程,故執行在不一樣進程中的組件屬於不一樣的虛擬機和不一樣的Application。服務器

4. 數據序列化

Intent和Binder數據傳輸時。或是對象持久化轉存/經過網絡傳輸給其它client時,需要使用Parcelable或Serializable將對象轉換成可以傳輸的形式。markdown


(1) Serializable接口
Serializable是Java提供的一個序列化接口,它是一個空接口。想要某個類實現序列化,僅僅需要對應類實現Serializable接口就能夠。Serializable是藉助ObjectOutputStream和ObjectInputStream實現對象的序列化和反序列化
通常在對應類實現Serializable類中還會定義一個final long型的serialVersionUID,不用它也能實現對象的序列化。它是用來輔助反序列化的。網絡

序列化時會把當前類的serialVersionUID寫入序列化文件裏。當反序列化系統會檢測文件裏的serialVersionUID,看它是否和當前類的serialVersionUID一致,假設一致說明序列化的類的版本號和當前類的版本號一樣,這時可反序化成功;不然說明當前類和序列化的類相比發生了變換,如添加或下降某個成員變量,這時沒法正常反序列化。
(2) Parcelable接口
在序列化過程當中需要實現的功能有:
1) 序列化:由writeToParcel方法完畢,終於經過Parcel中的一系列的write方法完畢。
2) 反序列化:由CREATOR完畢。內部標識了怎樣建立序列化對象和數組,終於經過Parcel的一系列read方法來完畢反序列化;
3) 內容描寫敘述符:由describeContents方法來完畢。差點兒所有狀況下該方法都返回0,僅噹噹前對象中存在文件描寫敘述符時返回1。
(3)二者差異
Serializable是Java中序列化接口,使用簡單但開銷大,序列和反序列化需要大量I/O操做。
Parcelable是Android中特有的序列化接口,使用複雜但開銷小,效率高,是Android推薦的序列化方法;
Parcelable主要用在內存序列化上,假設將對象序列化到存儲設備或將對象序列化後經過網絡傳輸。過程會比較複雜。建議使用Serializable。併發

5. Binder

Binder是什麼?socket

  • Binder是Android中的一個類,它繼承了IBinder接口;
  • 從IPC角度來講,Binder是Android中一種跨進程通訊的方式;
  • Binder還可以理解爲一種虛擬的物理設備,它的設備驅動是/dev/binder。
  • 從AndroidFramework角度來講,Binder是ServiceManager鏈接各類Manager(ActivityManager, WindowManager)和對應ManagerService的橋樑;
  • 從Android應用層來講。Binder是client和服務端進行通訊的媒介,當bindService時,服務端會返回一個包括了服務端業務的Binder對象,經過這個Binder對象,client就可以獲取服務端提供的服務或數據。這裏的服務包括普通服務和基於AIDL的服務。

6.android中跨進程通訊方式

6.1 結合Bundle使用Intent

在一個進程中啓動還有一個進程的Activity, Service, Receiver組件時,可以使用Bundle附加上需要傳遞的消息給遠程進程。並經過Intent發送出去。ide

6.2 使用文件共享

兩個進程經過讀/寫同一個文件來交換數據,還可以序列化一個對象到文件系統中。從還有一個進程中恢復這個對象。它的實現原理是經過ObjectOutputStream將文件寫入文件裏。再經過ObjectInputSteam將文件恢復。

經過文件共享的方式有必定侷限性。如併發讀/寫,讀出的內容可能不是最新的。併發寫就可能致使數據混亂。所以儘可能避免併發寫這樣的操做,或考慮用線程同步來限制多個線程的寫操做。文件共享適合在對數據要求不高的進程之間通訊。


SharedPreferences是Android提供的一種輕量級存儲方案,它經過鍵值對方式存儲數據,底層它是採用XML來存儲鍵值對。

因爲系統對SharedPreferences的讀寫有必定的緩存策略。即在內存中會有一份SharedPreferences文件的緩存,在多進程模式下,系統對它的讀/寫就變得不可靠,當面對高併發讀/寫訪問時。SharedPreferences會有很是大概率會丟失數據。所以不建議在進程間通訊中使用SharedPreferences。

6.3 使用Messenger

Messenager可以在不一樣進程中傳遞Message對象。在Message中放入咱們要傳遞的數據。

它的底層實現是AIDL,如下是Messenger的兩個構造方法:

public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

不管是IMessenger仍是Stub.asInterface,這樣的用法都代表它的底層是AIDL,它一次僅僅處理一個請求,不存在線程同步問題。
實現步驟:
(1) 服務端進程
1) 定義一個Service用於client的綁定,創建一個Handler,在handleMessage裏處理client發送過來的消息。

//用ServiceHandler接收並處理來自於client的消息
    private class ServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == RECEIVE_MESSAGE_CODE){
                Bundle data = msg.getData();
                if(data != null){
                    String str = data.getString("msg");
                }
                //經過Message的replyTo獲取到client自身的Messenger,
                //Service可以經過它向client發送消息
                clientMessenger = msg.replyTo;
                if(clientMessenger != null){
                    Message msgToClient = Message.obtain();
                    msgToClient.what = SEND_MESSAGE_CODE;
                    //可以經過Bundle發送跨進程的信息
                    Bundle bundle = new Bundle();
                    bundle.putString("msg", "你好,client,我是MyService");
                    msgToClient.setData(bundle);
                    try{
                        clientMessenger.send(msgToClient);
                    }catch (RemoteException e){
                        e.printStackTrace();
                        Log.e("DemoLog", "向client發送信息失敗: " + e.getMessage());
                    }
                }
            }
        }
    }

2) 經過Handler建立一個Messenger對象

//serviceMessenger是Service自身的Messenger,其內部指向了ServiceHandler的實例
    //client可以經過IBinder構建Service端的Messenger,從而向Service發送消息,
    //並由ServiceHandler接收並處理來自於client的消息
    private Messenger serviceMessenger = new Messenger(new ServiceHandler());

3) 在Service的onBind中經過Messenger.getBinder()返回底層的Binder對象。

@Override
    public IBinder onBind(Intent intent) {
        Log.i("DemoLog", "MyServivce -> onBind");
        //獲取Service自身Messenger所對應的IBinder。並將其發送共享給所有client
        return serviceMessenger.getBinder();
    }

4) 註冊服務,讓其執行在單獨進程中

<service
    android:name=".MyService"
    android:enabled="true"
    android:process=":remote" >
</service>

(2) client進程
1) 綁定服務端的Service

private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            //client與Service創建鏈接
            Log.i("DemoLog", "client onServiceConnected");

            //咱們可以經過從Service的onBind方法中返回的IBinder初始化一個指向Service端的Messenger
            serviceMessenger = new Messenger(binder);
            isBound = true;

            Message msg = Message.obtain();
            msg.what = SEND_MESSAGE_CODE;

            //此處跨進程Message通訊不能將msg.obj設置爲non-Parcelable的對象,應該使用Bundle
            //msg.obj = "你好。MyService,我是client";
            Bundle data = new Bundle();
            data.putString("msg", "你好,MyService,我是client");
            msg.setData(data);

            //需要將Message的replyTo設置爲client的clientMessenger,
            //以便Service可以經過它向client發送消息
            msg.replyTo = clientMessenger;
            try {
                Log.i("DemoLog", "client向service發送信息");
                serviceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
                Log.i("DemoLog", "client向service發送消息失敗: " + e.getMessage());
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //client與Service失去鏈接
            serviceMessenger = null;
            isBound = false;
            Log.i("DemoLog", "client onServiceDisconnected");
        }
    };

    Intent intent = new Intent();            
    ComponentName componentName = new ComponentName(packageName, serviceNmae);
    intent.setComponent(componentName);
    try{
        Log.i("DemoLog", "client調用bindService方法");
        bindService(intent, conn, BIND_AUTO_CREATE);
    }catch(Exception e){
        e.printStackTrace();
        Log.e("DemoLog", e.getMessage());
    }
}

綁定成功後用服務端返回的IBinder對象建立一個Messenger,經過它向服務端發送Message消息。


假設需要服務端給client發送消息。需要在Handler的handleMessage方法裏,依據client發送過來的Message.replyTo獲取到client的Messenger對象,就可以向client發送消息了。同一時候client需要在服務鏈接的onServiceConnected方法中,將client的Messenger對象經過Message.replyTo給收到服務端發送過來的Message對象。這樣就實現兩方通訊。

6.4 AIDL

6.4.1 在服務端新建需要的AIDL類

(1)先新建一個Book.java實現Parcelable接口,表示一個圖書的信息類;

import android.os.Parcel;
import android.os.Parcelable;

/** * Book.java */
public class Book implements Parcelable{
    private String name;
    private int price;
    public Book(){}
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

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

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

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

    /** * 參數是一個Parcel,用它來存儲與數據傳輸 * @param dest */
    public void readFromParcel(Parcel dest) {
        //注意。此處的讀值順序應當是和writeToParcel()方法中一致的
        name = dest.readString();
        price = dest.readInt();
    }

    //方便打印數據
    @Override
    public String toString() {
        return "name : " + name + " , price : " + price;
    }
}

(2) 新建一個Book.aidl。表示Book類在AIDL中的聲明;

// Book.aidl
//這個文件的做用是引入了一個序列化對象 Book 供其它的AIDL文件使用
//注意:Book.aidl與Book.java的包名應當是同樣的
//注意parcelable是小寫
parcelable Book;

(3) 新建一個IBookManger.aidl接口,裏面包括對應的方法;

// IBookManger.aidl
//導入所需要使用的非默認支持數據類型的包
import com.lypeer.ipcclient.Book;

interface IBookManger {

    //所有的返回值前都不需要加不論什麼東西。不管是什麼數據類型
    List<Book> getBooks();
    Book getBook();
}

該方法保存後,會在gen文件夾下生成一個IBookManger.java類,它是系統爲BookManger.aidl生成的Binder類,繼承android.os.IInterface。它本身也仍是個接口。
它包括如下內容:
1) 定義了一個String型的DESCRIPTOR。Binder的惟一標識;
2) asInterface(android.os.IBinder obj)方法,將服務端的Binder對象轉換成client所需的AIDL接口類型對象,當client和服務端處於同一進程,直接返回服務端的Stub對象自己。不然返回系統封裝後的Stub.proxy對象。


3) asBinder方法:返回當前Binder對象。
4) onTransact方法:執行在服務端的Binder線程池中,當client發起遠程請求,遠程請求會經過系統封裝後交由此方法處理,該方法原型爲public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服務端經過code可以肯定client請求的是哪個方法,從data中取出目標方法所需的參數,執行對應方法後,將返回值寫入reply中。假設此方法返回false,client會請求失敗。
5) IBookManger.aidl接口中聲明的方法的代理實現,此方法執行在client,當client調用此方法時,首先需要建立此方法需要的輸入類型Parcel對象_data, 輸出類型Parcel對象_reply和返回值對象。接着將參數信息寫入_data中(如有參數的話),再調用transact方法發起RPC(遠程過程調用)。當前線程掛起,服務端的onTransact方法會被調用,直到RPC過程返回後,當前線程繼續執行。並從_reply中取出RPC過程的返回結果;最後返回_reply中的數據。
6) 聲明瞭IBookManger.aidl中聲明的方法。
7) 聲明瞭對應的整型id分別用於標識聲明的方法。該id用於標識在transact過程當中推斷client請求的到底是哪一個方法。
8) 聲明瞭一個類部類Stub,繼承android.os.Binder實現IBookManager.aidl中類接口。當client和服務端處於同一進程,方法調用不會走跨進程的transact過程。若位於不一樣進程。則由Stub的內部代理Proxy,調用跨進程transact過程。


需要注意的是:client發起請求時。當前線程會掛起直至服務端返回數據,若方法是一個耗時操做,不能在UI線程中發起此遠程請求。服務端的Binder方法執行在Binder線程池中。因此不管操做是否耗時,都應採用同步的方法去實現。
(4)linkToDeath unlinkToDeath
因爲Binder執行在服務端進程中。假設服務端進程因爲某種緣由終止。這時client服務端的Binder鏈接斷裂,會致使遠程調用失敗。但client很是可能不知道,解決此問題的辦法,利用Binder提供的兩個配對方法linkToDeath和unlinkToDeath,經過linkToDeath可以給Binder設置一個死亡代理,當Binder被終止時。咱們會接收到通知。這時可經過又一次發起請求恢復鏈接。
實現方法:
1) 先聲明一個DeathRecipient對象,它是一個接口。內部僅僅有一個方法binderDied,咱們需要實現該方法,當Binder死亡時,系統回調binderDied方法,咱們可以移出以前binder代理並又一次創建遠程服務。

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mBookManager == null) {
                return;
            }
            mBookManger.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mBookManger = null;
            //又一次綁定遠程服務
        }
    };

2) 在client綁定遠程服務成功後,給binder設置死亡代理;

mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);

6.4.2 遠程服務端Serivce的實現

(1)Serivce創建
咱們需要新建一個Service,稱爲AIDLService,代碼例如如下:

/** 1. 服務端的AIDLService.java */
public class AIDLService extends Service {

    public final String TAG = this.getClass().getSimpleName();

    //包括Book對象的list
    private List<Book> mBooks = new ArrayList<>();

    //由AIDL文件生成的IBookManager
    private final IBookManager.Stub mBookManager = new IBookManager.Stub() {
        @Override
        public List<Book> getBooks() throws RemoteException {
            synchronized (this) {
                Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString());
                if (mBooks != null) {
                    return mBooks;
                }
                return new ArrayList<>();
            }
        }


        @Override
        public void addBook(Book book) throws RemoteException {
            synchronized (this) {
                if (mBooks == null) {
                    mBooks = new ArrayList<>();
                }
                if (book == null) {
                    Log.e(TAG, "Book is null in In");
                    book = new Book();
                }
                //嘗試改動book的參數,主要是爲了觀察其到client的反饋
                book.setPrice(2333);
                if (!mBooks.contains(book)) {
                    mBooks.add(book);
                }
                //打印mBooks列表,觀察client傳過來的值
                Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());
            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setName("Android開發藝術探索");
        book.setPrice(28);
        mBooks.add(book);   
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));
        return mBookManager;//在onBinder中返回服務端的Binder對象
    }
}

(2)註冊服務

<service android:name="aidl.AIDLService"
    android:process=":remote">
</service>

6.4.3 client的實現

/** 2. client的AIDLActivity.java */
public class AIDLActivity extends AppCompatActivity {

    //由AIDL文件生成的Java類
    private IBookManager mBookManager = null;

    //標誌當前與服務端鏈接情況的布爾值,false爲未鏈接。true爲鏈接中
    private boolean mBound = false;

    //包括Book對象的list
    private List<Book> mBooks;

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

    /** * button的點擊事件。點擊以後調用服務端的addBookIn方法 * * @param view */
    public void addBook(View view) {
        //假設與服務端的鏈接處於未鏈接狀態,則嘗試鏈接
        if (!mBound) {
            attemptToBindService();
            Toast.makeText(this, "當前與服務端處於未鏈接狀態,正在嘗試重連,請稍後再試", Toast.LENGTH_SHORT).show();
            return;
        }
        if (mBookManager == null) return;

        Book book = new Book();
        book.setName("APP研發錄In");
        book.setPrice(30);
        try {
            mBookManager.addBook(book);//經過服務端的Binder對象調服務端方法
            Log.e(getLocalClassName(), book.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    /** * 嘗試與服務端創建鏈接 */
    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("aidl.AIDLService");
        intent.setPackage("com.xx.packagename");
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);//綁定服務
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!mBound) {
            attemptToBindService();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(getLocalClassName(), "service connected");
            mBookManager = IBookManager.Stub.asInterface(service);
            mBound = true;

            if (mBookManager != null) {
                try {
                    mBooks = mBookManager.getBooks();//調用服務端的getBooks方法
                    Log.e(getLocalClassName(), mBooks.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(getLocalClassName(), "service disconnected");
            mBound = false;
        }
    };
}

經過獲取服務端的Binder對象,就可以和服務端進行通訊了。

6.4.4 其它知識點

(1)跨進程listener接口
假設在服務端定聲明瞭接口,client進行註冊和反註冊。會拋出異常,沒法反註冊。因爲在多進程中,Binder會把client傳過來的對象又一次轉化成一個新對象,這樣儘管client註冊和反註冊使用的是同一個對象,但是經過Binder傳遞到服務端卻變成兩個對象。對象的跨進程傳輸本質都是反序列化過程。
可以使用RemoteCallbackList,它是系統專門用來刪除跨進程listener接口。它實現原理利用底層Binder對象是同一個,僅僅要遍歷服務端所有listener,找出和解註冊listener具備一樣Binder對象的服務端listener,並把它刪除掉。

同一時候RemoteCallbackList還有一個做用,當client進程終止。它會移除client所註冊的所有listener.
(2)權限驗證
在註冊服務時加入permission權限驗證,或是在onTransact方法中進行權限驗證;
(3) AIDL訪問流程總結:
先建立一個Service和一個AIDL接口。再建立一個類繼承自AIDL接口中的Stub類並實現Stub中的抽象方法;在Service的onBind方法中返回這個對象,再在client就可以綁定服務端service。創建鏈接後就可以訪問遠程服務端的方法了。

6.5 ContentProvider

和Messenger同樣,ContentProvider底層一樣是Binder.系統預置了很是多ContentProvider,如通信錄、日程表等。僅僅需經過ContentResolver的query/update/insert/delete就可以跨進程訪問這些信息。詳細實現過程例如如下:
1) 新建一個繼承自系統ContentProvider的Provider,並重寫onCreate, query, getType, insert, delete, update六個方法。


這六個進程執行在ContentProvider的進程中,除了onCreate由系統回調執行在主線程中,其它五個方法執行在Binder線程池中。


2) 註冊Provider

<provider android:name=".provider.XXProvider"
        android:authorities="com.xx.xx.provider"
        android:permission="com.xx.PROVIDER"
        android:process=":provider">
</provider>

3) 在還有一個進程中訪問咱們定義的ContentProvider

Uri uri = Uri.parse("content://com.xx.xx.provider");
getContentResolver().query(uri, null, null, null, null);
getContentResolver().query(uri,)

4) ContentProvider數據源發生變化時。可經過ContentResolver的notifyChange方法來通知外界數據發生改變。外界可經過ContentResolver的registerContentObserver方法來註冊觀察者。經過unregisterContentObserver方法來解除觀察者。

6.6 Socket

Socket分爲流式套接字和用戶數據報套接字。各自是對應於網絡的傳輸控制層中的TCP/UDP
TCP:面向鏈接的協議,穩定的雙向通訊功能,鏈接需要「三次握手」。提供了超時重傳機制,具備很是高的穩定性;
UDP:面向無鏈接協議。不穩定的單向通訊功能,也可提供雙向通訊功能。效率高,不能保證數據必定能正確傳輸。


實現步驟:
1)服務端在新建一個Service,並創建TCP服務

@Override
    public void onCreate() {
        new Thread(new TcpServer()).start();//堵塞監聽client鏈接
        super.onCreate();
    }

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

    private class TcpServer implements Runnable {
        @Override
        public void run() {
            ServerSocket serverSocket;
            try {
                //監聽8688端口
                serverSocket = new ServerSocket(8688);
            } catch (IOException e) {
                return;
            }
            while (!isServiceDestroyed) {
                try {
                    // 接受client請求。並且堵塞直到接收到消息
                    final Socket client = serverSocket.accept();
                    new Thread() {
                        @Override
                        public void run() {
                            try {
                                //有消息接收到。新開一個線程進行處理消息
                                responseClient(client);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void responseClient(Socket client) throws IOException {
        // 用於接收client消息,將client的二進制數據流格式轉成文本格式
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        // 用於向client發送消息
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
        out.println("您好。我是服務端");
        while (!isServiceDestroyed) {
            String str = in.readLine();//經過數據流的readLine。可直接變成文本格式
            Log.i("moon", "收到client發來的信息" + str);
            if (TextUtils.isEmpty(str)) {
                //client斷開了鏈接
                Log.i("moon", "client斷開鏈接");
                break;
            }
            String message = "收到了client的信息爲:" + str;
            // 從client收到的消息加工再發送給client
            out.println(message);
        }
        out.close();//關閉流
        in.close();
        client.close();
    }

2)註冊服務

<service
    android:name=".SocketServerService"
    android:process=":remote" />

3) client先創建鏈接服務端socket

Socket socket = null;
while (socket == null) {
    try {
        //選擇和服務器一樣的端口8688
        socket = new Socket("localhost", 8688);
        mClientSocket = socket;
        mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
    } catch (IOException e) {
        SystemClock.sleep(1000);
    }
}

4) client和服務端通訊。經過while循環不斷去讀取服務器發送過來的消息

// 接收服務器端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()) {
    final String msg = br.readLine();
    if (msg != null) {
        runOnUiThread(new Runnable() {
                          @Override
                          public void run() {
                              tv_message.setText(tv_message.getText() + "\n" + "服務端:" + msg);
                          }
                      }
        );
    }
}

5) client在onCreate中單開線程啓動鏈接服務

Intent service = new Intent(this, SocketServerService.class);
        startService(service);
        new Thread() {
            @Override
            public void run() {
                connectSocketServer();
            }
        }.start();

6.7 進程間通訊方式比較

這裏寫圖片描寫敘述

相關文章
相關標籤/搜索