lock)和强制锁(
mandatory
lock)
2.可以使用
O_APPEND
每次seek到文件尾部,完成
3.可以像syslog
一样的机制,由一个进程进行读写文件,其他进程通过消息队列的方式(有的说是/dev/log
的本地socket)和该读写文件进程进行通信,完成多进程读写。
java.lang.RuntimeException: Using WebView from more than one process at once with the same data directory is not supported
实际在项目中运用 application中设置多个存储目录,虽然能减少问题发生的次数,但是我们在firebase上依然能发现很多这个同样的崩溃信息
那么这个问题发生的原因究竟是什么?一起来分析下抛出这个异常的逻辑吧
https://chromium.googlesource.com/chromium/src/+/refs/heads/main/android_webview/java/src/org/chromium/android_webview/AwDataDirLock.java#126
判断原理:进程是否持有WebView数据目录中的webview_data.lock文件的锁,如果子进程也对相同文件尝试加速,则会crash
通过检查目标目录的文件锁,如果能够获得到锁,就表明无异常;如果获取不到文件锁,再次重新设置存储目录。
进程间的通讯无非就是读写文件,socket通讯或者使用共享内存。你不想用读写文件的方式,那就用共享内存或者socket通讯的方式。我个人觉得用socket比较简单,也许是因为我对socket比较熟悉。
下面是一篇java实现共享内存的文章,java没法管理内存,其实他也是靠创建映像文件来实现的。
共享内存在java中的实现
在jdk1.4中提供的类MappedByteBuffer为我们实现共享内存提供了较好的方法。该缓冲区实际上是一个磁盘文件的内存映像。二者的变化将保持同步,即内存数据发生变化会立刻反映到磁盘文件中,这样会有效的保证共享内存的实现。
将共享内存和磁盘文件建立联系的是文件通道类:FileChannel。该类的加入是JDK为了统一对外部设备(文件、网络接口等)的访问方法,并且加强了多线程对同一文件进行存取的安全性。例如读写 *** 作统一成read和write。这里只是用它来建立共享内存用,它建立了共享内存和磁盘文件之间的一个通道。
打开一个文件建立一个文件通道可以用RandomAccessFile类中的方法getChannel。该方法将直接返回一个文件通道。该文件通道由于对应的文件设为随机存取文件,一方面可以进行读写两种 *** 作,另一方面使用它不会破坏映像文件的内容(如果用FileOutputStream直接打开一个映像文件会将该文件的大小置为0,当然数据会全部丢失)。这里,如果用 FileOutputStream和FileInputStream则不能理想的实现共享内存的要求,因为这两个类同时实现自由的读写 *** 作要困难得多。
下面的代码实现了如上功能,它的作用类似UNIX系统中的mmap函数。
// 获得一个只读的随机存取文件对象
RandomAccessFile RAFile = new RandomAccessFile(filename,"r")
// 获得相应的文件通道
FileChannel fc = RAFile.getChannel()
// 取得文件的实际大小,以便映像到共享内存
int size = (int)fc.size()
// 获得共享内存缓冲区,该共享内存只读
MappedByteBuffer mapBuf = fc.map(FileChannel.MAP_RO,0,size)
// 获得一个可读写的随机存取文件对象
RAFile = new RandomAccessFile(filename,"rw")
// 获得相应的文件通道
fc = RAFile.getChannel()
// 取得文件的实际大小,以便映像到共享内存
size = (int)fc.size()
// 获得共享内存缓冲区,该共享内存可读写
mapBuf = fc.map(FileChannel.MAP_RW,0,size)
// 获取头部消息:存取权限
mode = mapBuf.getInt()
如果多个应用映像同一文件名的共享内存,则意味着这多个应用共享了同一内存数据。这些应用对于文件可以具有同等存取权限,一个应用对数据的刷新会更新到多个应用中。
为了防止多个应用同时对共享内存进行写 *** 作,可以在该共享内存的头部信息加入写 *** 作标志。该共享内存的头部基本信息至少有:
int Length; // 共享内存的长度。
int mode// 该共享内存目前的存取模式。
共享内存的头部信息是类的私有信息,在多个应用可以对同一共享内存执行写 *** 作时,开始执行写 *** 作和结束写 *** 作时,需调用如下方法:
public boolean StartWrite()
{
if(mode == 0) { // 标志为0,则表示可写
mode = 1// 置标志为1,意味着别的应用不可写该共享内存
mapBuf.flip()
mapBuf.putInt(mode)// 写如共享内存的头部信息
return true
}
else {
return false// 指明已经有应用在写该共享内存,本应用不可写该共享内存
}
}
public boolean StopWrite()
{
mode = 0// 释放写权限
mapBuf.flip()
mapBuf.putInt(mode)// 写入共享内存头部信息
return true
}
这里提供的类文件mmap.java封装了共享内存的基本接口,读者可以用该类扩展成自己需要的功能全面的类。
如果执行写 *** 作的应用异常中止,那么映像文件的共享内存将不再能执行写 *** 作。为了在应用异常中止后,写 *** 作禁止标志自动消除,必须让运行的应用获知退出的应用。在多线程应用中,可以用同步方法获得这样的效果,但是在多进程中,同步是不起作用的。方法可以采用的多种技巧,这里只是描述一可能的实现:采用文件锁的方式。写共享内存应用在获得对一个共享内存写权限的时候,除了判断头部信息的写权限标志外,还要判断一个临时的锁文件是否可以得到,如果可以得到,则即使头部信息的写权限标志为1(上述),也可以启动写权限,其实这已经表明写权限获得的应用已经异常退出,这段代码如下:
// 打开一个临时的文件,注意同一共享内存,该文件名要相同,可以在共享文件名后加后缀“.lock”。
RandomAccessFile fis = new RandomAccessFile("shm.lock","rw")
// 获得文件通道
FileChannel lockfc = fis.getChannel()
// 获得文件的独占锁,该方法不产生堵塞,立刻返回
FileLock flock = lockfc.tryLock()
// 如果为空,则表明已经有应用占有该锁
if(flock == null) {
...// 不能执行写 *** 作
}
else {
...// 可以执行写 *** 作
}
该锁会在应用异常退出后自动释放,这正是该处所需要的方法。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)