从 "Choose Application" 列表中隐藏 NFC 应用程序/禁止通过外部 NFC 意图启动
Hide NFC app from "Choose Application" list / Disable start by external NFC intent
我目前正在为 Android 编写几个支持 NFC 的应用程序,想知道如何阻止我的应用程序出现在 "choose application" 列表中,只要从中扫描标签就会打开该列表启动器或非 NFC 应用程序。我只希望我的应用程序在打开时能够读取标签。
我当前的意图过滤器:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />
nfc_tech_filter.xml:
<tech-list>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.Ndef</tech>
</tech-list>
您正在使用 NFC and Tech Discovered intent-filters
。您可以删除它们并在您的应用程序中以编程方式注册意图侦听器。
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
关于注册意图:Here's a very good answer.
In your onCreate
method you can register a receiver like this:
private BroadcastReceiver receiver;
@Override
public void onCreate(Bundle savedInstanceState){
// your oncreate code should be
IntentFilter filter = new IntentFilter();
filter.addAction("SOME_ACTION");
filter.addAction("SOME_OTHER_ACTION");
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//do something based on the intent's action
}
};
registerReceiver(receiver, filter);
}
Remember to run this in the onDestroy
method:
@Override
protected void onDestroy() {
if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
super.onDestroy();
}
下面那个答案中的另一个有趣引述:
One important point that people forget to mention is the life time of
the Broadcast Receiver
. The difference of programmatically
registering it from registering in AndroidManifest.xml is that.
In the manifest file, it doesn't depend on application life time.
While when programmatically registering it it does depend on the
application life time. This means that if you register in
AndroidManifest.xml, you can catch the broadcasted intents even when your application is not running.
Edit: The mentioned note is no longer true as of Android 3.1, the Android system excludes all receiver from receiving intents by default
if the corresponding application has never been started by the user or
if the user explicitly stopped the application via the Android menu
(in Manage → Application).
https://developer.android.com/about/versions/android-3.1.html
This is an additional security feature as the user can be sure that
only the applications he started will receive broadcast intents.
So it can be understood as receivers programmatically registered in
Application's onCreate()
would have same effect with ones declared
in AndroidManifest.xml from Android 3.1 above.
如果您在 activity 被杀死时注销接收器,您的应用程序将不再接收这些广播。
您的应用清单中目前有几个与 NFC 相关的 Intent 过滤器:
<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.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
通过将此类 Intent 过滤器添加到您应用的清单中,您表明某些 Activity 应该 启动 (或者如果注册了多个 Activity,则在 Intent 选择器中显示为选项)相同的意图)在这些 NFC 标签发现事件上。
因此,如果您不希望您的应用可以通过这些意图启动。
但是,当您的应用程序的 activity 位于前台时,您也会失去侦听这些意图的能力。由于 NFC 意图是 activity 意图,因此您无法通过动态注册的广播接收器接收它们(参见 RubbelDieKatz 的回答)。相反,Android API 提供了替代方法来捕获 NFC 相关事件,而你的应用程序的 activity 显示在前台(这也允许你的应用程序优先于其他应用程序,这使用基于清单的 NFC 意图过滤器是不可能的):
自API level 10(基本上是Android NFC开始以来),Android具有前台调度系统。您可以在 onResume()
上使用方法 NfcAdapter.enableForegroundDispatch()
注册您的前台 activity 以接收 NFC 事件(当您的 activity 在 [=17= 中失去焦点时不要忘记注销]):
public void onResume() {
super.onResume();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
adapter.enableForegroundDispatch(this, pendingIntent, null, null);
}
public void onPause() {
super.onPause();
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
adapter.disableForegroundDispatch(this);
}
您甚至可以通过向 enableForegroundDispatch()
.
提供可选的 tech-list 和 intent-filters 参数来注册以仅接收特定的 NFC 发现事件子集
在前台调度系统注册 activity 后,您将通过 onNewIntent()
回调接收 NFC 事件:
public void onNewIntent(Intent intent) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
...
}
从API 19级开始,有新的NFCreader模式API。您现在可以注册以接收定期回调(而不是通过有时会导致一些问题的意图调度)。您可以使用方法 NfcAdpater.enableReaderMode()
:
来完成此操作
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
adapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A, null);
}
除了(或代替)NfcAdapter.FLAG_READER_NFC_A
,您还可以使用其他标志,您可以设置为 reader 模式来监听其他标记技术。
您将在标签发现时收到回调(接收方需要实现回调接口NfcAdapter.ReaderCallback
):
public void onTagDiscovered(Tag tag) {
...
}
另请参阅 以了解两个选项之间的更详细比较。
我目前正在为 Android 编写几个支持 NFC 的应用程序,想知道如何阻止我的应用程序出现在 "choose application" 列表中,只要从中扫描标签就会打开该列表启动器或非 NFC 应用程序。我只希望我的应用程序在打开时能够读取标签。
我当前的意图过滤器:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />
nfc_tech_filter.xml:
<tech-list>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.Ndef</tech>
</tech-list>
您正在使用 NFC and Tech Discovered intent-filters
。您可以删除它们并在您的应用程序中以编程方式注册意图侦听器。
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
关于注册意图:Here's a very good answer.
In your
onCreate
method you can register a receiver like this:private BroadcastReceiver receiver; @Override public void onCreate(Bundle savedInstanceState){ // your oncreate code should be IntentFilter filter = new IntentFilter(); filter.addAction("SOME_ACTION"); filter.addAction("SOME_OTHER_ACTION"); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //do something based on the intent's action } }; registerReceiver(receiver, filter); }
Remember to run this in the
onDestroy
method:@Override protected void onDestroy() { if (receiver != null) { unregisterReceiver(receiver); receiver = null; } super.onDestroy(); }
下面那个答案中的另一个有趣引述:
One important point that people forget to mention is the life time of the
Broadcast Receiver
. The difference of programmatically registering it from registering in AndroidManifest.xml is that. In the manifest file, it doesn't depend on application life time. While when programmatically registering it it does depend on the application life time. This means that if you register in AndroidManifest.xml, you can catch the broadcasted intents even when your application is not running.Edit: The mentioned note is no longer true as of Android 3.1, the Android system excludes all receiver from receiving intents by default if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu (in Manage → Application). https://developer.android.com/about/versions/android-3.1.html
This is an additional security feature as the user can be sure that only the applications he started will receive broadcast intents.
So it can be understood as receivers programmatically registered in Application's
onCreate()
would have same effect with ones declared in AndroidManifest.xml from Android 3.1 above.
如果您在 activity 被杀死时注销接收器,您的应用程序将不再接收这些广播。
您的应用清单中目前有几个与 NFC 相关的 Intent 过滤器:
<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.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
通过将此类 Intent 过滤器添加到您应用的清单中,您表明某些 Activity 应该 启动 (或者如果注册了多个 Activity,则在 Intent 选择器中显示为选项)相同的意图)在这些 NFC 标签发现事件上。
因此,如果您不希望您的应用可以通过这些意图启动。
但是,当您的应用程序的 activity 位于前台时,您也会失去侦听这些意图的能力。由于 NFC 意图是 activity 意图,因此您无法通过动态注册的广播接收器接收它们(参见 RubbelDieKatz 的回答)。相反,Android API 提供了替代方法来捕获 NFC 相关事件,而你的应用程序的 activity 显示在前台(这也允许你的应用程序优先于其他应用程序,这使用基于清单的 NFC 意图过滤器是不可能的):
自API level 10(基本上是Android NFC开始以来),Android具有前台调度系统。您可以在
onResume()
上使用方法NfcAdapter.enableForegroundDispatch()
注册您的前台 activity 以接收 NFC 事件(当您的 activity 在 [=17= 中失去焦点时不要忘记注销]):public void onResume() { super.onResume(); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); adapter.enableForegroundDispatch(this, pendingIntent, null, null); } public void onPause() { super.onPause(); NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); adapter.disableForegroundDispatch(this); }
您甚至可以通过向
提供可选的 tech-list 和 intent-filters 参数来注册以仅接收特定的 NFC 发现事件子集enableForegroundDispatch()
.在前台调度系统注册 activity 后,您将通过
onNewIntent()
回调接收 NFC 事件:public void onNewIntent(Intent intent) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); ... }
从API 19级开始,有新的NFCreader模式API。您现在可以注册以接收定期回调(而不是通过有时会导致一些问题的意图调度)。您可以使用方法
来完成此操作NfcAdpater.enableReaderMode()
:public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); adapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A, null); }
除了(或代替)
NfcAdapter.FLAG_READER_NFC_A
,您还可以使用其他标志,您可以设置为 reader 模式来监听其他标记技术。您将在标签发现时收到回调(接收方需要实现回调接口
NfcAdapter.ReaderCallback
):public void onTagDiscovered(Tag tag) { ... }
另请参阅