简体   繁体   English

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

[英]Getting NDEF message from tag freezes Android app

I'm trying to read an NDEF message from an NFC tag. 我正在尝试从NFC标签读取NDEF消息。 The detection of the tag works correctly. 标签的检测正常进行。 For reading the NDEF data from the tag I'm running a TimerTask. 为了从标签读取NDEF数据,我正在运行TimerTask。 The task polls the NDEF message from the tag with getNdefMessage() every 900 ms and updates the UI. 该任务每900毫秒使用getNdefMessage()从标签轮询NDEF消息,并更新UI。

The procedure works perfect until I remove the phone. 在我取下手机之前,该程序可以正常运行。 Than the app freezes without a logcat error message. 比应用程序冻结时没有出现logcat错误消息。

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

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). 删除标签时,您的应用程序阻塞的原因是您对应用程序的主线程(UI线程)中的标签执行了IO操作。

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) 但是,当TimerTask执行时(在Timer线程上)

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: 您可以通过在其消息队列上发布Runnable立即将控制权返回给主(UI)线程:

_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: 在您的情况下,在主线程上创建处理程序时,处理程序的线程是主(UI)线程:

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

Consequently the IO operation ( _mainActivity._ndef.getNdefMessage() ) will block the main thread. 因此,IO操作( _mainActivity._ndef.getNdefMessage() )将阻塞主线程。 Note that the Android documentation explicitly says that this method " must not be called from the main application thread " (see here ). 请注意,Android文档明确指出此方法“ 不得从主应用程序线程中调用 ”(请参见此处 )。 The same applies to the connect() method , btw. btw的connect()方法也是如此

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM