찾다

 >  Q&A  >  본문

Android 버그 - Android에서 인텐트 초기화를 메서드 외부에 작성할 수 없는 이유는 무엇입니까?

초보자. 오늘은 명시적 인텐트를 사용하여 활동 간을 이동하는 코드를 작성했지만 클래스에 인텐트 멤버 변수를 정의했는데 이를 메서드에서 사용하면 프로그램이 충돌하는 이유는 무엇입니까?

으아악

코드는 위와 같으나 오류 메시지는 다음과 같습니다.

원인: java.lang.NullPointerException: null 개체 참조에서 가상 메서드 'java.lang.String android.content.Context.getPackageName()' 호출 시도

"null 개체 참조"에서 getPackageName() 가상 메서드를 호출할 때 오류가 발생했다고 합니다.

처음에는 이것이 비어 있어서 문제가 발생한다고 생각하여 코드를 수정했습니다.

으아악

하지만 여전히 오류가 발생하고 오류 메시지도 동일합니다. 조건부 판단을 통해 이것이 비어 있지 않다는 것을 알고 있는데 왜 여전히 널 참조라고 하는 걸까요?

伊谢尔伦伊谢尔伦2742일 전913

모든 응답(3)나는 대답할 것이다

  • phpcn_u1582

    phpcn_u15822017-05-16 13:37:09

    이 작업을 수행하기 전에 먼저 ActivityContext 사이의 관계를 이해해야 합니다. ActivityContext를 상속하지만 > 하지만 실제 구현 클래스는 반환된 객체에 해당하는 클래스일 수 있습니다. ActivityContext之间的关系: 虽然Activity继承了Context, 但是它却不是真正的实现类, 真正的实现可能是ContextWrapper#getBaseContext()返回对象所对应的类.

    ActivityContextWrapper的子类, 所以我们先找到并打开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

    ActivityContextWrapper의 하위 클래스이므로 먼저 ContextWrapper.java 소스 코드를 찾아서 엽니다. 🎜 으아아아 Context를 기반으로 하는 🎜ContextWrapper 호출은 mBase를 직접 사용하여 호출을 간접적으로 구현합니다. 그러면 이 mBase는 언제였습니까? 할당되었나요? ActivityThread.java를 찾아 열면 할당된 코드 부분을 찾을 수 있습니다. 🎜 으아아아 🎜위에서 알 수 있듯이 Activity를 인스턴스화할 때 ContextWrapper#getBaseContext()는 null을 반환하므로 생성자에서 Context와 관련된 모든 함수 및 클래스의 인스턴스화. 필요한 경우 라이프 사이클 함수에서 호출하세요.🎜

    회신하다
    0
  • 伊谢尔伦

    伊谢尔伦2017-05-16 13:37:09

    클래스는 Activity 클래스를 상속하고 라이프 사이클을 가지며 모든 로직은 이러한 라이프 사이클에서 수행됩니다. 즉, 로직 코드는 해당 라이프 사이클의 메서드로 작성되어야 합니다. onCreate 메소드를 작성하고 거기에 페이지 점프를 작성하면 메소드의 식별자 이름도 표준화되지 않습니다

    회신하다
    0
  • 天蓬老师

    天蓬老师2017-05-16 13:37:09

    onCreate() 메서드를 재정의해야 합니다

    회신하다
    0
  • 취소회신하다