Leanplum / Leanplum-Android-SDK

Leanplum's integrated solution delivers meaningful engagement across messaging and the in-app experience.
https://www.leanplum.com
Apache License 2.0
46 stars 40 forks source link

com.leanplum.actions.internal.ActionDefinition.getName() - NullPointerException #533

Closed michalkierasinski closed 1 year ago

michalkierasinski commented 1 year ago
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.leanplum.actions.internal.ActionDefinition.getName()' on a null object reference
       at com.leanplum.actions.internal.ActionManagerDefinitionKt.defineAction(ActionManagerDefinition.kt:87)
       at com.leanplum.messagetemplates.MessageTemplates.register(MessageTemplates.java:123)
       at com.leanplum.messagetemplates.MessageTemplates.registerTemplate(MessageTemplates.java:82)

Expected Behavior

The application is not crashing

Actual Behavior

Application is crashing

Steps to Reproduce the Problem

  1. Open the application with some custom action

Specifications

michalkierasinski commented 1 year ago

Probably connected with:

Fatal Exception: java.util.ConcurrentModificationException:
       at java.util.ArrayList$Itr.next(ArrayList.java:860)
       at com.leanplum.actions.internal.ActionManagerDefinitionKt.defineAction(ActionManagerDefinition.kt:130)
       at com.leanplum.messagetemplates.MessageTemplates.register(MessageTemplates.java:123)
       at com.leanplum.messagetemplates.MessageTemplates.registerTemplate(MessageTemplates.java:82)
hborisoff commented 1 year ago

@michalkierasinski Can you share your custom action definition and which thread executes it?

I would suggest using the UI thread when registering the actions to avoid such issues. The action queue mechanism is triggered on the UI thread, which probably is the cause of the crash. The heavy network operations are executed in a background threads.

michalkierasinski commented 1 year ago
class OpenAction @Inject constructor() : MessageTemplate {

    override fun getName() = ACTION_NAME

    override fun createActionArgs(context: Context): ActionArgs = ActionArgs().with(URL, "")

    override fun present(actionContext: ActionContext): Boolean {
        val url: String = actionContext.stringNamed(URL)

        if (!deepLinkManager.isLinkSupported(url)) {
            Timber.e("Deep Link is not supported")
            return false
        }

        // TODO: Open deep link
        actionContext.actionDismissed()
        return true
    }

    override fun dismiss(context: ActionContext): Boolean = true

    private companion object {
        const val ACTION_NAME = "Action Name"

        const val URL = "URL"
    }
}

Actions are registered on a different thread than UI.

hborisoff commented 1 year ago

Thanks, I will rise a bug about that, meanwhile could you use the UI thread when registering them. The registration is a light operation, that doesn't involve any network requests, and wouldn't freeze the UI thread.

michalkierasinski commented 1 year ago

Ok, thanks. Please fix it or update the documentation.

hborisoff commented 1 year ago

@michalkierasinski Fixed in #534 and will be included in next release. Thank you for your feedback!