Android Context理解

 Activity mActivity =new Activity(),为何不能简单new一个对象?

 Android程序不像Java程序同样,随便建立一个类,写个main()方法就能运行,Android应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境,在这个环境下,Activity、Service等系统组件才可以正常工做,而这些组件并不能采用普通的Java对象建立方式,new一下就能建立实例了,而是要有它们各自的上下文环境,也就是咱们这里讨论的Context。 Context在加载资源、启动Activity、获取系统服务、建立View等操做都要参与。 java

 Context究竟是什么 

一个Activity就是一个Context,一个Service也是一个Context。Android程序员把“场景”抽象为Context类,他们认为用户和操做系统的每一次交互都是一个场景,好比打电话、发短信,这些都是一个有界面的场景,还有一些没有界面的场景,好比后台运行的服务(Service)。一个应用程序能够认为是一个工做环境,用户在这个环境中会切换到不一样的场景,这就像一个前台秘书,她可能须要接待客人,可能要打印文件,还可能要接听客户电话,而这些就称之为不一样的场景,前台秘书能够称之为一个应用程序。android

源码中的注释

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */ 
public abstract class Context {
}
复制代码

它描述一个应用程序环境的信息(即上下文);是一个抽象类,Android提供了该抽象类的具体实现类;经过它咱们能够获取应用程序的资源和类(包括应用级别操做,如启动Activity,发广播,接受Intent等)。既然上面Context是一个抽象类,那么确定有他的实现类,咱们在Context的源码中经过IDE能够查看到他的子类最终能够获得以下关系图:
程序员


Context类自己是一个纯abstract类,它有两个具体的实现子类:ContextImpl和ContextWrapper。设计模式

    ContextWrapper类,如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。ContextThemeWrapper类,如其名所言,其内部包含了与主题(Theme)相关的接口,这里所说的主题就是指在AndroidManifest.xml中经过android:theme为Application元素或者Activity元素指定的主题。固然,只有Activity才须要主题,Service是不须要主题的,由于Service是没有界面的后台场景,因此Service直接继承于ContextWrapper,Application同理。bash

     而ContextImpl类则真正实现了Context中的全部函数,应用程序中所调用的各类Context类的方法,其实现均来自于该类。一句话总结:Context的两个子类分工明确,其中ContextImpl是Context的具体实现类,ContextWrapper是Context的包装类。Activity,Application,Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程当中都会建立ContextImpl对象,由ContextImpl实现Context中的方法
app

Context关键函数

public abstract class Context {

// 获取应用程序包的AssetManager实例
public abstract AssetManager getAssets();

// 获取应用程序包的Resources实例
public abstract Resources getResources();

// 获取PackageManager实例,以查看全局package信息
public abstract PackageManager getPackageManager();

// 获取应用程序包的ContentResolver实例
public abstract ContentResolver getContentResolver();

// 它返回当前进程的主线程的Looper,此线程分发调用给应用组件(activities, services等)
public abstract Looper getMainLooper();

// 返回当前进程的单实例全局Application对象的Context
public abstract Context getApplicationContext();

// 从string表中获取本地化的字符串
public final String getString(int resId) {
return getResources().getString(resId);
}

// 返回一个可用于获取包中类信息的class loader
public abstract ClassLoader getClassLoader();

// 返回应用程序包名
public abstract String getPackageName();

// 返回应用程序信息
public abstract ApplicationInfo getApplicationInfo();

// 根据文件名获取SharedPreferences
public abstract SharedPreferences getSharedPreferences(String name,
int mode);

// 其根目录为: Environment.getExternalStorageDirectory()
public abstract File getExternalFilesDir(String type);

// 返回应用程序obb文件路径
public abstract File getObbDir();

// 启动一个新的activity
public abstract void startActivity(Intent intent, Bundle options);

// 启动多个新的activity
public abstract void startActivities(Intent[] intents, Bundle options);

// 广播一个intent给全部感兴趣的接收者,异步机制
public abstract void sendBroadcast(Intent intent);

// 广播一个intent给全部感兴趣的接收者,异步机制
public abstract void sendBroadcast(Intent intent,String receiverPermission);

// 注册一个BroadcastReceiver,且它将在主activity线程中运行
public abstract Intent registerReceiver(BroadcastReceiver receiver,
IntentFilter filter);

public abstract void unregisterReceiver(BroadcastReceiver receiver);

// 请求启动一个application service
public abstract ComponentName startService(Intent service);

// 请求中止一个application service
public abstract boolean stopService(Intent service);

// 链接一个应用服务,它定义了application和service间的依赖关系
public abstract boolean bindService(Intent service, ServiceConnection conn,
int flags);

// 断开一个应用服务,当服务从新开始时,将再也不接收到调用,
// 且服务容许随时中止
public abstract void unbindService(ServiceConnection conn);

//检查权限
public abstract int checkPermission(String permission, int pid, int uid);
}复制代码
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
private final static String TAG = "ContextImpl";
private final static boolean DEBUG = false;

