国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

IPC機制

2019-11-08 00:36:25
字體:
來源:轉載
供稿:網友

簡介

    ipC是 Inter-PRocess Communication的縮寫,含義為進程間通信或者跨進程通信,是指兩個進程之間進行數據交換的過程。    進程和線程是包含與被包含的關系。

Android中的多進程模式

1. 在Android中創建多進程的方法:        ① 通過JNI在native層去fork一個新的進程(不常用)        ② 給四大組件添加屬性 android:process = "進程名" 就可以開啟多進程模式,如果沒有指定這個屬性,那么默認進程名是包名    2. 給進程命名:        ① android:process = ": 名字" ,這是一種簡寫的方法,完整的進程名為 "包名+名字";是屬于當前應用的私有進程,其他應用的組件不可以和它跑到同一個進程中

<service    android:name=".services.BookManagerService"    android:enabled="true"    android:exported="true"    android:process=":myaidl" />        

        ② android:process = "全名" ,屬于全局進程,其它的應用通過ShareUID方法可以和它跑到同一個進程中。    

3. 一般來說,使用多進程會造成如下幾方面的問題:        ① 靜態成員和單例模式完全失效:                Android為每個進程都會分配一個獨立的虛擬機,這導致在不同的虛擬機中訪問同一個類的對象會產生多個副本        ② 線程同步機制完全失效                Android為每個進程都會分配一個獨立的虛擬機(內存)        ③ SharedPreferences的可靠性下降                底層是通過xml文件來實現的,并發讀寫都可能出問題        ④ application會創建多次                運行在不同進程的組件是屬于不同的虛擬機和Application的

Android中的序列化機制

        Serializable是java中的序列化接口,其使用起來簡單但是開銷很大,序列化、反序列化過程都需要大量的I/O操作        Parceable是Adnroid中的序列化接口,效率很高,就是使用起來麻煩一點

一、Serializable接口:

    ① 為該類指定 private static final long serialVersionUID = 465465464l;        作用:當版本升級后,可能刪除或者添加了某個成員變量,這個時候我們仍然能夠反序列化成功,程序能夠最大限度的恢復數據,相反,如果不指定serialVersionUID,              程序則會掛掉。    ② 采用ObjectOutputStream、ObjectInputStream實現序列化和反序列化操作:

File file = new File(Environment.getExternalStorageDirectory() , "test.text");//序列化Userss usersS = new UsersS(20 , "xiaoming" , true);try {    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));    out.writeObject(usersS);    out.close();} catch (IOException e) {    e.printStackTrace();}//反序列化try {    ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));    UsersS usersS1 = (UsersS) in.readObject();    in.close();    LogUtils.e("反序列化得到的名字是:" + usersS1.getName());} catch (IOException e) {    e.printStackTrace();} catch (ClassNotFoundException e) {    e.printStackTrace();}

二、Parceable接口:

    ① 實現這個接口可以實現序列化,并可以通過Intent和Binder傳遞:

//返回當前對象的描述(一般返回0)@Overridepublic int describeContents() {    return 0;}//將當前對象寫入序列化結構中@Overridepublic void writeToParcel(Parcel dest, int flags) {    dest.writeInt(age);    dest.writeString(name);    dest.writeInt(isMale ? 1 : 0);    dest.writeParcelable(usersS , 0);//users另外是一個序列化的類的對象;flag一般是0}public static final Creator<UsersP> CREATOR = new Creator<UsersP>() {    //從序列化后的對象中創建原始對象    @Override    public UsersP createFromParcel(Parcel in) {        return new UsersP(in);    }    //創建指定長度的原始對象數組    @Override    public UsersP[] newArray(int size) {        return new UsersP[size];    }};protected UsersP(Parcel in) {    age = in.readInt();    name = in.readString();    isMale = in.readInt() == 1;    in.readParcelableArray(Thread.currentThread().getContextClassLoader());//反序列化過程需要傳入當前線程的上下文類加載器}

 Bundle

        可以在Bundle上附加需要傳輸的信息并通過Intent發送出去    Intent intent = new Intent();    Bundle bundle = new Bundle();    intent.putExtra("1" , bundle);

 文件共享

        適合在對數據同步要求不高的進程之間進行通信,并且要妥善處理并發讀、寫的問題    例子:在兩個進程中分別進行序列化和反序列化一個java類,存在問題:               一個序列化、一個反序列化,那么反序列化出來的可能不是最新的;               如果兩個同時進行序列化就有可能出現更嚴重的問題;

