SharedPreferences源码解析和可能ANR的情况及建议

ANR使用不当可能会造成ANR,为什么这样说呢?下面我们分析源码:
一般来说我们是通过Context实例获取SharedPreferences对象的,即:

 public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
 

上面是Context类里面的抽象方法,所以我们需要找到具体实现类,正常来说我们可以在Activity或者Application里面直接进行上面方法的调用,在Activity内部点击调用getSharedPreferences方法点击进去,发现定位到ContextWrapper类的对应方法,因为Activity和Application都是继承自ContextWrapper这个父类的:

public class ContextWrapper extends Context {
   
    Context mBase;

    public ContextWrapper(Context base) {
   
        mBase = base;
    }
    
    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
   
        if (mBase != null) {
   
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
   
        return mBase;
    }
   @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
   
        return mBase.getSharedPreferences(name, mode);
    }
}


可以看到ContextWrapper是继承了Context这个抽象类的,然后我们看到getSharedPreferences方法内部是调用了mBase对应方法的,那这个mBase是什么时候初始化的呢?这个就涉及到了Activity的创建流程了,正常Activity的创建是回调ActivityThreadhandleLaunchActivity方法的,里面又调用了performLaunchActivity方法,

/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
   
       ···
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);

	    if (activity != null) {
   
             appContext.setOuterContext(activity);
             activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
	     ···
        }
		···
}

可以看到在这里创建了ContextImpl对象,也创建了Activity并调用了Activity的attach方法

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
   
        attachBaseContext(context);
}

@Override
protected void attachBaseContext(Context newBase) {
   
        super.attachBaseContext(newBase);
        if (newBase != null) {
   
            newBase.setAutofillClient(this);
        }
}

attach里面调用了attachBaseContext,最终经过层层super的调用,走到了ContextWrapper父类里面,将创建好的ContextImpl对象赋值给了mBase
所以我们最终定位到sharedPreference的实现是在ContextImpl这个类里面。


 /**
     * Map from package name, to preference name, to cached preferences.
     */
    @GuardedBy("ContextImpl.class")
    private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;

    /**
     * Map from preference name to generated path.
     */
    @GuardedBy("ContextImpl.class")
    private ArrayMap<String, File> mSharedPrefsPaths;

@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
   
        // 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";
            }
        }

        File file;
        synchronized (ContextImpl.class) {
   
            if (mSharedPrefsPaths == null) {
   
                mSharedPrefsPaths = new ArrayMap<>();
            }
            file = mSharedPrefsPaths.get(name);
            if (file == null) {
   
                file = getSharedPreferencesPath(name);
                mSharedPrefsPaths.put(name, file);
            }
        }
        return getSharedPreferences(file, mode);
}

    @Override
    public SharedPreferences getSharedPreferences(File file, int mode) {
   
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
   
            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
            sp = cache.get(file);
            if (sp == null) {
   
                checkMode(mode);
                if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
   
                    if (isCredentialProtectedStorage()
                            && !getSystemService(UserManager.class)
                                    .isUserUnlockingOrUnlocked(UserHandle.myUserId
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值