Context 笔记

Context :上下文; 本文章专门对Context体系进行详细了解


一. Context 的类继承图
-- Context
        ---ContextImpl
        ---ContextWrapper
                     -----Application
                     -----ContextThemeWrapper
                                      ---Activity
                     -----Service
1.1 ContextImple负责Context核心功能的实现,而我们常见的Activity Context,Application Context …则是继承ContextWrapper(Wrapper:包装类后缀,由此可以推测,使用了包装设计模式,继承于Context,同时有一个Context的引用),以下是源码:
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
}    

通过以上的类图,我们的 Application、service、Activity都是继承于ContextWraper

二.Context初始化

2.1.Activity

ActivityThread.performLaunchActivity()是Activity的启动方法:

ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
        //创建了ContextImple
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
        ...


         appContext.setOuterContext(activity);
         activity.attach(appContext, xxxx);

获得LoadedAkp实例,接着调用createBaseContextForAcivity(),创建了ContextImple实例;通过Activitiy#attach(ContextImple,xxx)方法,让ContextWraper 获得ContextImpl 的引用

2.1.1 Q:ContextImple 怎么创建的?
  private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        final int displayId;
        try {
            displayId = ActivityManager.getService().getActivityDisplayId(r.token);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        //这里创建的
        ContextImpl appContext = ContextImpl.createActivityContext(xxxxx);



 static ContextImpl createActivityContext(xxxx....) 
{

ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);

     ......

        final ResourcesManager resourcesManager = ResourcesManager.getInstance();
   context.setResources(resourcesManager.createBaseActivityResources(activityToken,
                packageInfo.getResDir(),
                splitDirs,
                packageInfo.getOverlayDirs(),
                packageInfo.getApplicationInfo().sharedLibraryFiles,
                displayId,
                overrideConfiguration,
                compatInfo,
                classLoader));
        context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
                context.getResources());


}

2.1.2 Q:Activitiy 是如何attach(mBaseContext)的?

2
3
final void attach(context) {
    attachBaseContext(context);
}

protected void attachBaseContext() {
    super.attachBaseContext(newBase);
}
2.2 Q:Service是如何获取ContextImple 的引用的?

Service 是ActivityThread#handleCreateService()创建的

2.2.1 handleCreateService源码
handleCreateService()
{

LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            //反射得到的Service
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
        }

     try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());

     ....
     }Catch(Exp e){


    }


}

简单的说就是:
初始化LoadedApk–>ContextImpl.createAppContext(this, LoadedApk)获得ContextImpl–>反射获得的Servicea,attch获得该引用;

而且attach的方法也是调用ContextWrapper的方法
attach

public final void attach() {
    attachBaseContext(context);
}

三.Application

Application的创建是在LoadApk#makeApplication

3.1 makeApplication 的源码
public Application makeApplication(){

    if (mApplication != null) {
            return mApplication;
        }


    Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
        //反射的方法创建Applicaton
            appClass = "android.app.Application";
        }

 try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");

                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

//这里使用createApplicationContext 创建
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
}
}

貌似这里没有attach方法,查阅资料说在Instrumentation#newApplication()方法里面
newApplication

static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

/**
     * @hide APP类
     */
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

 protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

最后发现还是使用了ContextWrapper#attach 方法

creatApplication

static ContextImpl createAppContext() {
    ContextImpl context = new ContextImpl();
    context.setResources(packageInfo.getResources());
}
四.小结
  • Context 是最终父类,分别有主要实现类ContextImple,和使用包装设计模式的ContextWrapper,他需要ContextImpl的引用;
  • Activity,Service,Application的大致流程:

1.在首先需要LoadedApk,(前两者在各自方法中获得,后者直接在LoadedApk创建)

2.通过反射构建自身实

3.以LoadedApk作为参数构建ContextImple

4.最终都是调用ContextWrapper的attach(context)方法获得引用的(这里符合单一责任原则)

五.实践

5.1 View.getContext()返回当前View使用的Context实例,这个实例主要用于获取View所要使用的资源信息,那么这个Context实例是什么时候赋值的,使用的是那种类型的Context,有什么区别呢?
5.2 View的mContext 是LayoutInflater.from(context)传进去的
{
LayoutInflater layoutInflater = LayoutInflater.from(context);
View view = layoutInflater.inflate(R.layout.xxx,null);
}

