从“选择应用程序”列表中隐藏NFC应用程序/通过外部NFC意图禁用启动

时间:2017-08-29 08:31:00

标签: android android-intent android-activity nfc intentfilter

我目前正在为Android编写几个支持NFC的应用程序,并希望了解如何阻止我的应用程序进入“选择应用程序”列表,只要从启动器或非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>

2 个答案:

答案 0 :(得分:2)

您目前在应用的清单中有几个与NFC相关的意图过滤器:

<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>

通过将此类意图过滤器添加到应用的清单中,您可以指示某些活动应该启动(或者如果在同一意图中注册了多个活动,则在意图选择器中显示为选项)这些NFC标签发现事件。

因此,如果您不希望您的应用可以通过这些意图启动,则需要从 AndroidManifest.xml 删除这些意图过滤器。

然而,当你的应用程序的活动在前台时,你也会失去监听这些意图的能力。由于NFC意图是活动意图,因此您无法通过动态注册的广播接收器接收它们(参见RubbelDieKatz的回答)。相反,Android API提供了另外的方法来捕获NFC相关事件,同时您的应用程序的活动显示在前台(这也允许您的应用程序优先于其他应用程序,这是基于清单的NFC意图过滤器无法实现的):

  1. 自API级别10(基本上自Android NFC开始以来),Android具有前台调度系统。您可以在NfcAdapter.enableForegroundDispatch()上使用onResume()方法注册前台活动以接收NFC事件(当您的活动失去焦点onPause()时,不要忘记取消注册):

    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()提供可选的技术列表和intent-filters参数来注册以仅接收特定的NFC发现事件子集。

    使用前台调度系统注册活动后,您将通过onNewIntent()回调接收NFC事件:

    public void onNewIntent(Intent intent) {
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
        ...
    }
    
  2. 从API级别19开始,有新的NFC阅读器模式API。您现在可以注册接收常规回调(而不是通过意图调度,有时会导致soome hickup)。您可以使用方法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之外,通过使用其他标志,您可以设置为阅读器模式以收听其他标签技术。

    然后,您将在标记发现时收到回调(接收方需要实现回调接口NfcAdapter.ReaderCallback):

    public void onTagDiscovered(Tag tag) {
        ...
    }
    
  3. 另请参阅What's the difference between enableReaderMode and enableForegroundDispatch?以了解两个选项之间的更详细比较。

答案 1 :(得分:1)

您正在使用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

  

onCreate方法中,您可以注册这样的接收器:

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

请记住在onDestroy方法中运行:

 @Override
 protected void onDestroy() {
  if (receiver != null) {
   unregisterReceiver(receiver);
   receiver = null;
  }
  super.onDestroy();
 }

下面的答案另一个有趣的引用:

  

人们忘记提到的一个重点是生命的时间   Broadcast Receiver。程序上的差异   通过 AndroidManifest.xml 注册来注册它就是这样。   在清单文件中,它不依赖于应用程序的生命周期。   虽然以编程方式注册它确实依赖于   应用寿命。这意味着如果您注册    AndroidManifest.xml ,即使应用程序未运行,您也可以捕获广播的意图。

     

修改:Android 3.1中提到的注释不再适用,Android系统默认情况下排除所有接收方接收意图   如果用户从未启动相应的应用程序或   如果用户通过Android菜单明确停止了应用程序   (在管理→应用程序中)。   https://developer.android.com/about/versions/android-3.1.html

     

这是一个额外的安全功能,因为用户可以确定   只有他开始的应用程序才会收到广播意图。

     

因此可以理解为以编程方式注册的接收器   应用程序的onCreate()与声明的效果相同   在Android 3.1上面的 AndroidManifest.xml 中。

如果您在活动被杀时取消注册您的接收器,您的应用程序将不再接收这些广播。