从标签获取NDEF消息冻结Android应用 [英] Getting NDEF message from tag freezes Android app

查看:268
本文介绍了从标签获取NDEF消息冻结Android应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从一个NFC标签读取NDEF消息。标签的检测工作正常。对于从标签读取的数据NDEF我跑一个TimerTask。任务民调来自标签的NDEF消息, getNdefMessage()每900毫秒,并更新用户界面。

程序完美的作品,直到我取出手机。比应用程序冻结没有logcat的错误消息。

有没有人有一个想法,为什么出现这种情况?

 包com.example.infcdemo;进口java.io.IOException异常;
进口java.util.Timer中;进口android.app.Activity;
进口android.app.PendingIntent;
进口android.content.Intent;
进口android.content.IntentFilter;
进口android.nfc.NfcAdapter;
进口android.nfc.Tag;
进口android.nfc.tech.Ndef;
进口android.nfc.tech.NdefFormatable;
进口android.nfc.tech.NfcA;
进口android.os.Bundle;
进口android.os.Handler;
进口android.widget.TextView;公共类MainActivity扩展活动
{
    保护NfcAdapter mAdapter;
    受保护的PendingIntent mPendingIntent;
    受保护的IntentFilter mIntentfilter;
    保护的String [] [] mTechLists;
    受保护的IntentFilter [] mFilters;
    保护NFCA NFCA = NULL;
    保护意图mIntent = NULL;
    保护特征MTAG;
    私人布尔nfc_initialized = FALSE;    标签MyTag的= NULL;
    NDEF _ndef = NULL;    定时器_incomingMessageTimer;
    @覆盖
    保护无效的onCreate(捆绑savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.activity_main);        如果(nfc_initialized == FALSE)
        {
            //初始化NFC具体
            mAdapter = NfcAdapter.getDefaultAdapter(本);
            mPendingIntent = PendingIntent.getActivity(这一点,0,
                    新的意图(这一点,的getClass())
                            .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),1);
            IntentFilter的NDEF =新的IntentFilter(
                    NfcAdapter.ACTION_TAG_DISCOVERED);
            mTechLists =新的String [] [] {新的String [] {NfcA.class.getName(),Ndef.class.getName()
                    NdefFormatable.class.getName()}};
            mFilters =新的IntentFilter [] {NDEF,};
            nfc_initialized = TRUE;
        }
    }    公共无效updateIncomingMessage(弦乐味精)
    {
        TextView的txtView =(的TextView)findViewById(R.id.txtReceive);
        txtView.setText(MSG);
    }    @覆盖
    保护无效调用onStart()
    {
        super.onStart();        _incomingMessageTimer =新的Timer();
        _incomingMessageTimer.schedule(新MessageReceiveTimer(新的处理程序(),这一点),0900);
    }    @覆盖
    保护无效的onStop()
    {
        super.onStop();        _incomingMessageTimer.cancel();
        _incomingMessageTimer.purge();    }    @覆盖
    公共无效onNewIntent(意向意图)
    {
        mIntent =意图;        字符串行动= intent.getAction();
        如果(NfcAdapter.ACTION_TAG_DISCOVERED.equals(动作))
        {
            MyTag的= intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);            如果(_ndef!= NULL)
            {
                尝试
                {
                    _ndef.close();
                    _ndef = NULL;
                }
                赶上(IOException异常E)
                {
                    e.printStackTrace();
                }
            }            如果(_ndef == NULL)
            {
                _ndef = Ndef.get(MyTag的);                尝试
                {
                    _ndef.connect();
                }
                赶上(IOException异常E)
                {
                    e.printStackTrace();
                }
            }
        }
    }
    @覆盖
    公共无效的onPause()
    {
        super.onPause();
        mAdapter.disableForegroundDispatch(本);
    }    @覆盖
    公共无效onResume()
    {
        super.onResume();
        mAdapter.enableForegroundDispatch(这一点,mPendingIntent,mFilters,
            mTechLists);
    }
}

// TimerTask的类

 包com.example.infcdemo;
进口java.util.TimerTask中;进口android.nfc.NdefMessage;
进口android.os.Handler;公共类MessageReceiveTimer扩展的TimerTask
{
    处理器_handler;
    MainActivity _mainActivity;    MessageReceiveTimer(处理程序处理程序,MainActivity mainActivity)
    {
        超();
        _handler =处理程序;
        _mainActivity = mainActivity;
    }    @覆盖
    公共无效的run()
    {
        _handler.post(新的Runnable()
        {
            @覆盖
            公共无效的run()
            {
                尝试
                {
                    如果(_mainActivity._ndef!= NULL)
                    {
                        NdefMessage ndefMsg = NULL;                        如果(_mainActivity._ndef.isConnected())
                            ndefMsg = _mainActivity._ndef.getNdefMessage();
                        其他
                            ndefMsg = NULL;                        字节[] = ndefRecord n​​defMsg.getRecords()[0] .getPayload();
                        字符串strMsg =新的String(ndefRecord,US-ASCII);
                        _mainActivity.updateIncomingMessage(strMsg);
                    }
                }
                赶上(例外五)
                {
                    返回;
                }
            }
        });
    }
}


解决方案

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

您首先创建一个处理其预定任务的计时器的独立的主题

  _incomingMessageTimer =新的Timer();
                        ^^^^^^^^^^^
_incomingMessageTimer.schedule(新MessageReceiveTimer(新处理程序(),这一点),0,900);
                      ^^^^^^^^

然而,当TimerTask的执行(定时器螺纹上)

 公共类MessageReceiveTimer扩展的TimerTask {
    @覆盖
    公共无效的run(){
        // code excecution发生在计时器线程
    }
}

您立即发布一个Runnable上的邮件队列控制权返回给主(UI)线程:

  _handler.post(新的Runnable(){
    @覆盖
    公共无效的run(){
        // code执行发生在处理器的线程
    }
});

处理程序的线程是在你的情况下,主(UI)线程为您创建的主线程处理程序:

  _incomingMessageTimer.schedule(新MessageReceiveTimer(新的处理程序(),这一点),0900);
                                                       ^^^^^^^^^^^^^

因此​​IO操作( _mainActivity._ndef.getNdefMessage())将阻止主线程。需要注意的是Android文档明确地说,这种方法的不得从主应用程序线程名为的(见的这里)。这同样适用于 连接()方法,顺便说一句。

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

解决方案

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.

这篇关于从标签获取NDEF消息冻结Android应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