android里每个服务启动时context到底从哪里来的呢
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android里每个服务启动时context到底从哪里来的呢相关的知识,希望对你有一定的参考价值。
参考技术A 1.android里每个服务启动时context到底从哪里来的呢????以mountservice的context为例在SystemServer.java的run函数中有如下代码
context = ActivityManagerService.main(factoryTest);
......
if (!"0".equals(SystemProperties.get("system_init.startmountservice")))
try
/*
* NotificationManagerService is dependant on MountService,
* (for media / usb notifications) so we must start MountService first.
*/
Slog.i(TAG, "Mount Service");
mountService = new MountService(context);
ServiceManager.addService("mount", mountService);
catch (Throwable e)
reportWtf("starting Mount Service", e);
由此可知,由systemserver启动的服务的context都来自ActivityManagerService
ActivityManagerService.java
public static final Context main(int factoryTest)
......
ActivityThread at = ActivityThread.systemMain();
......
Context context = at.getSystemContext();
......
return context;
由上可知,该context是由ActivityThread 创建的
ActivityThread .java
public ContextImpl getSystemContext()
synchronized (this)
if (mSystemContext == null)
ContextImpl context =
ContextImpl.createSystemContext(this);
LoadedApk info = new LoadedApk(this, "android", context, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
context.init(info, null, this);
context.getResources().updateConfiguration(
getConfiguration(), getDisplayMetricsLocked(
Display.DEFAULT_DISPLAY,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO));
mSystemContext = context;
//Slog.i(TAG, "Created system resources " + context.getResources()
// + ": " + context.getResources().getConfiguration());
return mSystemContext;
在ContextImpl .java中
static ContextImpl createSystemContext(ActivityThread mainThread)
final ContextImpl context = new ContextImpl();
context.init(Resources.getSystem(), mainThread, Process.myUserHandle());
return context;
由此可知所有systemserver启动的服务的context对象实际为一个ContextImpl对象
即ServerThread::run<-ActivityManagerService.main<-ActivityThread::getSystemContext<-ContextImpl.createSystemContext
2.对于一个apk中的context一般直接指向这个activity本身,因为activity本身即继承自context
Android开发中的Context
作为一个Android开发者,不知道你有没有发现,在我们的代码中有个很厉害的角儿–Context,这个角色上能启动Service、Activity,下能Get各种应用资源,所以这个Context到底是个什么“人物”?为什么他拥有这么大的权力能支配四大组件呢?
Context是什么?
Context翻译为:语境; 上下文; 背景; 环境,而我们在开发中说的这个“上下文”到底是什么意思呢?在开发中,我们可以理解为当前对象在程序中所处的一个环境,一个与系统交互的过程。Context提供了一个应用的运行环境,应用通过这个环境才可以完成资源获取、组件交互、启动服务等操作。
Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。
在Android中,一个Activity是Context,一个Service也是一个Context,一个Application也是一个Context。不信?那让我们来看看Context的关系图:
可以从这个关系图中看出,Activity、Service、Application都是Context的子类,而Activity和Service及Application的区别是他继承的是ContextThemeWrapper,而Service和Application继承的是ContextWrapper,光从名字上就可以看出,ContextThemeWrapper跟ContextWrapper的区别是多了Theme,也就是为什么Activity能够设置界面的原因。那有人会问了,楼主前面说的Context能支配四大组件,那BroadcastReceiver和ContentProvider呢?他们并不是Context的子类啊?是的,他们并不是Context的子类,但是他们却持有从其他地方传过去的Context对象。
我们看看源码,看看Context到底是什么东西?
public abstract class Context
/**
* File creation mode: the default mode, where the created file can only
* be accessed by the calling application (or all applications sharing the
* same user ID).
* @see #MODE_WORLD_READABLE
* @see #MODE_WORLD_WRITEABLE
*/
public static final int MODE_PRIVATE = 0x0000;
...
/** Return an AssetManager instance for your application's package. */
public abstract AssetManager getAssets();
/** Return a Resources instance for your application's package. */
public abstract Resources getResources();
/** Return PackageManager instance to find global package information. */
public abstract PackageManager getPackageManager();
/** Return a ContentResolver instance for your application's package. */
public abstract ContentResolver getContentResolver();
...
可以从源码看出,Context是一个抽象的类,声明了一些静态常量,以及一些访问当前包资源和启动Service、Activity的抽象方法。而从上面的关系图上来看,Activity和Service及Application都是Context的子类,那是不是他们来实现Context的那些抽象方法呢?通过查看源码发现,Activity、Service、Application以及他们的父类ContextThemeWrapper、ContextWrapper都没有真正地实现Context里的抽象方法,而是选择性地重写了Context的一些抽象方法。
ContextWrapper.java
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 AssetManager getAssets()
return mBase.getAssets();
@Override
public Resources getResources()
return mBase.getResources();
@Override
public PackageManager getPackageManager()
return mBase.getPackageManager();
...
Context还有另外一个子类–ContextImpl,这个看名字是用来实现Context,那我们看看它的源码:
ContextImpl.java
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context
...
final ActivityThread mMainThread;
final LoadedApk mPackageInfo;
private final IBinder mActivityToken;
private final UserHandle mUser;
private final ApplicationContentResolver mContentResolver;
private final String mBasePackageName;
private final String mOpPackageName;
private final ResourcesManager mResourcesManager;
private final Resources mResources;
...
static ContextImpl getImpl(Context context)
Context nextContext;
while ((context instanceof ContextWrapper) &&
(nextContext=((ContextWrapper)context).getBaseContext()) != null)
context = nextContext;
return (ContextImpl)context;
@Override
public AssetManager getAssets()
return getResources().getAssets();
@Override
public Resources getResources()
return mResources;
@Override
public PackageManager getPackageManager()
if (mPackageManager != null)
return mPackageManager;
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null)
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
return null;
...
可以看到,抽象方法的真正的实现是在ContextImpl里面,但是这里又有疑问了,看了源码的朋友都会发现,除了Application中的attach方法里
/**
* @hide
*/
/* package */ final void attach(Context context)
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
有用到ContextImpl,在Activity、Service包括ContextWrapper及ContextThemeWrapper中都没有创建ContextImpl的对象,那ContextImpl是怎么和Activity、Service挂钩的呢?这里就要谈到Activity的启动过程了,由于本篇文章只讲Context,所以在这里不会花大量篇幅讲Activity的启动过程,有兴趣的同学可以去看看Activity的启动过程及源码。Service、Activity的实际启动实现是在ActivityThread类里,下面是源码:
ActivityThread.java#handleCreateService
//创建Service
private void handleCreateService(CreateServiceData data)
...
try
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
//可以看到,ContextImpl对象在Service创建时进行关联
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,
ActivityManagerNative.getDefault());
service.onCreate();
...
ActivityThread.java#createBaseContextForActivity
/**
* 该方法在performLaunchActivity方法中被调用,而performLaunchActivity是启动Activity的方法
**/
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity)
...
//ContextImpl对象在Activity创建时关联
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
...
return baseContext;
ActivityThread.java#attach
//创建application
private void attach(boolean system)
...
try
mInstrumentation = new Instrumentation();
//当前Application创建时关联
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
catch (Exception e)
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
...
通过源码我们解答了上面的疑问,ContextImpl对象是在ActivityThread类里创建并且和在Activity、Service、Application创建时进行关联。
Context能做什么
Context的作用很强大,下面用一个表格来看看Context的作用
Context作用 | Application | Activity | Service |
---|---|---|---|
Show Dialog | No | Yes | No |
Start Activity | - | Yes | - |
Layout Inflation | - | Yes | - |
Start Service | Yes | Yes | Yes |
Send Broadcast | Yes | Yes | Yes |
Register Broadcast Receiver | Yes | Yes | Yes |
Load Resource Values | Yes | Yes | Yes |
可以看到,Activity对Context持有的作用域最广,这是因为Activity继承了ContextThemeWrapper,而ContextThemeWrapper在ContextWrapper的基础上增加了一些对界面的操作,使得Activity使用Context的作用更广。
使用Application和Service的Context进行Start Activity是不推荐的,Application和Service作为的Context是没有所谓的任务栈的,只有Activity作为的Context才有,如果使用非Activity的Context进行Start Activity,会报错
android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
需要标记FLAG_ACTIVITY_NEW_TASK,让启动Activity时给它创建一个新的任务栈,并且Activity作为SingleTask启动才能解决。
使用Application和Service的Context来Inflate Layout也是不推荐的,因为最终设置的样式是系统默认的样式,而自定义的样式在这里是无效的。
Activity、Application、Service使用Context有区别吗
答案是肯定的,在大多数情况下,Activity、Service、Application的Context功能都是通用的,因为Context的方法具体实现是ContextImpl,但是就如上面说的,一些关于UI的操作最好用Activity作为Context来处理,而除此之外,他们使用方法并没有什么区别。
getApplication和getApplicationContext有什么区别吗?
先看看这两个方法实现的源码:
ContextImpl.java#getApplicationContext
@Override
public Context getApplicationContext()
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
ActivityThread.java#attach
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
ActivityThread.java#getApplication
public Application getApplication()
return mInitialApplication;
可以看出,这两个方法返回的Application都是由包的信息(PackageInfo)来决定的,而一个应用只有一个包信息,所以在能正常获取到包信息的情况下,这两个方法获取到的是同一个对象,只是类型不一样。对于ActivityThread创建的Activity和Service,能调用getApplication,而对于BroadcastReceiver,只能用他持有的Context调用getApplicationContext获取Application实例。
Context引发的内存泄露
Context并不能随便乱用,用的不好有可能会引起内存泄露的问题,下面就示例两种错误的引用方式。
错误的单例模式
public class Singleton
private static Singleton instance;
private Context mContext;
private Singleton(Context context)
this.mContext = context;
public static Singleton getInstance(Context context)
if (instance == null)
instance = new Singleton(context);
return instance;
这是一个非线程安全的单例模式,instance作为静态对象,生命周期要长于普通的对象,假如一个Activity去getInstance传入this获得instance对象,常驻内存的Singleton一直持有你传入的Activity对象,即使Activity被销毁掉,但它的引用还存在于一个Singleton中,就不可能被GC掉,这样就导致了内存泄漏。
View持有Activity引用
public class MainActivity extends Activity
private static Drawable mDrawable;
@Override
protected void onCreate(Bundle saveInstanceState)
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = new ImageView(this);
mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
iv.setImageDrawable(mDrawable);
这个静态的Drawable对象间接引用了MainActivity的mContext,当MainActivity被销毁时,mDrawable还持有MainActivity的引用,也不能被GC掉,所以造成内存泄漏。
总结下来就是使用了长周期的静态类,使得短周期的被销毁后长周期的还持有短周期的引用,从而导致内存泄漏。
使用Context的正确姿势
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。(比如静态对象持有Activity,当Activity销毁时,静态对象不能释放引用)
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
以上是关于android里每个服务启动时context到底从哪里来的呢的主要内容,如果未能解决你的问题,请参考以下文章
曹工说Spring Boot源码(15)-- Spring从xml文件里到底得到了什么(context:load-time-weaver 完整解析)