【问题标题】:Why does Android give the "Attempt to invoke virtual method on a null object reference" error when the object is definitely not null?当对象绝对不为空时,为什么Android会给出“尝试在空对象引用上调用虚拟方法”错误?
【发布时间】:2019-11-11 18:52:20
【问题描述】:

我有一个 RecyclerView.Adapter 派生类型,它应该通过 http 将图像下载到设备。为此,我将ContentDownloadService 传递给它,它扩展了IntentService.

所以在我的MainActivity.kt 我这样做:

class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

    private val Issues: ArrayList<Issue> = ArrayList<Issue>()

    private val downloadService: ContentDownloadService = ContentDownloadService()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        loadConfiguration();
        val issueCards: RecyclerView = findViewById(R.id.issues_list)
        issueCards.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
        issueCards.adapter = IssueAdapter(Issues.toTypedArray(), this, downloadService)

    }
}

ContentDownloadService 是一个相当通用的 IntentService - 据我所知,它在这里并不真正相关,因为它从未被调用过。

IssueAdapter 我们这样做:

class IssueAdapter(private var issues: Array<Issue>, private val context: Context, private val downloadService: ContentDownloadService) : RecyclerView.Adapter<IssueAdapter.IssueViewHolder>() {


    class IssueViewHolder(public val cardView: CardView) : RecyclerView.ViewHolder(cardView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int ) : IssueAdapter.IssueViewHolder {
        val issueView = LayoutInflater.from(parent.context).inflate(R.layout.issue_card_view, parent, false)as CardView;
        return IssueViewHolder(issueView);
    }

    override fun onBindViewHolder(holder: IssueViewHolder, position: Int) {
        val issueView:CardView = holder.cardView
        val title : TextView = issueView.findViewById(R.id.issue_title)
        val image: ImageView = issueView.findViewById(R.id.issue_cover_image))
        title.text = issues[position].title
        description.text = issues[position].description
        date.text = format.format(issues[position].date)
        if ( issues[position].imagePath != null) {
            downloadImage(position)
        }
    }

 private fun downloadImage( position : Int ){
        val downloadIntent = Intent()
        val resultReceiver =  object: ResultReceiver(Handler()){
            override fun onReceiveResult(resultCode: Int, bundleResultData : Bundle)
            {
                if ( resultCode == 0 ) {
                    issues[position].imagePath = bundleResultData.getString("path")
                }
            }
        };
        downloadIntent.putExtra(ContentDownloadService.IMAGE, issues[position].imagePath)
        downloadIntent.putExtra(ContentDownloadService.RECEIVER, resultReceiver)
        downloadService.startActivity(downloadIntent);
    }
}

当我尝试在调试器中运行它时,我收到以下消息:

    java.lang.NullPointerException: Attempt to invoke virtual method 'void android.content.Context.startActivity(android.content.Intent)' on a null object reference
        at android.content.ContextWrapper.startActivity(ContextWrapper.java:379)
        at com.myapp.models.IssueAdapter.downloadImage(IssueAdapter.kt:72)

使用调试器进行单步调试显示downloadService 在调用时绝对是一个真实的对象

为什么 Android 在调用时将我的服务视为 null,但在调试器中显示为正确?

【问题讨论】:

    标签: android kotlin android-intent


    【解决方案1】:
    private val downloadService: ContentDownloadService = ContentDownloadService()
    

    您不能仅通过调用构造函数来实例化 Android 生命周期对象。您需要让框架为您初始化它们。对于Services,这意味着调用startService() 或其变体之一。

    单步调试,发现downloadService在被调用时肯定是一个真实的对象。

    为什么 Android 在调用时将我的服务视为 null,但在调试器中显示为正确?

    从堆栈跟踪中可以看出,它不是 null。该对象中的方法因 NPE 而失败,因为对象本身未正确初始化。特别是,ContextWrapper 中的 mBase 基本上下文在此处为空。

    【讨论】:

    • 我打赌就是这样!我一直忙于学习 Kotlin,以至于忘记了我的 Android 基础知识!
    【解决方案2】:

    为此,我将ContentDownloadService 传递给它,它扩展了IntentService

    您永远不应该自己创建 android 框架对象,因为它们包含很多内部状态,如果以这种方式创建它们,它们将不会被初始化。通过使用val downloadService: ContentDownloadService = ContentDownloadService() 创建它,它会留下一些内部Context 变量未设置,这会导致您的错误。

    【讨论】:

      【解决方案3】:

      startActivity 是一种异步方法,因此您可能会在 IntentService 能够启动活动之前失去它的范围。你可以试试

      downloadService.applicationContext.startActivity(downloadIntent);
      

      但是,您应该考虑在这里重新考虑您的整个方法。通常你只想从另一个活动中启动一个活动。如果您的应用程序在前台并且被调用,系统可能会崩溃并显示一个错误,指出未由其他活动启动的活动必须将 NEW_TASK 标志添加到它们的意图中。原因是您将在当前 UI 的上下文之外启动 Activity,因此将终止当前任务并使用提供的 Activity 重新启动它。

      【讨论】:

        猜你喜欢
        • 2023-03-13
        • 1970-01-01
        • 1970-01-01
        • 2015-03-24
        • 1970-01-01
        • 1970-01-01
        • 2021-08-02
        • 2018-12-08
        • 1970-01-01
        相关资源
        最近更新 更多