Android實(shí)現(xiàn)多進(jìn)程并發(fā)控制的兩種方案
一、問(wèn)題背景
當(dāng)一個(gè)App中存在多個(gè)進(jìn)程時(shí)例如存在 主進(jìn)程,輔進(jìn)程兩個(gè)進(jìn)程,兩個(gè)進(jìn)程都會(huì)去向A文件中寫入數(shù)據(jù)。但是我們業(yè)務(wù)中希望每次僅允許有一個(gè)進(jìn)程向A文件寫入內(nèi)容。即當(dāng)主進(jìn)程寫入時(shí),輔進(jìn)程要等待主進(jìn)程寫完之后才可以寫入,防止出現(xiàn)并發(fā)修改導(dǎo)致數(shù)據(jù)異常的問(wèn)題。
在實(shí)際的場(chǎng)景上,例如在我們的項(xiàng)目中未使用MMKV之前,KV存儲(chǔ)是自行實(shí)現(xiàn)的多進(jìn)程并發(fā)的SP。
二、實(shí)現(xiàn)方案
1、方案1:僅一個(gè)進(jìn)程負(fù)責(zé)寫
將所有的寫入操作調(diào)整到同一個(gè)進(jìn)程中,這樣就相當(dāng)于規(guī)避了多進(jìn)程并發(fā)問(wèn)題。
我們可以通過(guò)提供一個(gè)ContantProvider或者Service來(lái)是實(shí)現(xiàn)這個(gè)功能。
以下是使用ContentProvider的方式:
FileProvider
public class FileProvider extends ContentProvider { private static final String AUTHORITY = "com.example.fileprovider"; private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/file"); // 文件鎖,確保單進(jìn)程寫入 private static final Object fileLock = new Object(); @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; // 不提供查詢功能 } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { return null; // 不提供插入功能 } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; // 不提供刪除功能 } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; // 不提供更新功能 } // 自定義方法:寫入文件 @Override public Bundle call(String method, String arg, Bundle extras) { if ("writeToFile".equals(method)) { String content = extras.getString("content"); synchronized (fileLock) { writeToFile(content); } Bundle result = new Bundle(); result.putBoolean("success", true); return result; } return super.call(method, arg, extras); } // 實(shí)際寫入文件的邏輯 private void writeToFile(String content) { File file = new File(getContext().getFilesDir(), "A.txt"); try (FileOutputStream fos = new FileOutputStream(file, true)) { fos.write(content.getBytes()); } catch (IOException e) { e.printStackTrace(); } } }
注冊(cè)
<provider android:name=".FileProvider" android:authorities="com.example.fileprovider" android:exported="true" />
寫入邏輯
private void writeToFileViaProvider(String content) { Uri uri = Uri.parse("content://com.example.fileprovider/file"); ContentResolver resolver = getContentResolver(); Bundle extras = new Bundle(); extras.putString("content", content); try { Bundle result = resolver.call(uri, "writeToFile", null, extras); if (result != null && result.getBoolean("success")) { Log.d("FileProvider", "Write successful"); } } catch (Exception e) { Log.e("FileProvider", "Failed to write file", e); } }
使用Service + Binder的方式,代碼比較簡(jiǎn)單,這里就不寫了。
2、方案2:通過(guò)文件鎖的方式
文件鎖主要是利用FileChannel、FileLock來(lái)控制多進(jìn)程并發(fā)。
關(guān)于 Channel
Channel 經(jīng)常翻譯為通道,類似 IO 中的流,用于讀取和寫入。不用像BIO那樣,讀數(shù)據(jù)和寫數(shù)據(jù)需要不同的數(shù)據(jù)通道。
public interface Channel extends Closeable { /** * Tells whether or not this channel is open. * * @return <tt>true</tt> if, and only if, this channel is open */ public boolean isOpen(); /** * Closes this channel. */ public void close() throws IOException; }
我們常用的Channel有:
- FileChannel:文件通道,用于文件的讀和寫。
- DatagramChannel:用于UDP連接的接收和發(fā)送。
- SocketChannel:把它理解為TCP連接通道,簡(jiǎn)單理解就是TCP客戶端。
- ServerSocketChannel:TCP對(duì)應(yīng)的服務(wù)端,用于監(jiān)聽某個(gè)端口進(jìn)來(lái)的請(qǐng)求。
FileChannel
FileChannel 是 Java NIO (New I/O) 中的一個(gè)類,用于對(duì)文件進(jìn)行高效的讀寫操作。它提供了比傳統(tǒng) FileInputStream
和 FileOutputStream
更靈活和高效的文件操作方式。
所有的NIO操作始于通道,通道是數(shù)據(jù)來(lái)源或數(shù)據(jù)寫入的目的地。其與 Buffer 打交道,讀操作的時(shí)候?qū)?Channel 中的數(shù)據(jù)填充到 Buffer 中,而寫操作時(shí)將 Buffer 中的數(shù)據(jù)寫入到 Channel 中。
FileChannel的獲取方式:
通過(guò)FileInputStream/FileOutputStream
// 通過(guò) FileInputStream/FileOutputStream (只讀或只寫) FileInputStream fis = new FileInputStream("file.txt"); FileChannel readChannel = fis.getChannel(); FileOutputStream fos = new FileOutputStream("file.txt"); FileChannel writeChannel = fos.getChannel();
通過(guò)RandomAccessFile
// 通過(guò) RandomAccessFile RandomAccessFile raf = new RandomAccessFile("file.txt", "rw"); FileChannel channel = raf.getChannel();
通過(guò)FileChannel.open()
FileChannel channel = FileChannel.open(Paths.get("file.txt"), StandardOpenOption.READ);
在我們示例代碼中選擇了使用 FileOutputStream
來(lái)獲取FileChannel。
FileLock
FileLock
表示文件或文件區(qū)域的鎖,用于控制多個(gè)進(jìn)程或線程對(duì)同一文件的并發(fā)訪問(wèn)。
鎖的類型
- 共享鎖 (Shared Lock) :多個(gè)進(jìn)程可同時(shí)持有,用于讀操作
- 排他鎖 (Exclusive Lock) :一次只能由一個(gè)進(jìn)程持有,用于寫操作
通過(guò)文件鎖的方式控制多進(jìn)程并發(fā)的 示例代碼:
public class FileWriter { private static final String FILE_PATH = "/path/to/your/file.txt"; public void writeToFile(String content) { File file = new File(FILE_PATH); try { FileOutputStream fos = new FileOutputStream(file, true); FileChannel channel = fos.getChannel()) // 獲取獨(dú)占鎖 FileLock lock = channel.lock(); try { // 寫入文件 fos.write(content.getBytes()); } finally { // 釋放鎖 lock.release(); } } catch (IOException e) { e.printStackTrace(); } } }
三、總結(jié)
以上簡(jiǎn)單介紹了一下兩種控制多進(jìn)程并發(fā)的方案。
其中使用ContentProvider或者Service的方式將所有的操作控制在同一個(gè)進(jìn)程中的方案邏輯清晰,但是代碼量比較多。尤其是使用Service的方式,雖然上面我們每個(gè)給出示例代碼,但是可以想象沒新增一個(gè)進(jìn)程都需要寫相關(guān)的代碼,寫起來(lái)就比較啰嗦了。
而ContentProvicer的方式,系統(tǒng)中也有很多相關(guān)的實(shí)現(xiàn)方案,例如更新媒體文件,更新聯(lián)系人數(shù)據(jù)等。
使用文件鎖的方式對(duì)于僅熟悉Android不熟悉Java的同學(xué)不容易想到,所以本篇也同時(shí)簡(jiǎn)單介紹了一下FileChannel以及FileLock。
到此這篇關(guān)于Android實(shí)現(xiàn)多進(jìn)程并發(fā)控制的兩種方案的文章就介紹到這了,更多相關(guān)Android多進(jìn)程并發(fā)控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡(jiǎn)單實(shí)現(xiàn)Android彈出菜單效果
這篇文章主要為大家詳細(xì)介紹了簡(jiǎn)單實(shí)現(xiàn)Android彈出菜單效果的相關(guān)代碼,感興趣的小伙伴們可以參考一下2016-06-06Android實(shí)現(xiàn)語(yǔ)音合成與識(shí)別功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)語(yǔ)音合成與識(shí)別功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07Android開發(fā)之全屏與非全屏的切換設(shè)置方法小結(jié)
這篇文章主要介紹了Android開發(fā)之全屏與非全屏的切換設(shè)置方法,結(jié)合實(shí)例形式分析了Android全屏切換靜態(tài)與動(dòng)態(tài)兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2017-08-08Android實(shí)戰(zhàn)打飛機(jī)游戲之無(wú)限循環(huán)的背景圖(2)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)戰(zhàn)打飛機(jī)游戲之無(wú)限循環(huán)的背景圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07Android開發(fā)之針對(duì)聯(lián)系人的封裝
本文給大家分享的是如何在Android開發(fā)中封裝聯(lián)系人模塊以及封裝后的使用及總結(jié),最后奉上代碼,有需要的小伙伴可以參考下。2016-02-02Android 中ContentProvider的實(shí)例詳解
這篇文章主要介紹了Android 中ContentProvider的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文大家能掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09Android實(shí)現(xiàn)的數(shù)字格式化用法示例
這篇文章主要介紹了Android實(shí)現(xiàn)的數(shù)字格式化用法,結(jié)合實(shí)例形式分析了Android數(shù)學(xué)運(yùn)算中數(shù)字格式化輸出的相關(guān)技巧,需要的朋友可以參考下2016-08-08Android簡(jiǎn)單實(shí)現(xiàn)天氣預(yù)報(bào)App
這篇文章主要為大家詳細(xì)介紹了Android簡(jiǎn)單實(shí)現(xiàn)天氣預(yù)報(bào)App,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09