我正在尝试从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()
方法,顺便说一下。