一个Android App在运行时会自动建立全局惟一的Application
对象,是用来维护应用全局状态的基类,它的生命周期等于应用的生命周期。java
public class Application extends ContextWrapper implements ComponentCallbacks2 {
public LoadedApk mLoadedApk;
public Application() {
super(null);
}
}
复制代码
Application
拥有一个LoadedApk
类型的成员变量,这个对象其实就是APK文件在内存中的表示。APK文件的相关信息,诸如其中的代码和资源,甚至Activity、Service等四大组件的信息均可以经过此对象获取。继续看构造函数,它直接调用了父类ContextWrapper
的构造方法:android
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
}
复制代码
ContextWrapper是Context的包装类,mBase就是它的成员变量,除了经过构造函数初始化,也能够经过attachBaseContext
方法初始化:git
public class ContextWrapper extends Context {
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
}
复制代码
上述方法被Application
的attach
方法用来实现实现了如下两个功能:①将新建立的ContextImpl对象赋给Application的父类成员变量mBase;②将新建立的LoadedApk对象赋给Application的成员变量mLoadedApk。缓存
public class Application extends ContextWrapper implements ComponentCallbacks2 {
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
}
复制代码
这个方法由于是被系统调用的,因此是一个隐藏的final方法,咱们没法重写。具体来讲,它被系统的Instrumentation
类所调用:app
public class Instrumentation {
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
app.attach(context);
return app;
}
static public Application newApplication(Class<?> clazz, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
}
复制代码
newApplication
方法被LoadedApk
类所调用:ide
public final LoadedApk{
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
//保证了一个LoadedApk对象只建立一个对应的Application对象
if (mApplication != null) {
return mApplication;
}
...
Application app = null;
...
try {
//获取类加载器
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
initializeJavaContextClassLoader();
}
//建立ContextImpl对象
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
//建立Application对象
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
...
}
mActivityThread.mAllApplications.add(app);
//将刚建立的Application对象赋值给mApplication变量
mApplication = app;
...
return app;
}
}
复制代码
可见,Application的父类方法attachBaseContext(Context context)
中传入的实参实际上是经过ContextImpl.createAppContext(mActivityThread, this)
这句话所建立的ContextImpl对象。并且,加载和构造出Application对象的类加载器实际上是由LoadedApk.getClassLoader()
方法建立获得的,这里就不展开讲述了。紧接着,ActivityThread
类调用了LoadedApk的makeApplication
方法来初始化Application信息:函数
public final class ActivityThread extends ClientTransactionHandler {
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
···
} else { //system进程才执行该流程
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
try {
//建立Instrumentation
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
//建立ContextImpl
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
//建立Application
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
//回调Application的onCreate方法
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
}
}
private void handleBindApplication(AppBindData data) {
···
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
//建立Instrumentation对象
if (ii != null) {
···
final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
···
final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,appContext.getOpPackageName());
···
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
···
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
}
Application app;
// 此处data.info是指LoadedApk, 经过反射建立目标应用Application对象
app = data.info.makeApplication(data.restrictedBackupMode, null);
···
mInitialApplication = app;
···
mInstrumentation.callApplicationOnCreate(app);
···
}
}
public class Instrumentation {
public void callApplicationOnCreate(Application app) {
//回调Application的onCreate
app.onCreate();
}
}
复制代码
由于system_server进程和app进程都运行着一个或多个app,每一个app都会有且仅有一个对应的Application
对象(该对象和LoadedApk
对象一一对应)。 system_server进程是由ActivityThread.attach()
方法建立出Application对象的; 而普通app进程的则是由ActivityThread.handleBindApplication()
方法建立获得。性能
从上述代码调用流程能够看到,Application初始化完成后系统就会回调其onCreate
方法,该方法默认为空实现,咱们能够扩展Application
类,能够经过重写该方法再进行一些应用程序级别的资源初始化或临时全局共享数据的设置工做。学习
注册和注销ComponentCallbacks2
监听器,下文将具体进行介绍。测试
注册和注销对全部Activity
生命周期的监听器ActivityLifecycleCallbacks
,每当应用程序内Activity的生命周期发生变化时,监听器中相对应的接口方法就会被调用执行。
在应用程序结束时调用,但该方法只用于仿真机测试,真机上并不会调用。
ComponentCallbacks2
监听器Application已经实现了该监听器接口,如前文所述,能够经过registerComponentCallbacks()方法注册该监听器,也可使用unregisterComponentCallbacks()注销。这个监听器提供了如下三个接口方法:
这是由于系统在内存不足时会从
LRU Cache
中按照从低到高的顺序杀死进程,可是那些高内存占用的应用也会被优先杀死,以此来让系统更快地获取更多可用内存。因此若是可以及时下降应用的内存占用,就能够下降它在后台被杀掉的几率,用户返回应用时就能快速恢复。
调用时刻:当系统检测到当前进程适合进行无用内存的释放操做时。例如系统却已经没有足够的内存来维持目前全部的后台进程,而咱们程序正好处于后台状态。
TrimMemoryLevel:代表了系统在回调onTrimMemory方法时的内存状况等级:
①TRIM_MEMORY_RUNNING_MODERATE
:级别为5,应用程序处于前台运行,但系统已经进入了低内存的状态。
② TRIM_MEMORY_RUNNING_LOW
:级别为10,应用程序处于前台运行,虽然不会被杀死,可是因为系统当前可用内存很低,系统开始准备杀死其余后台程序,咱们应该释放没必要要的资源来提供系统性能,不然会影响用户体验。
③ TRIM_MEMORY_RUNNING_CRITICAL
:级别为15,应用程序处于前台运行,大部分后台程序都已被杀死,此时咱们应该尽量地去释听任何没必要要的资源。
④ TRIM_MEMORY_UI_HIDDEN
:级别为20,应用程序的全部UI界面已经不可见,很是适合释放UI相关的资源。
⑤ TRIM_MEMORY_BACKGROUND
:级别为40,应用程序处于后台,且在LRU缓存列表头部,不会被优先杀死,可是系统将开始根据LRU缓存来依次清理进程。此时应该释放掉一些比较容易恢复的资源提升系统的可用内存,让程序可以继续保留在缓存中。
⑥ TRIM_MEMORY_MODERATE
:级别为60,应用程序处于后台,且在LRU缓存列表的中部,若是系统可用内存进一步减小,程序就会有被杀掉的风险。
⑦ TRIM_MEMORY_COMPLETE
:级别为80,应用程序处于后台,且在LRU缓存列表的尾部,随时会被系统杀死,此时应该尽量地把一切能够释放的资源释放掉。
除了Application,能够实现onTrimMemory
回调的组件还有Activity、Fragement、Service、ContentProvider。
该接口在Android4.0之后被上述方法替代,因此做用和上述方法相似。 若应用想兼容Android4.0之前的系统就使用OnLowMemory,不然直接使用OnTrimMemory便可。须要注意的是,onLowMemory
至关于level级别为TRIM_MEMORY_COMPLETE
的OnTrimMemory
。
AndroidManifest.xml
文件 中为Activity标签配置的android:configChanges
属性值,例如android:configChanges="keyboardHidden|orientation|screenSize
可使该Activity在屏幕旋转时不重启,而是执行onConfigurationChanged方法。AndroidManifest.xml
文件中配置<application>
标签的android:name
属性,例如android:name=".MyApplication"
,MyApplicaiton就是自定义的Application类名。getApplication()
和getApplicationContext()
方法getApplication()
方法只存在于Activity
和Service
对象中,该方法可主动获取当前所在mApplication,这是由LoadedApk.makeApplication()
进行初始化的;
getApplicationContext()
是Context类的方法,因此Context的子类均可以调用该方法。先来看一下该方法的执行逻辑:
public abstract class Context {
public abstract Context getApplicationContext();
}
class ContextImpl extends Context {
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
}
//上述mPackageInfo的数据类型为LoadedApk
public final class LoadedApk {
Application getApplication() {
return mApplication;
}
}
//上述mMainThread为ActivityThread
public final class ActivityThread {
public Application getApplication() {
return mInitialApplication;
}
}
复制代码
从上述代码能够看出若是LoadedApk
非空,getApplicationContext()
方法返回的就是LoadedApk
的成员变量mApplication
,因此对于Activity或Service组件来讲, getApplication()
和getApplicationContext()
没有差异,由于它们的返回值彻底相同。
BroadcastReceiver和ContentProvider没法使用getApplication()
,可是可使用getBaseContext().getApplicationContext()
获取所在的Application,可是ContentProvider使用该方法有可能会出现空指针的问题, 状况以下: 当同一个进程有多个apk的状况下, 若是第二个apk是由provider方式拉起,那么 因为provider建立过程并不会初始化相应的Application,此时执行getContext().getApplicationContext()
就会返回空,因此对于这种状况须要作好判空处理。
在Context对象的attachBaseContext()
中调用getApplicationContext()
方法也会返回空,这是由于从前文对Application构造函数的分析可知,LoadedApk.mApplication
是在attachBaseContext()
方法执行以后才被赋值的。
参考文章: