Context
1、ContextImpl和ContextWraper关系
ContextImpl最终会通过setOuterContext方法接收的ContextWraper对象
接收Activity的对象
ActivityThread .java
private Context createBaseContextForActivity(ActivityClientRecord r,final Activity activity) {
int displayId = Display.DEFAULT_DISPLAY;
try {
displayId =ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
} catch(RemoteException e) {
}
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
...
…
}
接收Service 对象
ActivityThread.java
private void handleCreateService(CreateServiceData data) {
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
接收Application对象
“`
ActivityThread.java
private void handleBindApplication(AppBindData data) {
…
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
/**
* Switch this process to density compatibility mode if needed.
*/
…
Application app = data.info.makeApplication(data.restrictedBackupMode,null);
mInitialApplication= app;
…
“`LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
…
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = “android.app.Application”;
}
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals(“android”)) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
…
ContextImpl实现了我们平常调用的很多和资源有一定关系的方法,比如getAssets(),getResource(),getSharedPreference()..其实ContextImpl只是packginfo的一个代理,最终的资源文件获取还得交由packgeInfo处理。每一个ContextImpl对象对应一个packginfo。
由于ContextImpl里面持有ActivityThread对象(),所以我们才能在context里面去打开一个activity,service等。
2 、Context中的内存泄漏
也正是context里面持有了如此重量级的一些对象,因此对context操作不当引起的内存泄漏时非常致命的,特别是activity做为context时,如果保存在了静态对象中时,很危险。
public class LeakManager {
private static LeakManager manager;
private Context mContext;
private static Context mContext2;
//这两个 context 谁可能会被泄漏?
private LeakManager() {
}
private static class SingleTone {
public static final LeakManager instance = new LeakManager();
}
public static LeakManager getInstance(Context context) {
mContext2 = context;
return SingleTone.instance;
}
public void setContext(Context context) {
mContext = context;
}
public Resources getResource() {
if (null == mContext)
return null;
return mContext.getResources();
}
}
//外部调用的时候 LeakManager.getInstance(context).setContext(activity);
//两个context都会被泄漏,因为LeakManager是静态事例,其持有mContext对象。
3、Context的一些使用场景:
Y :表示正常的使用习惯
N1:表示不推荐,但是不会出错,如果打开Activity的时候指定的conext非Activity那么该Activity只能使用系统默认的主题。
BroadCastReceiver其实并不是Context子类,但其onReceive方法里面可以获取context。
以下是onReceive中记录的context对象日志:
11-1702:52:47.006 7778-7778/com.example.ndh.myapplication3 D/ndh--: context=com.example.ndh.myapplication3.FloatService@bdf0d5c//这是代码中注册的,其context就是注册广播的context对象
11-1702:52:47.006 7778-7778/com.example.ndh.myapplication3 D/ndh--:action=android.intent.action.USER_PRESENT
11-1702:52:47.006 7778-7778/com.example.ndh.myapplication3 D/ndh--: user_present
11-1702:52:47.028 7778-7778/com.example.ndh.myapplication3 D/ndh--: context=android.app.ReceiverRestrictedContext@4c8d743//这是xml中注册的广播
11-1702:52:47.028 7778-7778/com.example.ndh.myapplication3 D/ndh--:action=android.intent.action.USER_PRESENT
4、Dialog同toast对比
Dialog的创建只能使用Activity,Dialog只能使用Activity的原因其实并不是context出现问题,而是Window相关的环节出现错误。
/
/创建dialog事例
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
...
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// dialog 就是一个没有令牌的应用窗口
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
//这里将appToken 设置为null 而activity的appToken是来源于ActivityThread
w.setGravity(Gravity.CENTER);
//显示dialog
public void show() {
...
mDecor = mWindow.getDecorView();
try {
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
} finally {
}
…
在非activity中通过getSystemService来获取WindowManger,获得的事例WindowManagerImpl里没有token,并且设置的appToken也为空,因此最终wms里面不会存在token,在添加view的时候会报错(viewRootImpl—setView()方法会抛异常)。但是如果是通过activity.getSystemService这种方式获得WindowManager,由于Activity复写了getSystemService方法,这个时候返回的是 当前activity所在的windowManager对象:
//Activity.java中
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
所以挂载dialog的窗体管理器最终使用的是当前activity的令牌token。
Toast同为弹窗为全局Context也能满足
这里涉及到窗体分类
1、应用型窗体 Activity Dialog等。 //应用型窗体 必须持有Token
2、子窗体 menu,popupwindow // 子窗体加载到父窗体上,必须有父窗体
3、系统窗体 alert,toast,输入法,等//没有父窗体,Token等限制