Android中的SharePreference的初始化流程

878 阅读2分钟

SharedPreference是Android中的一种简单的数据存储方式,支持存储数据量不大的数据内容.那么它是怎样进行初始化的呢.

查看源码时需要思考的问题

1.SharePreference是什么时间进行初始化的.
2.SharePreference是主线程加载还是子线程加载.

查看源码执行流程

SharedPreference在进程启动的时候是没有被加载的,只有当调用getSharedPreference的时候才回去初始化SP对象,通过查看源码可以看到: 当程序中调用了getSharedPreferences(String name,int mode)这个方法的时候,系统会调用ContextWrapper中的getSharedPreferences方法,代码如下

ContextWrapper
@Override
 public SharedPreferences getSharedPreferences(String name, int mode) {
     return mBase.getSharedPreferences(name, mode);
 }

在这个方法中会去调用mBase对象中的getSharedPreferences方法,这个mBase就是ContextImpl这个类的实例对象,是在Application创建的时候通过attach方法进行绑定的,所以接下来就执行到了ContextImpl中的getSharedPreferences方法,代码如下:

   ContextImpl
    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            if (sSharedPrefs == null) {
                sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
            }

            final String packageName = getPackageName();
            //通过包名获取当前整个应用中的所有的SP的实现对象
            ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
            if (packagePrefs == null) {
                packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
                sSharedPrefs.put(packageName, packagePrefs);
            }

            // At least one application in the world actually passes in a null
            // name.  This happened to work because when we generated the file name
            // we would stringify it to "null.xml".  Nice.
            if (mPackageInfo.getApplicationInfo().targetSdkVersion <
                    Build.VERSION_CODES.KITKAT) {
                if (name == null) {
                    name = "null";
                }
            }
			//根据对应的文件名称回去对应的SP实现对象
            sp = packagePrefs.get(name);
            if (sp == null) {
                File prefsFile = getSharedPrefsFile(name);
                sp = new SharedPreferencesImpl(prefsFile, mode);
                packagePrefs.put(name, sp);
                return sp;
            }
        }
        if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
            getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
            // If somebody else (some other process) changed the prefs
            // file behind our back, we reload it.  This has been the
            // historical (if undocumented) behavior.
            sp.startReloadIfChangedUnexpectedly();
        }
        return sp;
    }

通过上面的代码可以看出实际上SP对象创建的时候是通过创建SharedPreferencesImpl实现的,代码就是new SharedPreferencesImpl(prefsFile, mode)传递的参数是文件以及使用模式,那么看一下SharedPreferencesImpl(prefsFile, mode)又进行了那些工作

 SharedPreferencesImpl(File file, int mode) {
 		//保存文件
        mFile = file;
        //创建备份文件
        mBackupFile = makeBackupFile(file);
        //使用的模式
        mMode = mode;
        //加载完成的标记
        mLoaded = false;
        mMap = null;
        //开始从硬盘加载文件的任务
        startLoadFromDisk();
    }
    private void startLoadFromDisk() {
        synchronized (this) {
            mLoaded = false;
        }
        new Thread("SharedPreferencesImpl-load") {
            public void run() {
                synchronized (SharedPreferencesImpl.this) {
                    loadFromDiskLocked();
                }
            }
        }.start();
    }

    private void loadFromDiskLocked() {
        if (mLoaded) {
            return;
        }
        //备份文件如果存在
        if (mBackupFile.exists()) {
        	//
            mFile.delete();
            mBackupFile.renameTo(mFile);
        }

        // Debugging
        if (mFile.exists() && !mFile.canRead()) {
            Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
        }

        Map map = null;
        StructStat stat = null;
        try {
            stat = Os.stat(mFile.getPath());
            if (mFile.canRead()) {
                BufferedInputStream str = null;
                try {
                	//读取文件中的数据转换为字符串
                    str = new BufferedInputStream(
                            new FileInputStream(mFile), 16*1024);
                    //解析字符串生成对应的map集合
                    map = XmlUtils.readMapXml(str);
                } catch (XmlPullParserException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                } catch (FileNotFoundException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                } catch (IOException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                } finally {
                    IoUtils.closeQuietly(str);
                }
            }
        } catch (ErrnoException e) {
        }
        //加载整个文件完成
        mLoaded = true;
        if (map != null) {
            mMap = map;
            mStatTimestamp = stat.st_mtime;
            mStatSize = stat.st_size;
        } else {
            mMap = new HashMap<String, Object>();
        }
        //通知其他线程进行后续操作
        notifyAll();
    }

以上就是整个SP初始化的相关流程,那么上面的两个问题第一个可以发现实际上SP初始化只有在调用的时候才会进行初始化并且初始化成功一次之后就会复用当前这一个对象,第二个初始化的过程是保证在那个线程调用的就在那个线程初始化,但是整个文件的加载过程是在异步线程进行的.