多进程访问数据库SQLite问题

来源:互联网 发布:淘宝网妈妈秋装 编辑:程序博客网 时间:2024/05/18 01:21
一般来说,一个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。
原创粉丝点击