public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =       (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}

进一步在ContextImpl实例中,由ContextImple实现

public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}


 @Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

可以看到最终的调用都转发给了 ServiceFetcher,而 SYSTEM_SERVICE_FETCHERS 则是存储着各个类型的 Fetcher 的实例

可以看出:
SYSTEM_SERVICE_FETCHERS 作为一个HashMap存储着Service名称和Fetcher的映射关系,而这种关系的注册是用过registerService()注册的;

进一步

LayoutInflater的实现类是PhoneLayoutInflater,同时Fetcher的实现类是CachedServiceFetcher
现在看CachedServiceFetcher.getService()

public final T getService(ContextImpl ctx) {
    final Object[] cache = ctx.mServiceCache;
    synchronized (cache) {
        Object service = cache[mCacheIndex];
        if (service == null) {
            try {
                service = createService(ctx);
                cache[mCacheIndex] = service;
            } catch (ServiceNotFoundException e) {

            }
        }
        return (T)service;
    }
}

以下是分享LayoutInflater中inflat

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        // mContext 是具体 ContextWrapper 实现类的实例,上文我们已经提到了
        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
        View result = root;

        try {
            // 解析 xml 中的节点

            final View temp = createViewFromTag(root, name, inflaterContext, attrs);
        } catch () {

        } finally {
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;
        }

        return result;
    }
}

所以我们是createViewFromTag的方法里面根据标签名称去实例化具体的View实例的
createViewFromTag(){
view = createView(name, null, attrs);
}
该方法又是调用creatview方法创建View实例的

public final View createView(String name, String prefix, AttributeSet attrs){
    // 缓存构造函数
    Constructor<? extends View> constructor = sConstructorMap.get(name);
    // 检查构造函数类加载器
    if (constructor != null && !verifyClassLoader(constructor)){
        constructor = null;
        sConstructorMap.remove(name);
    }
    Class<? extends View> clazz = null;
    try {

        // 获取 View 构造函数实例,并处理过滤情况
        if (constructor == null) {
            clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);

            if (mFilter != null && clazz != null) {
            // 检查是否过滤
            boolean allowed = mFilter.onLoadClass(clazz);
            if (!allowed) {
                failNotAllowed(name, prefix, attrs);
            }
            }
            constructor = clazz.getConstructor(mConstructorSignature);
            constructor.setAccessible(true);
            sConstructorMap.put(name, constructor);
        } else {
            if (mFilter != null) {
                // 存在过滤器
                Boolean allowedState = mFilterMap.get(name);
                if (allowedState == null) {
                    // 没有缓存过滤结果,新的类
                    clazz = mContext.getClassLoader().loadClass(                            
                    prefix != null ? (prefix + name) : name).asSubclass(View.class);
                    boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                    mFilterMap.put(name, allowed);
                    if (!allowed) {
                        failNotAllowed(name, prefix, attrs);
                    }
                } else if (allowedState.equals(Boolean.FALSE)) {
                    failNotAllowed(name, prefix, attrs);
                } 
            }
        }

        Object lastContext = mConstructorArgs[0];
        if (mConstructorArgs[0] == null) {
            // 如果还没设置 context 参数
            mConstructorArgs[0] = mContext;
        }
        Object[] args = mConstructorArgs;
        args[1] = attrs;
        // 反射创建 View 实例,这里调用的构造函数类型为 (Context,AttributeSet),从 args 可知
        final View view = constructor.newInstance(args); 
        if (view instanceof ViewStub) {
            // 使用相同的 Context 处理 ViewStub
            final ViewStub viewStub = (ViewStub) view;
            viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
        }
        mConstructorArgs[0] = lastContext;
        return view;
    } catech () {

    }
}

七.Application Context 和Activity Conteext 用在View Context中由什么不同的地方呢?

