close
安卓進階漲薪訓練營,讓一部分人先進大廠

大家好,我是皇叔,最近開了一個安卓進階漲薪訓練營,可以幫助大家突破技術&職場瓶頸,從而度過難關,進入心儀的公司。

詳情見文章:沒錯!皇叔開了個訓練營

原文鏈接:https://www.jianshu.com/p/04fd2744e6c9

原理講解

在Linux中一般來說我們寫數據到文件是通過調用系統的函數將我們用戶進程中的數據先拷貝給Linux內核然後由Linux內核再將數據寫到文件中,中間經歷了兩個過程,如下圖所示

而我們使用mmap文件映射的話就可以將數據直接寫到文件中,如下圖所示

這樣的話中間就可以省略一個步驟,因此效率也會大大提升,這時我們再將這塊映射的文件區域進行共享讓其他進程可以訪問,如下圖所示,這樣我們就實現了一個簡單的跨進程通信了

代碼實現

這裡建立了兩個項目,Mmap(發送信息的項目)和Mmapobserve(接收信息的項目),接下來我們先從Mmap(發送信息的項目)說起

●Mmap(發送信息的項目)

這裡我創建了三個native方法

/***開啟共享映射*@paramabsolutePath*/publicnativevoidmmapOpen(StringabsolutePath);/***關閉共享映射*/publicnativevoidmmapClose();/***寫入數據*@paramcontent*/publicnativevoidmmapWrite(Stringcontent);

``

#include<jni.h>#include<string>#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/stat.h>#include<string.h>#include<sys/mman.h>#include<fcntl.h>extern"C"{char*ptr=NULL;}extern"C"JNIEXPORTvoidJNICALLJava_com_itfitness_mmap_MainActivity_mmapOpen(JNIEnv*env,jobject/*this*/,jstringpath){constchar*file_path=env->GetStringUTFChars(path,0);intfd=open(file_path,O_RDWR|O_CREAT|O_TRUNC,0644);//打開本地磁盤中的文件(如果沒有就創建一個),獲取fd,0644是可讀寫的意思if(fd==-1){perror("openerror");}//改變文件的大小(否則大小對應不起來就報錯)ftruncate(fd,100);ptr=(char*)mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(ptr==MAP_FAILED){perror("mmaperror");}//關閉文件句柄close(fd);}extern"C"JNIEXPORTvoidJNICALLJava_com_itfitness_mmap_MainActivity_mmapClose(JNIEnv*env,jobjectthiz){if(ptr!=NULL){//釋放內存映射區intret=munmap(ptr,100);if(ret==-1){perror("munmaperror");}}}extern"C"JNIEXPORTvoidJNICALLJava_com_itfitness_mmap_MainActivity_mmapWrite(JNIEnv*env,jobjectthiz,jstringcontent){if(ptr!=NULL){constchar*c_content=env->GetStringUTFChars(content,0);//修改映射區數據strcpy(ptr,c_content);}}

這裡各個函數的大致流程如下圖所示

Activity中調用的邏輯如下

