加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

【Android】多进程访问数据库SQLite问题

(2016-07-06 11:23:48)
标签:

android

多进程

共享数据

oncreate多次执行

分类: Android

 一般来说,一个APP默认只有一个进程,进程名称就是它的包名,我们可以通过DDMS里面的Devices视图中看到手机运行的进程,如下图所示:

http://s8/mw690/001ICmKvzy732ghbhK727&690

 当然,在一些项目中,可能存在着一个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”标签来指定组件在哪个进程中运行,如下:

 


<service
android:name="com.facebook.rti.push.service.FbnsService"
android:label="@string/app_name"
android:process=":mqtt">
</service>

 如果“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 contextint pid){
    ActivityManager am (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
    List 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



 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有