多进程访问数据库SQLite问题

xiaoxiao2021-02-28  74

一般来说,一个APP默认只有一个进程,进程名称就是它的包名,我们可以通过DDMS里面的Devices视图中看到手机运行的进程,如下图所示:   [Android开发]多进程访问数据库SQLite问题         当然,在一些项目中,可能存在着一个APP有多个进程的情况。如上面的“com.instagram.android”和“com.instragram.android.mqtt”其实就是Instragram的两个不同的进程。两者之间的通讯就属于跨进程通讯了。        多进程的有很多好处,        1)可以获得更多内存。进程是系统分配资源和调度的基本单位,进程越多得到的资源就越多。        2)一个Service如果处于一个独立的进程中,那么即使这个Service崩溃掉,主进程都不受影响。如果主进程崩溃掉,Service进程也不受影响。http://stackoverflow.com/questions/4658511/android-how-to-decide-whether-to-run-a-service-in-a-separate-process        多进程的实现方式,一般是在AndroidManefest.xml中声明组件的时候,通过“android:process”标签来指定组件在哪个进程中运行,如下:   [Android开发]多进程访问数据库SQLite问题          如果“android:process”的值不是“:”开头,则系统里有同样名字的进程的话,会放到已存在的同名进程里运行,这样能减小消耗。         如果“android:process”的值是以“:”开头,则启动一个指定名字的进程。         多进程的APP,进程间的内存是不可见。         另外,多进程的APP还会导致Application.onCreate()函数会被执行多次(每个进程执行一次)。这种情况会导致很多不必要的错误。下面举个例子说明一下:         假设APP在Application.onCreate()中启动了一个账号数据管理助手AccountManager,该类为单例模式,负责对SQLite的账号进行增、删、改等操作。那么,如果这个APP是多进程模式的话,就会执行多次Application.onCreate()方法,导致出现多个AccountManager实例。可能你会迷惑,这个类明明是单例模式啊,怎么会有多个实例呢?其原因就是进程间内存的不可见性。由于两个进程的内存相互独立,这就会出现多个AccountManager实例了。如果每个进程都使用AccountManager去对同一个数据进行操作的话,那就会出现多进程访问共享数据库问题了。         多进程访问共享数据与多线程访共享问数据是不同的,多线程下还可以通过同步或加锁的方式避免冲突。但是多进程访问数据库就很难解决了,因为在Android系统中一个进程就是一个VM虚拟机,其底层如何对数据库进行操作,我们控制不了(多进程访问同一文件还有解决方法),我们也很难在Java层针对多进程访问数据度进行有效控制,所以强烈建议避免多进程访问同一数据库。         为了避免多进程访问数据库,通常的做法是避免Application.onCreate()被多次调用,我们通过在onCreate()被调用时,判断当前进程的名称,如果是默认进程名称(即包名),那么我们才做AccountManager的初始化操作,如果不是,那我们就不做处理,这样就可以避免多次初始化AccountManager,从而避免多进程并发访问同一数据库。         以下方法可以获取当前进程的名称        ------------------------------------------------------------------------------     public static String getProcessName(Context context, int pid){         ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);         List<</SPAN>ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();         if (runningApps != null && !runningApps.isEmpty()) {             for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {                 if (procInfo.pid == pid) {                     return procInfo.processName;           }         }       }         return null;     }        ------------------------------------------------------------------------------         其中pid即进程的ID,可以通过“android.os.Process.myPid()”获取。          使用方法如下所示:       ------------------------------------------------------------------------------     @Override     public void onCreate()         String processName = getProcessName(this, android.os.Process.myPid());         if (processName != null) {             boolean defaultProcess = processName.equals(getPackageName());             if (defaultProcess) {                initMainProcess();             } else if (processName.contains(":mqtt")) {           //TODO-处理mqtt进程的初始化         }       }     }        ------------------------------------------------------------------------------        这样一来,我们就可以根据不同的进程来初始化不同的数据,也就可以解决AccountManager被实例化多次的问题,进而避免了多进程访问数据库。         如果需求中必须用到多进程访问共享数据库才能解决问题,那么可以考虑使用ContentProvider。
转载请注明原文地址: https://www.6miu.com/read-52309.html

最新回复(0)