publicclassMainActivityextendsAppCompatActivity{privateButtonbtOpen;privateEditTextetContent;privateButtonbtWrite;privateButtonbtClose;//Usedtoloadthe'native-lib'libraryonapplicationstartup.static{System.loadLibrary("native-lib");}@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btOpen=(Button)findViewById(R.id.bt_open);etContent=(EditText)findViewById(R.id.et_content);btWrite=(Button)findViewById(R.id.bt_write);btClose=(Button)findViewById(R.id.bt_close);btOpen.setOnClickListener(v->{mmapOpen(Environment.getExternalStorageDirectory().getAbsolutePath()+"/mmaptest.txt");});btClose.setOnClickListener(v->{mmapClose();});btWrite.setOnClickListener(v->{Stringcontent=etContent.getText().toString();mmapWrite(content);etContent.setText("");});}/***開啟共享映射*@paramabsolutePath*/publicnativevoidmmapOpen(StringabsolutePath);/***關閉共享映射*/publicnativevoidmmapClose();/***寫入數據*@paramcontent*/publicnativevoidmmapWrite(Stringcontent);}

布局文件如下

<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><EditTextandroid:id="@+id/et_content"android:layout_width="match_parent"android:layout_height="wrap_content"/><Buttonandroid:id="@+id/bt_write"android:text="寫入"android:layout_width="match_parent"android:layout_height="wrap_content"/><Buttonandroid:id="@+id/bt_open"android:text="打開"android:layout_width="match_parent"android:layout_height="wrap_content"/><Buttonandroid:id="@+id/bt_close"android:text="關閉"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout>

接下來我們運行APP寫入一段文字

我們打開SD卡中對應的文件

發現信息已經寫入

接下來我們再來看接收信息的項目

●Mmapobserve(接收信息的項目)

這裡我創建了四個native方法

/***開啟共享映射*@paramabsolutePath*/publicnativevoidmmapOpen(StringabsolutePath);/***關閉共享映射*/publicnativevoidmmapClose();/***將映射區置空*/publicnativevoidmmapSetEmpty();/***監聽映射區的內容*@paramdefaultVal傳入的默認值*@return*/publicnativeStringobserve(StringdefaultVal);#include<jni.h>#include<string>#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/stat.h>#include<string.h>#include<sys/mman.h>#include<fcntl.h>extern"C"{char*ptr=NULL;}extern"C"JNIEXPORTvoidJNICALLJava_com_itfitness_mmapobserve_MainActivity_mmapOpen(JNIEnv*env,jobject/*this*/,jstringpath){constchar*file_path=env->GetStringUTFChars(path,0);intfd=open(file_path,O_RDWR|O_CREAT|O_TRUNC,0644);//打開本地磁盤中的文件(如果沒有就創建一個),獲取fd,0644是可讀寫的意思if(fd==-1){perror("openerror");}//改變文件的大小(否則大小對應不起來就報錯)ftruncate(fd,100);ptr=(char*)mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if(ptr==MAP_FAILED){perror("mmaperror");}//關閉文件句柄close(fd);}extern"C"JNIEXPORTvoidJNICALLJava_com_itfitness_mmapobserve_MainActivity_mmapClose(JNIEnv*env,jobjectthiz){if(ptr!=NULL){//釋放內存映射區intret=munmap(ptr,100);if(ret==-1){perror("munmaperror");}}}extern"C"JNIEXPORTvoidJNICALLJava_com_itfitness_mmapobserve_MainActivity_mmapSetEmpty(JNIEnv*env,jobjectthiz){if(ptr!=NULL){//將共享映射區置空memset(ptr,0,100);}}extern"C"JNIEXPORTjstringJNICALLJava_com_itfitness_mmapobserve_MainActivity_observe(JNIEnv*env,jobject/*this*/,jstringdefaultVal){if(ptr!=NULL){returnenv->NewStringUTF(ptr);}returndefaultVal;}

由於上面已經展示了mmapOpen和mmapClose函數的流程了,因此這裡我就只展示下mmapSetEmpty和observe函數的流程

Activity中的邏輯如下

publicclassMainActivityextendsAppCompatActivity{privateButtonbtObserve;privateButtonbtOpen;privateButtonbtClose;//Usedtoloadthe'native-lib'libraryonapplicationstartup.static{System.loadLibrary("native-lib");}privatebooleanisObserve=false;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btObserve=(Button)findViewById(R.id.bt_observe);btOpen=(Button)findViewById(R.id.bt_open);btClose=(Button)findViewById(R.id.bt_close);btOpen.setOnClickListener(v->{mmapOpen(Environment.getExternalStorageDirectory().getAbsolutePath()+"/mmaptest.txt");});btClose.setOnClickListener(v->{isObserve=false;mmapClose();Toast.makeText(this,"關閉並停止監聽",Toast.LENGTH_SHORT).show();});btObserve.setOnClickListener(v->{if(isObserve)return;isObserve=true;//開線程每隔500毫秒獲取一下共享映射區的內容newThread(()->{while(isObserve){try{Stringobserve=observe("");//當我們監聽到共享區的內容不為空的時候就將內容以Toast的方式顯示出來if(!TextUtils.isEmpty(observe)){runOnUiThread(()->{Toast.makeText(this,observe,Toast.LENGTH_SHORT).show();});//獲取完之後將共享區內容置空mmapSetEmpty();}Thread.sleep(500);}catch(Exceptione){}}}).start();Toast.makeText(this,"開始監聽",Toast.LENGTH_SHORT).show();});}/***開啟共享映射*@paramabsolutePath*/publicnativevoidmmapOpen(StringabsolutePath);/***關閉共享映射*/publicnativevoidmmapClose();/***將映射區置空*/publicnativevoidmmapSetEmpty();/***監聽映射區的內容*@paramdefaultVal傳入的默認值*@return*/publicnativeStringobserve(StringdefaultVal);}<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/bt_observe"android:text="監聽"android:layout_width="match_parent"android:layout_height="wrap_content"/><Buttonandroid:id="@+id/bt_open"android:text="打開"android:layout_width="match_parent"android:layout_height="wrap_content"/><Buttonandroid:id="@+id/bt_close"android:text="關閉"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout>

這裡我們主要的邏輯就是通過開啟子線程去循環讀取共享映射區的內容,如果內容不為空我們就將讀取的內容顯示出來,然後我們將共享區的內容置空

效果展示

接下來我們將兩個項目運行起來,看一看效果

案例源碼

https://gitee.com/itfitness/mmap https://gitee.com/itfitness/mmap-observe



為了防止失聯,歡迎關注我防備的小號


微信改了推送機制,真愛請星標本公號👇

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()