초보자. 오늘은 명시적 인텐트를 사용하여 활동 간을 이동하는 코드를 작성했지만 클래스에 인텐트 멤버 변수를 정의했는데 이를 메서드에서 사용하면 프로그램이 충돌하는 이유는 무엇입니까?
으아악코드는 위와 같으나 오류 메시지는 다음과 같습니다.
원인: java.lang.NullPointerException: null 개체 참조에서 가상 메서드 'java.lang.String android.content.Context.getPackageName()' 호출 시도
"null 개체 참조"에서 getPackageName() 가상 메서드를 호출할 때 오류가 발생했다고 합니다.
처음에는 이것이 비어 있어서 문제가 발생한다고 생각하여 코드를 수정했습니다.
으아악하지만 여전히 오류가 발생하고 오류 메시지도 동일합니다. 조건부 판단을 통해 이것이 비어 있지 않다는 것을 알고 있는데 왜 여전히 널 참조라고 하는 걸까요?
phpcn_u15822017-05-16 13:37:09
이 작업을 수행하기 전에 먼저
Activity
와Context
사이의 관계를 이해해야 합니다.Activity
는Context
를 상속하지만 > 하지만 실제 구현 클래스는 반환된 객체에 해당하는 클래스일 수 있습니다.Activity
与Context
之间的关系: 虽然Activity
继承了Context
, 但是它却不是真正的实现类, 真正的实现可能是ContextWrapper#getBaseContext()
返回对象所对应的类.
Activity
是ContextWrapper
的子类, 所以我们先找到并打开ContextWrapper.java
源码, 关键代码如下:
public class ContextWrapper extends Context {
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();
}
... ...
ContextWrapper
里基于Context
的调用都是直接使用mBase
来间接实现调用的. 那么这个mBase
是什么时候被赋值的呢? 找到并打开ActivityThread.java
, 就能找到它被赋值的代码部分, 关键代码如下:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
... ...
// -------------------------------------------------------------------
// 创建Activity实例
// -------------------------------------------------------------------
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
// -------------------------------------------------------------------
// 设置 appContext 为Activity 的 BaseContext
// -------------------------------------------------------------------
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);
以上, 可知: 实例化Activity
时, ContextWrapper#getBaseContext()
返回的是null, 因此, 不能在构造函数或者构造成员变量时直接调用与Context
Activity
는 ContextWrapper
의 하위 클래스이므로 먼저 ContextWrapper.java
소스 코드를 찾아서 엽니다. 🎜
으아아아
Context
를 기반으로 하는 🎜ContextWrapper
호출은 mBase
를 직접 사용하여 호출을 간접적으로 구현합니다. 그러면 이 mBase
는 언제였습니까? 할당되었나요? ActivityThread.java
를 찾아 열면 할당된 코드 부분을 찾을 수 있습니다. 🎜
으아아아
🎜위에서 알 수 있듯이 Activity
를 인스턴스화할 때 ContextWrapper#getBaseContext()
는 null을 반환하므로 생성자에서 Context와 관련된 모든 함수 및 클래스의 인스턴스화
. 필요한 경우 라이프 사이클 함수에서 호출하세요.🎜伊谢尔伦2017-05-16 13:37:09
클래스는 Activity 클래스를 상속하고 라이프 사이클을 가지며 모든 로직은 이러한 라이프 사이클에서 수행됩니다. 즉, 로직 코드는 해당 라이프 사이클의 메서드로 작성되어야 합니다. onCreate 메소드를 작성하고 거기에 페이지 점프를 작성하면 메소드의 식별자 이름도 표준화되지 않습니다