Messenger(翻譯為“信使”,對AIDL做了封裝)

        ① 通過它可以在不同的進程中傳遞Message對象,在Message中可以放入我們需要傳遞的數據        ② 兩個構造函數表明它的底層實現是AIDL:            public Messenger(Handler handler){                mTarget = target.getIMessenger();            }            public Messenger(IBinder target){                mTarget = IMessenger.Stub.asInterface(target);            }        ③ 一次只處理一個請求,在服務端不需要考慮線程同步的問題。        ④ Messenger是串行的方式處理客戶端發來的消息,如果大量的消息同時發送到服務端,服務端仍然只能一個個處理,如果有大量的并發請求,            那么Messenger就不太合適了。        ⑤ Messenger的主要作用是為了傳遞消息,很多時候我們很可能需要跨進程調用服務端的方法,這種情形Messenger就無法做到了,aidl可以。

下面通過一個例子來加深理解:

客戶端通過bindService()來啟動服務,然后根據返回的IBinder對象得到Messenger對象:

private ServiceConnection conn = new ServiceConnection(){    @Override    public void onServiceConnected(ComponentName name, IBinder service) {        //通過返回的IBinder對象,得到一個Messenger對象        Messenger messenger = new Messenger(service);        Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);        Bundle bundle = new Bundle();        bundle.putString("msg" , "this is test");        msg.setData(bundle);        //將客戶端的Messenger通過Message的replyTo參數傳遞給服務端        msg.replyTo = mMessenger;        try {            messenger.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    public void onServiceDisconnected(ComponentName name) {        //斷開連接會觸發此方法    }};

服務端接收到客戶端傳來的信息,并返回數據:

private final Messenger messenger = new Messenger(new MessengerHandler());private static class MessengerHandler extends Handler{    @Override    public void handleMessage(Message msg) {        switch (msg.what){            case MyConstants.MSG_FROM_CLIENT:                LogUtils.e("服務端:" + msg.getData().getString("msg"));                //通過Message的replyTo得到客戶端傳遞過來的Messenger對象                Messenger replyTo = msg.replyTo;                Message replyMsg = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);                Bundle bundle = new Bundle();                bundle.putString("reply" , "this is reply");                replyMsg.setData(bundle);                try {                    replyTo.send(replyMsg);                } catch (RemoteException e) {                    e.printStackTrace();                }                break;            default:                super.handleMessage(msg);        }    }}@Overridepublic IBinder onBind(Intent intent) {    //綁定成功,返回Messenger對象底層的Binder    return messenger.getBinder();}

然后再在客戶端通過handler來進行處理:

switch (msg.what){    case MyConstants.MSG_FROM_SERVICE:        LogUtils.e("服務端返回回來的消息:" + msg.getData().getString("reply"));        break;

AIDL

一、創建AIDL文件:    ① 自定義的Parcelable對象和AIDL對象必須要顯示import進來,不管是否位于同一個包    ② aidl接口只支持方法,不支持靜態常量    ③ aidl中除了基本的數據類型,其他類型的參數必須標上方向:in 、out或者inout。其中:           in: 表示輸入型的參數           out:表示輸出型的參數           inout:表示輸入輸出型的參數    ④ 如果aidl中用到了自定義的Parcelable對象,那么就必須新建一個和它同名的AIDL文件,并且在其中申明它為 Parcelable    ⑤ build--->Rebuild project 生成對應的java文件,java文件路徑 build/generated/source/aidl/debug/XX.java        二、保持客戶端、服務端的aidl文件一致:    AIDL的包結構在服務端和客戶端要保持一致,否則運行會出錯,這是因為客戶端需要反序列化服務端中和AIDL接口相關的所有類。如果類的完整路徑不一樣,就無法成功反序列化三、問題:    ① 添加單個元素集合(list)        CopyOnWriteArrayList:支持并發的讀、寫(會存在多個線程同時訪問的情形);相對應的ConcurrentHashMap

/** * CopyOnWriteArrayList: *      支持并發的讀、寫(會存在多個線程同時訪問的情形);相對應的ConcurrentHashMap */private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();    ② 添加注冊注銷接口集合(map):              RemoteCallbackList:是系統專門提供的用于刪除跨進程listener的接口,其內部實現了自動同步的功能。              int N = RemoteCallbackList.beginBroadcast();              RemoteCallbackList.finishBroadcast();必須配對使用

/** * RemoteCallbackList: 是系統專門提供的用于刪除跨進程listener的接口,其內部實現了自動同步的功能。 *         int N = RemoteCallbackList.beginBroadcast(); *         RemoteCallbackList.finishBroadcast();必須配對使用 */private RemoteCallbackList<OnNewBookListener> mListeners = new RemoteCallbackList<>();    ② Binder意外死亡需要重新連接:            binderDied():默認在子線程執行            onServiceDisconnected():在默認主線程執行

四、添加aidl的訪問權限:    在客戶端和服務端都添加自定義的權限    <permission android:name="zidingyiquanxian"        android:protectionLevel="normal"/>    在服務端進行權限驗證

@Overridepublic IBinder onBind(Intent intent) {    int check = checkCallingOrSelfPermission("自定義權限");    if(check == PackageManager.PERMISSION_DENIED){        //權限拒絕,直接返回null    }    return mBinder;}

五、配置 builder.gradle文件:

//把src/main/aidl文件也作為Java.srcDirs, resources.srcDirs;否則找不到自定義的類sourceSets {    main {        manifest.srcFile 'src/main/AndroidManifest.xml'        java.srcDirs = ['src/main/java', 'src/main/aidl']        resources.srcDirs = ['src/main/java', 'src/main/aidl']        aidl.srcDirs = ['src/main/aidl']        res.srcDirs = ['src/main/res']        assets.srcDirs = ['src/main/assets']    }}

ContentProvider

這里以數據庫的存儲方式的例子來加深理解:

創建一個contentprovider:

/** * 定義單獨的uri和uri_code,并關聯在一起。當外界請求時可以根據請求的uri得到對應的uri_code,從而知道訪問哪一張表 */private static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);static {    matcher.addURI(AUTHORITIES , "book" , BOOK_URI_CODE);    matcher.addURI(AUTHORITIES , "users" , USERS_URI_CODE);}

/** * 代表contentprovider的創建,一般做初始化操作,運行在主線程,其他的方法運行在Binder線程池中 * @return */@Overridepublic boolean onCreate() {    context = getContext();    ProviderSqliteOpenHelper providerSqliteOpenHelper = new ProviderSqliteOpenHelper(context , ProviderSqliteOpenHelper.DB_NAME , null , 1);    db = providerSqliteOpenHelper.getWritableDatabase();    return true;}
/** * @param uri Uri請求的路徑 * @return 請求對應的MIME類型(媒體類型),如果不關心這個選項,可以直接返回null或者"星號/星號" */@Overridepublic String getType(Uri uri) {    return null;}@Overridepublic Uri insert(Uri uri, ContentValues values) {    String tableName = getTableName(uri);    if(tableName != null){        db.insert(tableName, null, values);        context.getContentResolver().notifyChange(uri , null);        return uri;    }    return null;}
public String getTableName(Uri uri){    String table = null;    switch (matcher.match(uri)){        case BOOK_URI_CODE:            table = ProviderSqliteOpenHelper.BOOK_TABLE_NAME;            break;        case USERS_URI_CODE:            table = ProviderSqliteOpenHelper.USER_TABLE_NAME;            break;    }    return table;}

在另外一個進程中訪問contentprovider:

Cursor cursor = contentResolver.query(bookUri, null, null, null, null);if (cursor.moveToFirst()){    int index = cursor.getColumnIndex("name");    String name = cursor.getString(index);    LogUtils.e("查詢到的數據:name = " + name);    while (cursor.moveToNext()){        int index1 = cursor.getColumnIndex("name");        String name1 = cursor.getString(index);        LogUtils.e("查詢到的數據:name = " + name1);    }}cursor.close();

Socket(“套接字”)

        ① 流式套接字(網絡傳輸控制層中的TCP協議):面向連接的協議,提供穩定的雙向通信功能,TCP連接的建立需要經過“三次握手”才能完成,為了提供穩定的數據傳輸功能,其本身提供了超時重傳機制,因此具有很高的穩定性。            Socket必須在發送數據之前與目的地的Socket取得連接,一旦連接建立了,Socket就可以使用一個流接口進行打開、讀寫以及關閉操作。并且,所有發送的數據在另一端都會以相同的順序被接收。        ② 用戶數據報套接字(UDP協議):是無連接的,提供不穩定的單向通信功能(目的地址和要發送的內容),當然UDP也能實現雙向通信功能。

1、Socket的構造方法  Java在包java.NET中提供了兩個類Socket和ServerSocket,分別用來表示雙向連接的Socket客戶端和服務器端。  Socket的構造方法如下:  (1)Socket(InetAddress address, int port);  (2)Socket(InetAddress address, int port, boolean stream);  (3)Socket(String host, int port);  (4)Socket(String host, int port, boolean stream);  (5)Socket(SocketImpl impl);  (6)Socket(String host, int port, InetAddress localAddr, int localPort);  (7)Socket(InetAddress address, int port, InetAddrss localAddr, int localPort);  ServerSocket的構造方法如下:  (1)ServerSocket(int port);  (2)ServerSocket(int port, int backlog);  (3)ServerSocket(int port, int backlog, InetAddress bindAddr);  其中,參數address、host和port分別是雙向連接中另一方的IP地址、主機名和端口號;參數stream表示Socket是流Socket還是數據報Socket;參數localAddr和localPort表示本地主機的IP地址和端口號;SocketImpl是Socket的父類,    既可以用來創建ServerSocket,也可以用來創建Socket。2、輸入流和輸出流  Socket提供了方法getInputStream()和getOutPutStream()來獲得對應的輸入流和輸出流,以便對Socket進行讀寫操作,這兩個方法的返回值分別是InputStream和OutPutStream對象。  為了便于讀寫數據,我們可以在返回的輸入輸出流對象上建立過濾流,如PrintStream、InputStreamReader和OutputStreamWriter等。3、關閉Socket  可以通過調用Socket的close()方法來關閉Socket。在關閉Socket之前,應該先關閉與Socket有關的所有輸入輸出流,然后再關閉Socket。

例子:

客戶端與服務端建立連接:

public void connectTcp(){        while (mSocket == null){            //和tcp服務建立連接            try {                mSocket = new Socket("localhost" , 8688);                printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream())) , true);                isConnected = true;            } catch (IOException e) {                e.printStackTrace();                SystemClock.sleep(1000);                isConnected = false;            }            if(isConnected) {                //接收服務端的消息                try {                    br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));                    while (!SocketTcpActivity.this.isFinishing()){                        String msg = br.readLine();                        if(msg != null){                            Message message = Message.obtain();                            Bundle bundle = new Bundle();                            bundle.putString("serverMsg" , msg);                            message.setData(bundle);                            message.what = MyConstants.ACCPT_SERVER_MSG;                            mHandler.sendMessage(message);                        }                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        }        br.close();        printWriter.close();        mSocket.close();

再次發送消息:

printWriter.println(msg);

在服務端開一個線程監聽客戶端的連接請求:

public void run() {    try {        serverSocket = new ServerSocket(8688);    } catch (IOException e) {        e.printStackTrace();        return;    }    while (!isServiceDied){        try {            //指定端口實例化一個ServerSocket,并調用ServerSocket的accept()方法在等待客戶端連接期間造成阻塞。            final Socket socket = serverSocket.accept();            new Thread(new Runnable() {                @Override                public void run() {                    responseClient(socket);
public void responseClient(Socket socket){    try {        //接收客戶端的消息        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));        //用于向客戶端發送消息        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);        while (!isServiceDied){            String str = in.readLine();            if(str == null){                //客戶端傳來的數據為空(可以做一些斷開連接、不回復消息等操作)                break;            }            int i = new Random().nextInt(strMsg.length);            String msg = strMsg[i];            out.println(packMessage(msg));        }        out.close();        in.close();        socket.close();

選擇合適的IPC機制

完整demo路徑

http://download.csdn.net/detail/fanghana/9762302


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 云南省| 定边县| 资兴市| 泊头市| 昭苏县| 克什克腾旗| 应城市| 琼中| 康定县| 盐源县| 建宁县| 西城区| 黔江区| 揭西县| 武陟县| 绵阳市| 凉山| 曲麻莱县| 三河市| 长顺县| 上高县| 邓州市| 阿拉善右旗| 平舆县| 清原| 龙山县| 洪泽县| 湟源县| 永新县| 五峰| 图木舒克市| 云南省| 错那县| 昭觉县| 绥滨县| 甘南县| 泾源县| 黔西| 锡林郭勒盟| 康保县| 台南市|