【问题标题】:NullPointerException in Service's constructor服务构造函数中的 NullPointerException
【发布时间】:2014-02-01 00:47:05
【问题描述】:

在我的 Android 项目中,我有一个 Service

public class MyService extends Service{

    //I defined a explicite contructor 
    public MyService(){
       //NullPointerException here, why?
       File dir = getDir("my_dir", Context.MODE_PRIVATE);
    }

    @Override
    public void onCreate(){
      super.onCreate();
      ...
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
        ...
    }

}

我通常知道,我不应该为 Android Service 显式定义构造函数。但就我而言,我想使用构造函数在我的内部存储中创建一个目录。

当我尝试它时,我得到了 NullPointerException,我不明白这是为什么?谁能向我解释为什么我在显式构造函数中调用 getDir(...) 时得到 NullPoineterException 的原因?

【问题讨论】:

    标签: android android-service


    【解决方案1】:

    getDir()Context 中的一个方法。

    onCreate() 之前,该服务未设置为用作Context

    基本上,框架通过以下方式设置服务(和相关活动):

    1. 创建对象
    2. 设置Context,为当前配置提供适当的资源
    3. 在对象上调用onCreate()

    您不能做任何需要在第 1 步(即成员变量初始化或构造函数期间)正确设置对象Context

    【讨论】:

      【解决方案2】:

      您必须使用服务生命周期函数 onCreate()。 onCreate 函数在服务生命周期中仅运行一次,并用作构造函数。如果您希望它在任何地方更新,则可以将其放入 onStartCommand(Intent intent, int flags, int startId)。

      【讨论】:

        【解决方案3】:

        可能 Context 在您的服务构造函数中仍然为空,并且 getDir 正在使用 Context 进行其内部工作。您可以在以下来源查看:

        http://androidxref.com/4.4.2_r1/xref/frameworks/base/core/java/android/content/ContextWrapper.java#244

        243    @Override
        244    public File getDir(String name, int mode) {
        245        return mBase.getDir(name, mode);
        246    }
        

        所以如果 mBase 为空,那么你会得到 NPE

        mBase 将在构造函数中设置:

        56    public ContextWrapper(Context base) {
        57        mBase = base;
        58    }
        

        服务扩展了 ContextWrapper,但服务构造函数直接调用这个构造函数,值为空:

        http://androidxref.com/4.4.2_r1/xref/frameworks/base/core/java/android/app/Service.java#294

        294    public Service() {
        295        super(null);
        296    }
        

        所以你必须等待 Service.onCreate(),或者(如果可以的话)使用一些全局已知的上下文,比如来自你的应用程序对象。

        【讨论】:

          【解决方案4】:

          正如@laalto 所说,直到 onCreate()..

          所以将Context设为一个全局变量。

          Context ctx;
          

          还应该为您的 MyService 类创建构造函数。

          public MyService(Context mContext){
          
             this.ctx = mContext;
             File dir = ctx.getDir("my_dir", Context.MODE_PRIVATE);
          }
          

          【讨论】:

          • 这是否意味着我必须通过 MyService myService = new MyService(getApplicationContext()) 显式创建我的服务实例?如果是这样,当我启动服务时,它是否使用相同的实例?
          • 不..你不喜欢这个...你必须使用 as new MyService(this);在你的活动中
          • 不,你不明白我的问题,我的意思是你怎么能保证当我在活动中启动我的服务时,它会使用这个明确创建的实例?比如我通过 startService(new Intent(this, MyService.class)) 启动服务,我需要使用 new MyService(this) 创建的实例,如何保证?
          • 不,当您使用 Intent 启动服务时,您不需要它。但是,每当您调用我们的服务并执行某些任务时,您都必须为其创建一个 Context 变量。有关更多规范,请阅读 laalto 所说的内容。
          • 好的,那么至少会创建 2 个 MyService 实例。一种是调用服务执行任务,另一种是系统启动服务时创建的。这不是我想要的。但无论如何,谢谢你让我清楚。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-22
          • 2021-09-10
          • 2023-03-08
          • 2014-08-01
          • 1970-01-01
          相关资源
          最近更新 更多