Getting NDEF message from tag freezes Android app

情到浓时终转凉″ 提交于 2019-12-24 20:48:19

问题


I'm trying to read an NDEF message from an NFC tag. The detection of the tag works correctly. For reading the NDEF data from the tag I'm running a TimerTask. The task polls the NDEF message from the tag with getNdefMessage() every 900 ms and updates the UI.

The procedure works perfect until I remove the phone. Than the app freezes without a logcat error message.

Does anyone have an idea why this happens?

package com.example.infcdemo;

import java.io.IOException;
import java.util.Timer;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.nfc.tech.NfcA;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;

public class MainActivity extends Activity
{
    protected NfcAdapter mAdapter;
    protected PendingIntent mPendingIntent;
    protected IntentFilter mIntentfilter;
    protected String[][] mTechLists;
    protected IntentFilter[] mFilters;
    protected NfcA nfca = null;
    protected Intent mIntent = null;
    protected Tag mTag;
    private boolean nfc_initialized = false;

    Tag myTag = null;
    Ndef _ndef = null;

    Timer _incomingMessageTimer;


    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (nfc_initialized == false) 
        {
            // Initialize NFC Specific
            mAdapter = NfcAdapter.getDefaultAdapter(this);
            mPendingIntent = PendingIntent.getActivity(this, 0,
                    new Intent(this, getClass())
                            .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 1);
            IntentFilter ndef = new IntentFilter(
                    NfcAdapter.ACTION_TAG_DISCOVERED);
            mTechLists = new String[][] { new String[] { NfcA.class.getName(), Ndef.class.getName(),
                    NdefFormatable.class.getName() } };
            mFilters = new IntentFilter[] { ndef, };
            nfc_initialized = true;
        }
    }

    public void updateIncomingMessage(String msg)
    {
        TextView txtView = (TextView) findViewById(R.id.txtReceive);
        txtView.setText(msg);
    }

    @Override
    protected void onStart()
    {
        super.onStart();

        _incomingMessageTimer = new Timer();
        _incomingMessageTimer.schedule(new MessageReceiveTimer(new Handler(), this),0,900);
    }

    @Override
    protected void onStop()
    {
        super.onStop();

        _incomingMessageTimer.cancel();
        _incomingMessageTimer.purge();

    }

    @Override
    public void onNewIntent(Intent intent) 
    {
        mIntent = intent;

        String action = intent.getAction();
        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) 
        {
            myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

            if(_ndef != null)
            {
                try 
                {
                    _ndef.close();
                    _ndef = null;
                } 
                catch (IOException e) 
                {
                    e.printStackTrace();
                }
            }

            if(_ndef == null)
            {
                _ndef = Ndef.get(myTag);

                try 
                {
                    _ndef.connect();
                } 
                catch (IOException e) 
                {
                    e.printStackTrace();
                }
            }
        } 
    }
    @Override
    public void onPause()
    {
        super.onPause();
        mAdapter.disableForegroundDispatch(this);
    }

    @Override
    public void onResume() 
    {
        super.onResume();
        mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters,
            mTechLists);
    }
}

//TimerTask Class

package com.example.infcdemo;
import java.util.TimerTask;

import android.nfc.NdefMessage;
import android.os.Handler;

public class MessageReceiveTimer extends TimerTask
{
    Handler _handler;
    MainActivity _mainActivity;

    MessageReceiveTimer(Handler handler, MainActivity mainActivity)
    {
        super();
        _handler = handler;
        _mainActivity = mainActivity;
    }

    @Override
    public void run() 
    {
        _handler.post(new Runnable() 
        {
            @Override
            public void run() 
            {
                try 
                {
                    if(_mainActivity._ndef != null)
                    {
                        NdefMessage ndefMsg = null;

                        if(_mainActivity._ndef.isConnected())
                            ndefMsg = _mainActivity._ndef.getNdefMessage();
                        else
                            ndefMsg = null;

                        byte[] ndefRecord = ndefMsg.getRecords()[0].getPayload();
                        String strMsg = new String(ndefRecord, "US-ASCII"); 
                        _mainActivity.updateIncomingMessage(strMsg);
                    }
                } 
                catch (Exception e)
                {
                    return;
                }
            }
        });
    }
}

回答1:


The reason your app blocks when you remove a tag is that you execute IO operations on the tag in your app's main thread (UI thread).

You first create a timer that processes its scheduled tasks in a separate thread

_incomingMessageTimer = new Timer();
                        ^^^^^^^^^^^
_incomingMessageTimer.schedule(new MessageReceiveTimer(new Handler(), this), 0, 900);
                      ^^^^^^^^

However, when the TimerTask executes (on the Timer thread)

public class MessageReceiveTimer extends TimerTask {
    @Override
    public void run() {
        // code excecution happens in the Timer thread
    }
}

you immediately return control to the main (UI) thread by posting a Runnable on its message queue:

_handler.post(new Runnable() {
    @Override
    public void run() {
        // code execution happens in the Handler's thread
    }
});

The Handler's thread is the main (UI) thread in your case as you created the handler on the main thread:

_incomingMessageTimer.schedule(new MessageReceiveTimer(new Handler(), this),0,900);
                                                       ^^^^^^^^^^^^^

Consequently the IO operation (_mainActivity._ndef.getNdefMessage()) will block the main thread. Note that the Android documentation explicitly says that this method "must not be called from the main application thread" (see here). The same applies to the connect() method, btw.



来源:https://stackoverflow.com/questions/25389126/getting-ndef-message-from-tag-freezes-android-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!