./frameworks/base/core/java/android/content/Context.java
./frameworks/base/core/java/android/app/ContextImpl.java
./frameworks/base/core/java/android/app/Activity.java
./frameworks/base/core/java/android/app/ActivityThread.java
./frameworks/base/core/java/android/view/ContextThemeWrapper.java
./frameworks/base/core/java/android/content/ContextWrapper.java
--------------------------------------------------------------------------------
Activity與ContextImpl的關聯:
繼承關係:
public class Activity extends ContextThemeWrapper
public class ContextThemeWrapper extends ContextWrapper
public class ContextWrapper extends Context
class ContextImpl extends Context
Activity本身是一個Context類型的子類。
在ContextWrapper中間接實現了Context相關的方法,所以Activity也具有這些方法。
而在ContextWrapper中對Context相關的方法的實現實際上是調用mBase的相關方法的。
而這個mBase實際上就是ContextImpl的實例。
也就是說,所有Context子類型的對象調用Context相關方法時,實際上都是調用的ContextImpl實例的方法。
mBase的賦值是在ContextWrapper的以下方法中:
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
而這個方法的調用是在Activity的以下方法中:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
Object lastNonConfigurationInstance,
HashMap<String,Object> lastNonConfigurationChildInstances,
Configuration config) {
attachBaseContext(context);
而這個方法的調用是在ActivityThread的以下方法中:
private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent)
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
mInstrumentation.callActivityOnCreate(activity, r.state);
這個方法是在luncher一個Activity時調用的。
通過方法可以看出,系統首先創建一個Activity對象,然後構造相關的Application對象。
之後,創建ContextImpl對象,並調用init方法初始化相關成員,之後將其添加給Activity對象。
這樣,Activity就和ContextImpl對象關聯上了。
在之後會調用Activity相關的回調函數,比如onCreate()等。
--------------------------------------------------------------------------------
Android關於Intent對象創建時報異常相關調查
<示例代碼>
public class MyServiceActivity extends Activity {
private Button startSer1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startSer1 = (Button) findViewById(R.id.startSer1);
startSer1.setOnClickListener(btnListener);
}
private OnClickListener btnListener = new OnClickListener() {
private Intent intent = new Intent(MyServiceActivity.this, MyService.class);
@Override
public void onClick(View v) {
}
};
}
<問題>
程序在加載時會報異常,位置是 private Intent intent = new Intent()。
而將該Intent對象的創建放到onClick中就沒有問題。
<分析>
1、需要理解Intent對象的創建過程:
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
public ComponentName(Context pkg, Class<?> cls) {
mPackage = pkg.getPackageName();
mClass = cls.getName();
}
可以看到,最後會調用pkg對象的getPackageName方法。
而getPackageName方法的實現是在ContextImpl中:
public String getPackageName() {
if (mPackageInfo != null) {
return mPackageInfo.getPackageName();
}
throw new RuntimeException("Not supported in system context");
}
這裏又調用了mPackageInfo成員的getPackageName方法。
而mPackageInfo成員是在ContextImpl的init方法中賦值的:
final void init(ActivityThread.PackageInfo packageInfo,
IBinder activityToken, ActivityThread mainThread,
Resources container) {
mPackageInfo = packageInfo;
而這個方法是在創建完Activity的實例之後,創建Application信息時調用的:
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
mInstrumentation.callActivityOnCreate(activity, r.state);
也就是說,如果要創建一個Intent對象,必須在ContextImpl對象的init方法運行完之後纔可以。
而Activity對象的創建明顯處在ContextImpl對象的init方法調用之前。
而Activity的onCreate方法的調用則處在ContextImpl對象的init方法調用之後:
public void callActivityOnCreate(Activity activity, Bundle icicle) {
activity.onCreate(icicle);
2、出錯原因主要是,Intent對象的創建是在Listener對象創建時創建的,而它的創建又依賴於Activity對象:
private Intent intent = new Intent(MyServiceActivity.this,
MyService.class);
(該方法中的第一個參數是一個Context的實例)
Listener對象是在Activity創建時創建的,:
private OnClickListener btnListener = new OnClickListener() {
而此時,Activity對象還沒有創建完成,ContextImpl的init方法還沒有被調用。
所以,此時創建Intent對象時,會因爲沒有mPackageInfo成員而創建失敗。
所以,整個程序在啓動的過程中,當創建到Activity對象的Listener對象的Intent對象時就直接報空指針異常了。
3、正確的做法是:
1、要麼將Listener對象的創建放到onCreate中。
2、要麼將Intent的創建放到onCreate中或者onClick中。
當然,還存在其它修改方法,但都大同小異。
而本質都是在創建Intent對象時,保證Activity的相關環境已經初始化完成。
通過以上分析可以看出,嚴格遵循Activity的生命週期進行編程才能寫出健壯不易出錯的代碼。
轉載自http://hi.baidu.com/gaogaf/blog/item/7efdb01185806264ca80c4d9.html