How do you write an NDEF message from an Android app?
Open Ndef on a formatted tag, or NdefFormatable on a blank one, connect off the main thread, check that the tag is writable and that the message fits, then write the whole message in one call. Making the tag read only is a separate call, and the reference says plainly that it cannot be reverted.
Building the message
The NdefRecord class builds the common records for you: createUri from API level 14, createMime from API level 16 and createTextRecord from API level 21. Assembling the bytes by hand is where encoding mistakes start, and the helpers remove the need.
createMime lower cases the MIME type to follow Android's rules for intent filtering, so the type written to the tag already matches a lower case filter.
Put the record that matters first. Android dispatches on the first record, and a message that opens with a text record is dispatched as text/plain, whatever follows. What the records contain is covered on the message structure page.
What to check before writing
Three values come without touching the radio. isWritable() says whether the tag is read only, and getMaxSize() gives the largest NDEF message it accepts, in bytes. The message knows its own size: the NdefMessage reference documents getByteArrayLength() as the length it will occupy once serialised.
Compare the last two before calling anything that writes. A message one byte too long fails at the write, after the user has held the phone still for nothing, and the comparison costs nothing.
getCachedNdefMessage() can return null on a tag that is formatted but holds no message yet, which the reference calls the initialized state. That is a tag ready to write, not a tag that failed to read.
The write itself
connect(), then writeNdefMessage(), then close(), on a thread that is not the main one. The Ndef reference marks the write as blocking and as overwriting whatever message the tag held.
A blank tag goes through NdefFormatable.format() instead, which formats it and writes a first message in one call. The reference describes a multi step process that throws IOException if any step fails, so an exception says that the process stopped, not where.
Four exceptions call for four responses. FormatException means the message itself is malformed. TagLostException means the tag left the field. IOException covers other failures and a cancellation. SecurityException means the Tag object is stale, used after its tag left the field.
After a TagLostException, assume nothing. Read the tag again before retrying, since the write may have completed, failed or stopped partway, the case the halfway failure page covers from the user's side.
Read only is one way
makeReadOnly() sets the capability container to read only and, where the tag allows it, sets the lock bits permanently. The reference calls it a one way process that cannot be reverted.
Check canMakeReadOnly() first, and never tie the call to the same button as the write. NdefFormatable.formatReadOnly() does both in one call, which removes the last chance to verify the content before it is frozen.
Lock at the end of a verified run, if at all. When locking is the right decision is covered on the locking page.
Reading it back
Verify with getNdefMessage(), which reads the tag, and never with the cached message, which still describes the tag as it was before the write.
Then read the tag with something other than your code, and on the other platform. A bug that writes the wrong record type decodes it the same wrong way, and an iPhone can ignore a record your Android parser accepts.
Frequently asked questions
Can a small write run on the main thread?
No. The reference marks every write as a blocking operation that must not run on the main thread, whatever its size.
Why does writeNdefMessage throw FormatException?
The reference raises it when the NDEF message to write is malformed, so the fix is in the message, not in the tag.
Does NdefFormatable work on every blank tag?
No. It appears only on tags the phone knows how to format, and the reference says no set of tags is mandatory across devices.
Can a read only tag be made writable again?
No. The reference describes makeReadOnly as a one way process that cannot be reverted.