One is by defining android:onClick="someFunction" and another is by having a listener in the activity. 
Not exactly. Your choices are:
- Use - android:onClick, pointing to a method in the activity that hosts this widget, using the native implementation of this (what we have had since Android 1.6)
 
- Call - setOnClickListener()on the- View, passing in an implementation of an- OnClickListener
 
- Use the data binding framework to tie - android:onClickto an arbitrary method or an arbitrary lambda expression
 
Are they the same and if I can use them interchangeably. 
They are the same, insofar as both define behaviors to be invoked if the user clicks the widget.
Are there things I can do with one and not the other? Any limitations?
Option #1 is limited to methods implemented on the hosting activity. Option #2 and Option #3 are not. With those, you can implement the logic to be invoked on a click event as part of some UI controller or presenter, such as a fragment.
Is it just up to opinion which is better practice? 
Well, pretty much everything is up to opinion.
IMHO, Option #1 is fine for trivial apps, such as book examples. Option #3 is new but powerful, and I anticipate that this will be the long-term solution.