无法触发使用 LockTaskMode 读取 NFC 的意图
Unable to trigger intent on NFC read using with LockTaskMode
我正在尝试在我的 Android 应用程序中读取 NFC 标签,NFC 标签是带有纯文本的简单卡片,在查看 Android 文档和其他一些指南之后我'我的 AndroidManifest 中包含以下代码:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<activity
android:name=".pterm" // activity where i would be able to read NFC
android:screenOrientation="portrait"
android:theme="@style/SplashScreen">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
</intent-filter>
</activity>
并且在我的 activity 中添加了以下代码:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (nfcRead) {
readFromIntent(intent);
}
}
private void readFromIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] messages = null;
if (rawMessages != null) {
messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < rawMessages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
NdefRecord[] records = messages[i].getRecords();
//if you are sure you have text then you don't need to test TNF
for(NdefRecord record: records){
processRecord(record);
}
}
}
}
}
public void processRecord(NdefRecord record) {
short tnf = record.getTnf();
switch (tnf) {
case NdefRecord.TNF_WELL_KNOWN: {
if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
String yourtext = processRtdTextRecord(record.getPayload());
Log.e("NFC:", yourtext);
} else if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
return;
} else if (Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER)) {
return;
} else {
return;
}
}
case NdefRecord.TNF_MIME_MEDIA: {
if (record.toMimeType().equals("MIME/Type")) {
// handle this as you want
} else {
//Record is not our MIME
}
}
// you can write more cases
default: {
//unsupported NDEF Record
}
}
}
private String processRtdTextRecord(byte[] payload) {
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
int languageCodeLength = payload[0] & 0063;
String text = "";
try {
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e("UnsupportedEncoding", e.toString());
}
return text;
}
但是当我尝试读取 NFC 标签时,甚至没有触发 onNewIntent 事件,但在设备发出 NFC 通知声音时读取了 NFC。
该应用程序的目的是仅在自定义 AletDialog 启动时读取 NFC,一旦 NFC 读取了值,应将其放置在 EditText 中,并且只有在对话框启动时才能再次读取新值又起来了。
应用程序正在使用 LockTaskMode。
更新:
根据应用程序在 LockTaskMode
中的评论,这可能会改变 re-launch 应用程序向其提供 intent
的系统能力(我没有 LockTaskMode
)
我建议您不要使用旧的 NFC API,当您处于限制性 LockTaskMode
时,必须使用启动或 re-launching 应用程序。
我建议您尝试更新更好的 enableReaderMode
NFC API 来读卡。
https://developer.android.com/reference/android/nfc/NfcAdapter#enableReaderMode(android.app.Activity,%20android.nfc.NfcAdapter.ReaderCallback,%20int,%20android.os.Bundle)
此 enableReaderMode
NFC API 会在您的应用程序中引发回调,启动一个新线程来处理传入的 NFC 数据,没有启动或 re-launch 您的应用程序和因此 LockTaskMode
.
可能没有问题
enableReaderMode
API 使用示例。
public class MainActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback{
private NfcAdapter mNfcAdapter;
@Override
protected void onResume() {
super.onResume();
enableNfc();
}
private void enableNfc(){
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter!= null && mNfcAdapter.isEnabled()) {
// READER_PRESENCE_CHECK_DELAY is a work around for a Bug in some NFC implementations.
Bundle options = new Bundle();
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);
// Ask for all type of cards to be sent to the App as they all might contain NDEF data
mNfcAdapter.enableReaderMode(this,
this,
NfcAdapter.FLAG_READER_NFC_A |
NfcAdapter.FLAG_READER_NFC_B |
NfcAdapter.FLAG_READER_NFC_F |
NfcAdapter.FLAG_READER_NFC_V |
NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
options);
} else {
NfcMessage();
}
}
@Override
protected void onPause() {
super.onPause();
if(mNfcAdapter!= null)
mNfcAdapter.disableReaderMode(this);
}
// This gets run in a new thread on Tag detection
// Thus cannot directly interact with the UI Thread
public void onTagDiscovered(Tag tag) {
// Get the Tag as an NDEF tag Technology
Ndef mNdef = Ndef.get(tag);
// only process if there is NDEF data on the card
if (mNdef != null) {
NdefMessage mNdefMessage = mNdef.getCachedNdefMessage();
// Now process the Ndef message
....
// If success of reading the correct Ndef message
// Make a notification sound (as we disabled PLATFORM_SOUNDS when enabling reader Mode)
// As getting a confirmation sound too early causes bad user behaviour especial when trying to write to cards
}
// Ignore other Tag types.
}
....
原创
我将在此处保留原始答案,因为它在此处显示了系统 NFC 应用程序的工作,其中 enableForegroundDispatch
通过 re-launching 并暂停将其中包含 NFC 数据的意图直接传递给您的应用程序并恢复它触发 onNewIntent
它还解释了如果你没有 enableForegroundDispatch
那么系统 NFC 应用程序将 NFC 意图传递给 Android OS 这导致它找到一个可以处理这种类型的意图的应用程序,它通过查看应用程序的所有清单文件以匹配 Intent 过滤器。
因此,如果系统扫描到匹配的标签,Manifest Intent 过滤器将导致 Android 启动您的应用程序。
onNewIntent
用于
the activity is re-launched while at the top of the activity stack instead of a new instance of the activity being started, onNewIntent() will be called on the existing instance with the Intent that was used to re-launch it.
来自 https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)
因此,在您的应用不是 运行 的情况下,它无法被 re-launch 编辑,因此必须在 onCreate
中捕获 Intent。
所以在 onCreate
中输入类似
的内容
Intent intent = getIntent();
// Check to see if the App was started because of the Intent filter in the manifest, if yes read the data from the Intent
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
readFromIntent(intent);
}
现在如果你想在应用程序 运行 时覆盖接收 NFC 数据,你需要使用 enableForegroundDispatch
和 onNewIntent
https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch
可以在 onResume
中创建 Intent Filter 并启用前台调度(当您的应用不在前台,即在 onPause
中时,应禁用前台调度,如图所示)。
例如像
private NfcAdapter adapter;
public void onResume() {
super.onResume();
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
// Only look for plain text mime type
ndef.addDataType("text/plain");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
intentFiltersArray = new IntentFilter[] {ndef, };
adapter = NfcAdapter.getDefaultAdapter(this);
adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}
@Override
public void onPause() {
super.onPause();
adapter.disableForegroundDispatch(this);
}
我正在尝试在我的 Android 应用程序中读取 NFC 标签,NFC 标签是带有纯文本的简单卡片,在查看 Android 文档和其他一些指南之后我'我的 AndroidManifest 中包含以下代码:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<activity
android:name=".pterm" // activity where i would be able to read NFC
android:screenOrientation="portrait"
android:theme="@style/SplashScreen">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
</intent-filter>
</activity>
并且在我的 activity 中添加了以下代码:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (nfcRead) {
readFromIntent(intent);
}
}
private void readFromIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] messages = null;
if (rawMessages != null) {
messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < rawMessages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
NdefRecord[] records = messages[i].getRecords();
//if you are sure you have text then you don't need to test TNF
for(NdefRecord record: records){
processRecord(record);
}
}
}
}
}
public void processRecord(NdefRecord record) {
short tnf = record.getTnf();
switch (tnf) {
case NdefRecord.TNF_WELL_KNOWN: {
if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
String yourtext = processRtdTextRecord(record.getPayload());
Log.e("NFC:", yourtext);
} else if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
return;
} else if (Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER)) {
return;
} else {
return;
}
}
case NdefRecord.TNF_MIME_MEDIA: {
if (record.toMimeType().equals("MIME/Type")) {
// handle this as you want
} else {
//Record is not our MIME
}
}
// you can write more cases
default: {
//unsupported NDEF Record
}
}
}
private String processRtdTextRecord(byte[] payload) {
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
int languageCodeLength = payload[0] & 0063;
String text = "";
try {
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e("UnsupportedEncoding", e.toString());
}
return text;
}
但是当我尝试读取 NFC 标签时,甚至没有触发 onNewIntent 事件,但在设备发出 NFC 通知声音时读取了 NFC。
该应用程序的目的是仅在自定义 AletDialog 启动时读取 NFC,一旦 NFC 读取了值,应将其放置在 EditText 中,并且只有在对话框启动时才能再次读取新值又起来了。
应用程序正在使用 LockTaskMode。
更新:
根据应用程序在 LockTaskMode
中的评论,这可能会改变 re-launch 应用程序向其提供 intent
的系统能力(我没有 LockTaskMode
)
我建议您不要使用旧的 NFC API,当您处于限制性 LockTaskMode
时,必须使用启动或 re-launching 应用程序。
我建议您尝试更新更好的 enableReaderMode
NFC API 来读卡。
https://developer.android.com/reference/android/nfc/NfcAdapter#enableReaderMode(android.app.Activity,%20android.nfc.NfcAdapter.ReaderCallback,%20int,%20android.os.Bundle)
此 enableReaderMode
NFC API 会在您的应用程序中引发回调,启动一个新线程来处理传入的 NFC 数据,没有启动或 re-launch 您的应用程序和因此 LockTaskMode
.
enableReaderMode
API 使用示例。
public class MainActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback{
private NfcAdapter mNfcAdapter;
@Override
protected void onResume() {
super.onResume();
enableNfc();
}
private void enableNfc(){
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter!= null && mNfcAdapter.isEnabled()) {
// READER_PRESENCE_CHECK_DELAY is a work around for a Bug in some NFC implementations.
Bundle options = new Bundle();
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);
// Ask for all type of cards to be sent to the App as they all might contain NDEF data
mNfcAdapter.enableReaderMode(this,
this,
NfcAdapter.FLAG_READER_NFC_A |
NfcAdapter.FLAG_READER_NFC_B |
NfcAdapter.FLAG_READER_NFC_F |
NfcAdapter.FLAG_READER_NFC_V |
NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
options);
} else {
NfcMessage();
}
}
@Override
protected void onPause() {
super.onPause();
if(mNfcAdapter!= null)
mNfcAdapter.disableReaderMode(this);
}
// This gets run in a new thread on Tag detection
// Thus cannot directly interact with the UI Thread
public void onTagDiscovered(Tag tag) {
// Get the Tag as an NDEF tag Technology
Ndef mNdef = Ndef.get(tag);
// only process if there is NDEF data on the card
if (mNdef != null) {
NdefMessage mNdefMessage = mNdef.getCachedNdefMessage();
// Now process the Ndef message
....
// If success of reading the correct Ndef message
// Make a notification sound (as we disabled PLATFORM_SOUNDS when enabling reader Mode)
// As getting a confirmation sound too early causes bad user behaviour especial when trying to write to cards
}
// Ignore other Tag types.
}
....
原创
我将在此处保留原始答案,因为它在此处显示了系统 NFC 应用程序的工作,其中 enableForegroundDispatch
通过 re-launching 并暂停将其中包含 NFC 数据的意图直接传递给您的应用程序并恢复它触发 onNewIntent
它还解释了如果你没有 enableForegroundDispatch
那么系统 NFC 应用程序将 NFC 意图传递给 Android OS 这导致它找到一个可以处理这种类型的意图的应用程序,它通过查看应用程序的所有清单文件以匹配 Intent 过滤器。
因此,如果系统扫描到匹配的标签,Manifest Intent 过滤器将导致 Android 启动您的应用程序。
onNewIntent
用于
the activity is re-launched while at the top of the activity stack instead of a new instance of the activity being started, onNewIntent() will be called on the existing instance with the Intent that was used to re-launch it.
来自 https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)
因此,在您的应用不是 运行 的情况下,它无法被 re-launch 编辑,因此必须在 onCreate
中捕获 Intent。
所以在 onCreate
中输入类似
Intent intent = getIntent();
// Check to see if the App was started because of the Intent filter in the manifest, if yes read the data from the Intent
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
readFromIntent(intent);
}
现在如果你想在应用程序 运行 时覆盖接收 NFC 数据,你需要使用 enableForegroundDispatch
和 onNewIntent
https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch
可以在 onResume
中创建 Intent Filter 并启用前台调度(当您的应用不在前台,即在 onPause
中时,应禁用前台调度,如图所示)。
例如像
private NfcAdapter adapter;
public void onResume() {
super.onResume();
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
// Only look for plain text mime type
ndef.addDataType("text/plain");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
intentFiltersArray = new IntentFilter[] {ndef, };
adapter = NfcAdapter.getDefaultAdapter(this);
adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}
@Override
public void onPause() {
super.onPause();
adapter.disableForegroundDispatch(this);
}