Вопрос

I have recently started developing in Android and have started a project using NFC tags with NDEF messages. I have managed to create a simple application which will read from text/plain NDEF records and write the data to the screen.

Now I am working on writing NDEF messages to tags. I seem to have it working but wonder whether or not it's best to write the tag on a thread other than the main UI thread?

Having recently started learning, I have been taught that doing (potentially) long-running tasks in the main thread is a no-no, and figured that writing to a tag would count as such. Putting the code to write to a tag inside a Runnable(), though, seems to cause the write to fail. So, my question is. am I wrong in my assumption that writing to a tag should be done off of the main thread? Or am I wrong to use a Runnable? And apologies if this has been answered before, I tried googling but couldn't find much on the topic.

I've included my example code:

The button handler:

public void writeButton(View view)
{
    //Some validation
    createMessageToWrite();
}

Creating the message:

public void createMessageToWrite()
{       
    EditText messageBox = (EditText)findViewById(R.id.toWrite);
    String toWrite = messageBox.getText().toString();
    if(toWrite.length() == 0)
        toWrite = "This is some text from My writer";
    NdefRecord textRecord = NdefRecord.createMime("text/plain", toWrite.getBytes());
    NdefRecord aar        = NdefRecord.createApplicationRecord(getPackageName());

    NdefMessage message = new NdefMessage(new NdefRecord[]{textRecord, aar});
    messageToWrite=message;
    Toast.makeText(getApplicationContext(), "Message made", Toast.LENGTH_SHORT).show();

    write();
}

And, finally, my code to write to the tag:

public void write()
{       
    Toast.makeText(getApplicationContext(),"Starting write. Touch tag",Toast.LENGTH_SHORT).show();
    sleep(3000);
//  Runnable r = new Runnable()
    //{
//      @Override
//      public void run()
//      {
            Toast.makeText(getApplicationContext(),"Starting runnable. Touch tag",Toast.LENGTH_SHORT).show();
            sleep(3000);

            if(messageToWrite == null)
            {
                Toast.makeText(getApplicationContext(),"Message is null",Toast.LENGTH_SHORT).show();
                return;
            }

            Toast.makeText(getApplicationContext(), "Trying", Toast.LENGTH_SHORT).show();
            sleep(3000);

            try
            {                   
                if(theTag != null)
                {
                    theTag.connect();//The tag is a global var set in onCreate() or onNewIntent()
                    theTag.writeNdefMessage(messageToWrite);
                    theTag.close();
                }
                else
                {
                    Toast.makeText(getApplicationContext(), "tag is null", Toast.LENGTH_SHORT).show();
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(), "IOException", Toast.LENGTH_LONG).show();
            }
            catch (FormatException e)
            {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(), "Format", Toast.LENGTH_LONG).show();
            }
            Toast.makeText(getApplicationContext(),"Written",Toast.LENGTH_SHORT).show();
        }
//      };
//      Thread t = new Thread(r);
//      t.start();
//  }

The code posted works, but uncommenting the Runnable() stuff causes the app to crash. Unfortunately, the system I'm working on isn't set up to allow for USB debugging from a device and I don't have permissions to install the drivers. So, until that's set up, I have to settle for the "The app has stopped working" message...

Many thanks for any and all help

EDIT: Grammar

Это было полезно?

Решение

First of all, without knowing what exception you get, I would assume that the crash/exception is not caused by doing the write operation in a separate thread but by showing a Toast from a non-UI thread.

Toasts must always be shown from the UI thread. You can overcome this issue by wrapping the Toast.makeText(...).show() call in something like the following:

runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(YourActivityClass.this, yourMessage, Toast.LENGTH_SHORT).show();
    }
});

Second, it is good practice to access NFC tags (or in general to do IO operations) in a separate thread.

Third, user interaction with an NFC tag is typically a very short action and a user will typically tap a tag only during the actual read, write, etc. operations. So saving the Tag handle and calling the write operation later upon additional user input (pressing a button) is generally a very bad idea (some rare exception to this may exist though).

A better approach would be to store the NDEF message to be written, set your application to some "write mode" state and prompt the user to tap the tag. Then, upon tag detection, start the write thread and let it write the previously saved message.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top