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的创建是回调ActivityThread
的handleLaunchActivity
方法的,里面又调用了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