搜索

首页  >  问答  >  正文

安卓bug - Android中,intent初始化为什么不能写在方法外面呢?

初学者。今天写一个活动之间跳转的代码,用到了显式的intent,但是我在类中定义了一个intent的成员变量,在方法中使用时候,程序却崩溃了,这是为什么呢?

public class MusicPlay extends Activity{
    //下面这一句初始化出了错误
    public Intent intent=intent=new Intent(this,MusicServer.class);
    ....

如上代码,但是错误信息提示是:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference

据说是在“空对象引用”上调用getPackageName()虚方法出现了错误。

起初,我以为是不是this是空从而导致问题,修改了代码:


public class MusicPlay extends Activity{
    Intent intent;
    public MusicPlay(){
        super();
        if(this!=null){
            intent=new Intent(this,MusicServer.class);
        }
    }
    ....

但是仍然出现了错误,错误信息还是一样的。通过条件判断我知道this不为空,那么为什么还说是空引用呢?

伊谢尔伦伊谢尔伦2773 天前937

全部回复(3)我来回复

  • phpcn_u1582

    phpcn_u15822017-05-16 13:37:09

    这么做之前, 先要了解清楚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相关的任何函数和类的实例化. 如果需要, 则在其生命周期函数中去调用.

    回复
    0
  • 伊谢尔伦

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

    你的类继承了Activity类,那么它就有生命周期,所有逻辑都在这几个生命周期里面进行,换而言之,你的逻辑代码都要写在那几个生命周期的方法里面.一般来说,都是重写onCreate的方法,在那里面写页面跳转.你的方法的标识符命名也不规范

    回复
    0
  • 天蓬老师

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

    要重写onCreate()方法啊

    回复
    0
  • 取消回复