private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
new HashMap<String, SharedPreferencesImpl>();

/*package*/ LoadedApk mPackageInfo; // 关键数据成员
private String mBasePackageName;
private Resources mResources;
/*package*/ ActivityThread mMainThread; // 主线程

@Override
public AssetManager getAssets() {
return getResources().getAssets();
}

@Override
public Looper getMainLooper() {
return mMainThread.getLooper();
}

@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}

@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity)null, intent, -1, options);
}
}复制代码

什么时候建立Context

1) 建立Application 对象时(整个App共一个Application对象)异步

2) 建立Service对象时ide

3) 建立Activity对象时函数

在ActivityThread中建立application、service、activity对象时,以Activity启动为例,除了使用classLoader进行相关对象的初始化oop

public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
        @Nullable Intent intent)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (Activity) cl.loadClass(className).newInstance();
}复制代码

还会new一个context对象进行赋值,主要代码以下

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
//省略
    return appContext;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
        LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
        Configuration overrideConfiguration) {
    ClassLoader classLoader = packageInfo.getClassLoader();
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
            activityToken, null, 0, classLoader);

    final ResourcesManager resourcesManager = ResourcesManager.getInstance();

    // Create the base resources for which all configuration contexts for this Activity
    // will be rebased upon.
    context.setResources(resourcesManager.createBaseActivityResources(activityToken,
            packageInfo.getResDir(),
            splitDirs,
            packageInfo.getOverlayDirs(),
            packageInfo.getApplicationInfo().sharedLibraryFiles,
            displayId,
            overrideConfiguration,
            compatInfo,
            classLoader));
    context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
            context.getResources());
    return context;
}复制代码

ApplicationContext初始化,关键代码以下

//在每一个Activity建立时都会先执行,若是已经生成过application,就跳过
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }
    Application app = null;

    String appClass = mApplicationInfo.className;
//若是没有在清单文件制定application name,就默认设一个名字
    if (forceDefaultAppClass || (appClass == null)) {
        appClass = "android.app.Application";
    }

    try {
        java.lang.ClassLoader cl = getClassLoader();
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
        }
    }
    return app;
}复制代码

Service建立关键代码

private void handleCreateService(CreateServiceData data) {
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
//经过mClassLoader加载Service类,并调用期构造方法生成service对象
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } catch (Exception e) {
    }

    try {
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);//与application中的context一致
        context.setOuterContext(service);
//跟Activity同样,先判断是否已生成过Application
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
    }
}复制代码

通过源码分析,咱们知道Application、Activity和Service建立过程当中生成的context,最终都会赋值给ContextWrapper中的mBase。并且Application和Service对应的mBase彻底一致,Activity对应的mBase信息更丰富一些,不只包含package信息,还包含ActivityInfo(主题、window、启动模式等)。

getApplication() == getApplicationContext(),二者等价,值都为mApplication实例。

Android为何要提供两个功能重复的方法呢?做用域上有区别。getApplication()方法只有在Activity和Service中才能调用的到。getApplicationContext()属于ContextWrapper里方法。

一个应用程序有几个Context

Context数量=Activity数量+Service数量+1

Context做用域


Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog)

一句话总结:凡是跟UI相关的,都应该使用Activity作为Context来处理;其余的一些操做,Service,Activity,Application等实例均可以,固然了,注意Context引用的持有,防止内存泄漏。

正确使用Context

错误使用例子:

一、单例模式持有

二、静态view持有

般Context形成的内存泄漏,几乎都是当Context销毁的时候,却由于被引用致使销毁失败,而Application的Context对象能够理解为随着进程存在的,因此咱们总结出使用Context的正确姿式:
1:当Application的Context能搞定的状况下,而且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽可能不要在Activity中使用非静态内部类,由于非静态内部类会隐式持有外部类实例的引用,若是使用静态内部类,将外部实例引用做为弱引用持有。

本文参考:

juejin.im/entry/573b2…

相关文章
相关标签/搜索