protected void onCreate(Bundle savedInstanceState) {
    // 使用两种 Context 创建两个 EditText
    addEditText(this);                   
    addEditText(getApplicationContext());
}
private void addEditText(@Nullable Context context) { 

    if (mRootLayout == null) {                        
        return;                                       
    }                                                 

    if (context == null) {                            
        return;                                       
    }                                                 

    EditText editText = new EditText(context);        
    mRootLayout.addView(editText);                    
    editText.setText(context.toString());                                                               
}

`ActivityContext EditText
ApplicationContext Editext

Activity Context 的Editext使用theme中的配置,回顾我们最开始的Context类图,Acitvity基础于ContextThemeWrapper,而Application则继承于ContextWrapper,那我们是不是可以猜测ContextThemeWrapper的继承类才会读取theme中配置信息?

ContextThemeWrapper中的源码

public Resources.Theme getTheme() {
    if (mTheme != null) {
        return mTheme;
    }
    if (mThemeResource == 0) {
        mThemeResource = R.style.Theme_AppCompat_Light;
    }
    initializeTheme();
    return mTheme;
}

private void initializeTheme() {
    final boolean first = mTheme == null;
    if (first) {
        mTheme = getResources().newTheme();
        Resources.Theme theme = getBaseContext().getTheme();
        if (theme != null) {
            mTheme.setTo(theme);
        }
    }
    onApplyThemeResource(mTheme, mThemeResource, first);
}

protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
    theme.applyStyle(resid, true);
}

mThem和mThemeResource都可以通过构造方法和set方法进行赋值,假如没有给mTheme赋值,只给mThemeResource赋值,那么上面代码将mThemeSource应用到ContextWrapper.getTheme()返回的Themes实例,从而获得新的Theme实例.Activitiy的启动流程是在ActivityThread.performLauncheActivity中,其中涉及了Theme的赋值:

int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
    activity.setTheme(theme);
}

八小结

  1. 当使用LayoutInflater从xml文件中inflate布局时,调用的是View(Context,Attributeset)构造函数,使用的Context实例跟LayoutInflater创建时使用的Context一样的,并且LayoutInflater会缓存在Context实例中,相同的Context实例多次调用一样的LayotuInflater实例
  2. Activity Context 会读取Theme的样式,而

九.getApplication()和getApplicationContext()

getApplicationContext()的存在是Android历史原因,getApplication()这个只存在Activity和Service类中,那么对于BroadcastRecervice和ContentProvider来说,只能使用getApplicationContext();

两者对比:
1.对于Activity和Service而已,没有区别
2.BroadcastReceiver只能通过getApplicationContext()获得Application
3.ContentProvider只能通过getApplicationContex(),但是有可能出现控制.当同个进程有多个apk的情况,对于第二个apk是由provider方式拉起,而provider创建过程中并不会出现初始化Application,此时调用getApplicationContext()是空置

Dialog Context
dialog实例的创建必须是Activity Context ,如果传进去别的,则乎异常

start Activity
在application 中是ContextImp实现的,如果使用Application启动Activity.在26上需要体检Intent.FLAG_ACTIVITY_NEW_TASK

总结:
1.Context的类图:ContextImp主要实现,ContextWrapper使用包装设计模式
2.Activity,Service,Applciation;构建过程基本相同,同时创建LoadedApk,发送创建实例,attack方法
3.View中的Context 就是Layoutinflater中的Context,通过Layoutinflater.from(context)中传间的.而LayoutInflater只有context相同,他就相同;
4.Activitiy 由于在ActivityThread.performLauncheActivity方法中setTo了Theme,所以View使用了该Context,会默认调用Theme的样式,而使用Application则不需
5.对于获得Context:Activity和Service没有区别;而BroadcastReceiver 和ContextProvider 只能getApplicationContext获得;后者在同一个进程多个apk并且使用Contentprovider拉起的时候,Context是空置
6.Dialog 需要Activity Context;
7.在Appllication 中启动startActivity;只在26版本时候异常,其他没问题

[参考]通过该文章写的笔记,以后争取水平越来越高,自己总结知识(https://linxiaotao.github.io/2018/04/12/%E7%86%9F%E6%82%89%E5%8F%88%E9%99%8C%E7%94%9F%E7%9A%84Context/)

猜你喜欢

转载自blog.csdn.net/qianyedoufulao/article/details/80447576
今日推荐