我有一个可以捕获NFC标签的应用。我过去遇到的问题是用户以不稳定的方式悬停在标签上,导致NFC适配器触发两次。
我做了一些事情来对付这件事。
清单:
<activity
android:name=".NfcActivity"
android:screenOrientation="portrait"
android:launchMode="singleTask"
android:noHistory="true"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
这将NFC捕获活动设置为堆栈中唯一的实例,并且没有历史记录。我已经覆盖了所有可以停止并重新启动此活动的配置更改,后者可能会导致意图数据重新传递到活动,使其看起来像重复扫描。
在Activity本身中,我已经覆盖了onNewIntent,除了显示一个糟糕的扫描屏幕之外什么都不做。我也理解onNewIntent应该从功能角度镜像onCreate,但是因为以前版本的应用程序已经向下一个Activity发送了2次扫描,我只想在一个地方使用NFC捕获代码onCreate。
在onCreate中我做了进一步的测试,以防止悬停在标签上并创建错误的扫描。
该应用程序似乎工作正常,但在一个特定的手机(三星Galaxy Young 2),如果用户将手机放在标签上几秒钟,NFC适配器似乎连续几次点火。< / p>
发生这种情况时,原始扫描将被取消。这样做的原因是oncreate处理标签,但是当后续扫描发生时(通过偶然的悬停),onPause - &gt; onNewIntent运行。因此,Activity跳出onCreate并停止处理标记。 onNewIntent显示失败的扫描屏幕并启动菜单屏幕。
上述问题不是太大,因为用户必须重新扫描标签。
我想要发生的是:
当onCreate运行时,无论如何处理标记,即使onNewintent执行也是如此。有没有办法可以在到达onNewintent和onPause之前拦截意图?
也许有一个我可以使用的全局标志,可以首先检查onCreate是否仍然在运行而onNewIntent不应该,或者更重要的是onPause不会被称为onCreate停止运行。
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.ndeftools.Message;
import org.ndeftools.Record;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.os.Vibrator;
import android.util.Log;
import android.widget.Toast;
public class NfcActivity extends Activity {
private static final String TAG = NfcActivity.class.getName();
protected NfcAdapter nfcAdapter;
protected PendingIntent nfcPendingIntent;
Handler handler;
Runnable runnable;
Handler failHandler;
Runnable failRunnable;
Parcelable[] messages;
Intent i;
Tag tag;
String tagId;
boolean nfcConnected;
ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.nfcactivitylayout);
Log.e(TAG, "oncreate");
nfcConnected = false;
// initialize NFC
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
nfcPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
tag = null;
tagId = null;
i = getIntent();
if ((i.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
//check to see if Android has previously killed the app and relaunched it from History
//and delivered the original intent.
//if it has do not process and launch the menu screen
Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
processPayloadIntent.setAction("QRCODE_ACTION");
processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(processPayloadIntent);
}else{
tag = i.getParcelableExtra(NfcAdapter.EXTRA_TAG);
tagId = bytesToHexString(tag.getId());
Log.e(TAG, "tagID = " + tagId);
Log.e(TAG, "oncreate intent action = " + i.getAction());
//The activity has captured tag data, prove the user is not hovering over the tag and is doing a good scan
//hovering can trigger the adapter twice
AsyncNfcConnect asnc = new AsyncNfcConnect();
try {
asnc.execute().get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.e(TAG, "nfcConnected!!!!!!!!!!!!!!!!!!!!!!!!! = " + nfcConnected);
if(nfcConnected == true){
int buildVersionSdk = Build.VERSION.SDK_INT;
int buildVersionCodes = Build.VERSION_CODES.GINGERBREAD;
Log.e(TAG, "buildVersionSdk = " + buildVersionSdk
+ "buildVersionCodes = " + buildVersionCodes);
int themeVersion;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {
themeVersion = 2;
} else {
themeVersion = 1;
}
try{
progressDialog = new ProgressDialog(this, themeVersion);
progressDialog.setTitle("NFC Tag Scanned");
progressDialog.setMessage("Processing tag...");
progressDialog.setIndeterminate(true);
progressDialog.show();
}catch(Exception e){ }
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(i.getAction()) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(i.getAction())) {
if(NfcScannerApplication.isCanScanNfcTag()){
messages = i.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (messages != null) {
//setContentView(R.layout.successfulnfc);
NfcScannerApplication.startNfcTimer();
//Toast.makeText(this, "NFC timer set", Toast.LENGTH_LONG).show();
Log.e(TAG, "Found " + messages.length + " NDEF messages"); // is almost always just one
vibrate(); // signal found messages :-)
initHandler();
handler.postDelayed(runnable, 2000);
}else{
Toast.makeText(this, "Data on tag was not correct", Toast.LENGTH_LONG).show();
try{
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}else{
try{
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
} else {
Toast.makeText(this, "Tag not recognized correctly", Toast.LENGTH_LONG).show();
try{
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}else{
try{
Toast.makeText(this, "Phone wasn't connected to Tag", Toast.LENGTH_LONG).show();
handler.removeCallbacks(runnable);
Log.e(TAG, "just removed callback to runnable that reads nfc tag data");
}catch(Exception e){
}
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}//end of NFC connect test
}//end of launched from history check
}//end of onCreate
@Override
protected void onStart() {
super.onStart();
Log.e(TAG, "onStart");
}
@Override
protected void onStop() {
super.onStop();
Log.e(TAG, "onStop");
}
@Override
public void onNewIntent(Intent intent) {
Log.e(TAG, "onNewIntent");
Toast.makeText(this, "Bad scan!!!", Toast.LENGTH_LONG).show();
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}//end of onNewIntent
public void enableForegroundMode() {
Log.e(TAG, "enableForegroundMode");
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); // filter for all
IntentFilter[] writeTagFilters = new IntentFilter[] {tagDetected};
nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null);
}
public void disableForegroundMode() {
Log.e(TAG, "disableForegroundMode");
nfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume");
enableForegroundMode();
}
@Override
protected void onPause() {
Log.e(TAG, "onPause");
super.onPause();
disableForegroundMode();
if(handler != null){
handler.removeCallbacks(runnable);
}
}
private void vibrate() {
Log.e(TAG, "vibrate");
Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE) ;
vibe.vibrate(500);
}
public void initHandler(){
handler = new Handler();
runnable = new Runnable() {
public void run() {
processTag();
}
private void processTag() {
Log.e(TAG, "about to process tag");
try{
progressDialog.dismiss();
}catch(Exception e){
//do nothing
}
// parse to records
for (int i = 0; i < messages.length; i++) {
try {
List<Record> records = new Message((NdefMessage)messages[i]);
Log.e(TAG, "Found " + records.size() + " records in message " + i);
for(int k = 0; k < records.size(); k++) {
Log.e(TAG, " Record #" + k + " is of class " + records.get(k).getClass().getSimpleName());
Record record = records.get(k);
NdefRecord ndefRecord = record.getNdefRecord();
byte[] arr = ndefRecord.getPayload();
String payload = new String(arr);
if(payload.length() > 0){
payload = payload.substring(3, payload.length());
Log.e(TAG, "payload = " + payload);
String[] splitPayload = payload.split(",");
String tagType = splitPayload[0];
String tagCompany = splitPayload[1];
String tagClientID = splitPayload[2];
String tagClientName = splitPayload[3];
if(! tagClientID.equalsIgnoreCase("0") && tagClientID.length() > 0){
handler.post(new Runnable(){
public void run() {
setContentView(R.layout.successfulnfc);
}
});
Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
processPayloadIntent.putExtra("payload", payload);
processPayloadIntent.putExtra("tagid", tagId);
processPayloadIntent.setAction("NFC");
processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//processPayloadIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(processPayloadIntent);
finish();
overridePendingTransition(0, R.anim.activity_animation_zoom_in);
}else{
Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}else{
Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();
initFailHandler();
failHandler.postDelayed(failRunnable, 1);
}
}
} catch (Exception e) {
Log.e(TAG, "Problem parsing message", e);
}
}
}
};
}
public void initFailHandler(){
failHandler = new Handler();
failRunnable = new Runnable() {
public void run() {
returnToMainMenu();
}
private void returnToMainMenu() {
//Log.e(TAG, "about to return to main menu");
try{
progressDialog.dismiss();
}catch(Exception e){
//do nothing
}
Toast.makeText(NfcActivity.this, "Please check your scanning technique.\nPlease do not hover over tag or swipe...", Toast.LENGTH_LONG).show();
failHandler.post(new Runnable(){
public void run() {
setContentView(R.layout.nfcfail);
}
});
//onBackPressed();
Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
processPayloadIntent.setAction("QRCODE_ACTION");
processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(processPayloadIntent);
finish();
//overridePendingTransition(0, R.anim.activity_animation_zoom_in);
}
};
}
private String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("0x");
if (src == null || src.length <= 0) {
return null;
}
char[] buffer = new char[2];
for (int i = 0; i < src.length; i++) {
buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
System.out.println(buffer);
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
private class AsyncNfcConnect extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
NfcActivity.this.nfcConnected = false;
String result;
Ndef ndefTag = Ndef.get(tag);
try {
Log.e(TAG, "about to test connect()********************************************");
ndefTag.connect(); // this should already perform an IO operation and should therefore fail if there is no tag
Log.e(TAG, "Ndef.connect() connected!********************************************");
NdefMessage ndefMsg = ndefTag.getNdefMessage(); // this reads the current NDEF message from the tag and consequently causes an IO operation
NfcActivity.this.nfcConnected = true;
result = "OK";
return result;
} catch (Exception e) {
// there is no tag or communication with tag dropped
Log.e(TAG, "There a problem with connecting to the tag using Ndef.connect(");
NfcActivity.this.nfcConnected = false;
result = "NOTOK";
return result;
} finally {
try {
ndefTag.close();
} catch (Exception e) {
}
}
}
}//end of Async
}
答案 0 :(得分:1)
您似乎坚持不在onNewIntent()
处理NFC意图。我建议onCreate()
和onNewIntent()
都调用扫描标记的常用过程。这样,两个入口点都将遵循相同的代码路径。
您声称应用程序跳出了创建&#34;可能只是一种比喻?会发生什么情况是您的标记扫描AsyncNfcConnect
在单独的线程上作为后台任务运行(应该如此)。该任务在onCreate()
中创建,并在onCreate()
完成后继续运行(您可以在Log
末尾添加onCreate()
语句进行检查)。当以某种方式丢失与标记的连接并重新发现标记时,正如您所观察到的那样调用onNewIntent()
。
在任何情况下都无法阻止这种情况发生,因此您的应用必须能够处理它。要检测并处理它,您可以在活动中设置一些标志,以指示您的后台任务正在运行或已运行。您还可以存储是否发生异常的信息,并在重新发现时再次尝试再次扫描标记(这可能需要您实施我上面提出的建议)。如果您想让您的应用程序更加失败,您还可以存储最后扫描的标签的ID,以便在成功扫描(或不扫描)后重新发现它时再次识别它。当异常持续发生在同一标签上时,您可以在一定次数后向用户指明他(例如,通过建议设备与标签的位置不同)。