本文主要總結梳理一下,應用程式啟動, 以及應用中各種Context那些事兒,如有錯誤,歡迎指出~
一,應用程式建立
1,zygote建立新程式
Android 應用程式不能主動開啟一個程式,只能被動開啟程式。在Mainfest註冊四大元件時,可以指定執行的程式。在啟動該元件時,AMS首先會判斷該程式 是否已存在,如果不存在,則首先請求zygote程式建立該程式。
<service android:name=".ServiceName"
android:process=":processName"/>
複製程式碼
以啟動一個服務為例,程式會先呼叫ActivityManagerService 中的startService()方法,在該方法內部會呼叫到ActiveService的bringUpServiceLocked方法 啟動服務。
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
......
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (app != null && app.thread != null) {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
}
if (app == null && !permissionsReviewRequired) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingType, r.name, false, isolated, false)) == null) {
bringDownServiceLocked(r);
return msg;
}
}
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
return null;
}
複製程式碼
mAm.getProcessRecordLocked()方法 用於判斷當前程式是否已經存在,若不存在,則呼叫ActivityManagerService的startProcessLocked方法建立一個程式。最終會呼叫到Process的start方法。會請求zygote程式建立一個程式。
public static final ProcessStartResult start(...) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
複製程式碼
ZygoteProcess封裝了與zygote程式相關的socket通訊。zygote程式建立一個新程式後,會將該新程式的pid 返回當前系統程式中。
@GuardedBy("mLock")
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
......
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
複製程式碼
2,新程式向AMS釋出IBinder實體物件
新程式啟動後,首先會執行ActivityThread.main函式,在 thread.attach()方法中會通過Binder機制呼叫AMS的attachApplication方法,實際上就是通知AMS 新程式已建立好,並將自己的IBinder釋出到AMS中。
public static void main(String[] args) {
.....
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製程式碼
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
.....
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
.....
}
複製程式碼
3,建立新程式Application物件
當應用程式啟動,並向AMS釋出自己的IBinder實體物件後,AMS 呼叫attachApplication()方法建立應用的Application物件。其中,thread為應用程式在AMS程式的binder物件。
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
}
}
複製程式碼
@GuardedBy("this")
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
.....
thread.bindApplication(...)
......
return true;
}
複製程式碼
bindApplication()方法在應用程式的Binder執行緒中執行。 首先會封裝app資料到AppBindData,然後傳送Handler訊息,在主執行緒中執行handleBindApplication()方法。
public final void bindApplication(...) {
AppBindData data = new AppBindData();
.....
sendMessage(H.BIND_APPLICATION, data);
}
複製程式碼
public void handleMessage(Message msg) {
switch (msg.what) {
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
複製程式碼
private void handleBindApplication(AppBindData data) {
//初始化應用程式 基本資料
......
Application app;
//建立Appcalition物件
app = data.info.makeApplication(data.restrictedBackupMode, null);
......
//呼叫Application.onCreate()方法
mInstrumentation.callApplicationOnCreate(app);
......
}
複製程式碼
通過呼叫Instrumentation 中的newApplication()方法,通過反射建立Application物件,並呼叫 Application.attach(Context)生命週期回撥方法
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;
}
複製程式碼
至此完成Application的建立工作,由此也可得出,Application並不是與應用相對應,而是與應用程式一一對應。
4,建立service例項
AMS最終會同過Binder機制通知 剛剛建立的程式建立服務類例項,並呼叫服務相關的生命週期函式。
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
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());
service.onCreate();
}
複製程式碼
二,各種Context
1,簡介
Context是提供應用程式上下文的一個抽象類,可以用來訪問系統資源,系統服務等等。
public abstract class Context {
public abstract Resources getResources();
public abstract Context getApplicationContext();
public abstract String getSystemServiceName(Class<?> serviceClass);
public abstract void startActivity( Intent intent);
public abstract ComponentName startService(Intent service);
}
複製程式碼
ContextWrapper 是Context的實現類,同時接收一個ContextImp 物件 做靜態代理。
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
public Context getBaseContext() {
return mBase;
}
...
}
複製程式碼
ContextImpl 實現Context, 完成具體操作
class ContextImpl extends Context {
....
}
複製程式碼
2, Application 中的Context
Applicaiton繼承ContextWrapper, 具有儲存應用程式內的全域性變數、初始化操作、提供應用上下文的作用。
public class Application extends ContextWrapper implements ComponentCallbacks2 {
...
public Application() {
super(null);
}
final void attach(Context context) {
attachBaseContext(context);
}
public void onCreate() {
}
}
複製程式碼
應用程式建立Application, 並建立ContextImp物件通過attch賦值給mBase
public Application makeApplication(...){
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
}
複製程式碼
public Application newApplication(ClassLoader cl, String className, Context context) {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
複製程式碼
3, Activity 中的Context
Activity繼承ContextThemeWrapper類,而 ContextThemeWrapper繼承ContextWrapper,只是封裝了主題相關內容。
public class Activity extends ContextThemeWrapper{
......
public final Application getApplication() {
return mApplication;
}
final void attach(...) {
attachBaseContext(context);
mApplication = application;
}
}
複製程式碼
應用程式建立Activity
private Activity performLaunchActivity(...){
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
}
複製程式碼
4, Service 中的Context
Service繼承ContextWrapper .
public abstract class Service extends ContextWrapper {
public final void attach(...) {
attachBaseContext(context);
mApplication = application;
}
public final Application getApplication() {
return mApplication;
}
}
複製程式碼
建立Service
private void handleCreateService(CreateServiceData data) {
service = packageInfo.getAppFactory() .instantiateService(cl, data.info.name, data.intent);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
}
複製程式碼
5,各種獲取Context方法比較
在Activity 或Service中,都可以使用以下三種方法取得Context, 但無論是Application、Activity或Service中,實際發揮作用的都是baseContext中的 ContextImp物件。
//獲取物件是 當前Activity 或Service 本身,慎用,可能會引起記憶體洩漏
Context context = this;
//獲取物件是 當前Activity 或Service中的mBase, 即ContextImpl例項
Context baseContext = getBaseContext();
//以下兩種方法獲取的都是當前應用的Application物件, 可以放心使用
Application application = getApplication();
Context applicationContext = getApplicationContext();
複製程式碼
完~