从标签中获取NDEF消息会冻结Android应用程序



我正在尝试从NFC标签读取NDEF消息。标签检测工作正常。为了从标签中读取NDEF数据,我运行了一个TimerTask。任务每900毫秒轮询来自标记的带有getNdefMessage()的NDEF消息,并更新UI。

程序工作完美,直到我拿掉手机。然后应用程序冻结,没有logcat错误消息。

有人知道为什么会这样吗?

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类
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;
                }
            }
        });
    }
}

当你删除一个标签时,你的应用程序阻塞的原因是你在应用程序的主线程(UI线程)中对标签执行IO操作。

首先创建一个定时器,在单独的线程

中处理它的计划任务。
_incomingMessageTimer = new Timer();
                        ^^^^^^^^^^^
_incomingMessageTimer.schedule(new MessageReceiveTimer(new Handler(), this), 0, 900);
                      ^^^^^^^^

然而,当TimerTask执行时(在Timer线程上)

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

通过在主线程的消息队列上发布一个Runnable,你可以立即将控制权返回给主线程:

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

处理程序的线程是主(UI)线程,因为你在主线程上创建了处理程序:

_incomingMessageTimer.schedule(new MessageReceiveTimer(new Handler(), this),0,900);
                                                       ^^^^^^^^^^^^^
因此,IO操作(_mainActivity._ndef.getNdefMessage())将阻塞主线程。请注意,Android文档明确地说这个方法"不能从主应用程序线程调用"(见这里)。这同样适用于connect()方法,顺便说一下。

最新更新