ForegroundDispatchとonPauseに注意
Android 2.3.3(4)、NFCのForegroundDispatchを制御するにはActivityのonResume/onPauseを基準にする必要があるということで、以下のようなスニペットがよく紹介されている。
private NfcAdapter mAdapter;
private PendingIntent mPendingIntent;
private IntentFilter mFilters;
private String mTechLists;
private TextView mText;
private int mCount = 0;
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
mAdapter = NfcAdapter.getDefaultAdapter(this);
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
mFilters = new IntentFilter {
ndef,
};
mTechLists = new String { new String[] { NfcF.class.getName() } };
}
@Override
public void onResume() {
mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
super.onResume();
}
@Override
public void onPause() {
mAdapter.disableForegroundDispatch(this);
super.onPause();
}
私も実際にこの通りコーディングしたのだが、この方法はケースによっては上手くいかない。例えば次々と連続して複数のNFCタグを読み込むような処理の場合、有効にしたはずのForegroundDispatchが無効になり、Androidにプリインストールされている汎用のNFCTaggerがタグを横取りしてしまうだろう。

ここで注意しなくてはならないのはonPauseメソッドである。onPauseは通常システムが現在前面にいる以外のActivityを再開する場合に呼ばれるのだが、それだけではなく、現在のActivityが新たなインテントを処理する際にも呼ばれる場合があるため、NFCのForegroundDispatchのようにPendingIntentを使ってタグの検出を処理する場合、インテントをハンドルした時点でonPuaseが呼ばれて、結果としてForegroundDispatchが無効になってしまうのだ。
意図せずに呼ばれているonPauseメソッドのスタックトレース (NfcAdapter.ACTION_TECH_DISCOVEREDアクションのインテント受信時)
NfcFeliCaTagFragment.onPause() line: 139 FragmentManagerImpl.moveToState(Fragment, int, int, int) line: 853 FragmentManagerImpl.moveToState(int, int, int, boolean) line: 991 FragmentManagerImpl.moveToState(int, boolean) line: 974 FragmentManagerImpl.dispatchPause() line: 1654 NFCFeliCaReader(FragmentActivity).onPause() line: 375 NFCFeliCaReader(Activity).performPause() line: 3851 Instrumentation.callActivityOnPause(Activity) line: 1191 ActivityThread.performNewIntents(IBinder, List) line: 1732 ActivityThread.handleNewIntent(ActivityThread$NewIntentData) line: 1742 ActivityThread.access$2300(ActivityThread, ActivityThread$NewIntentData) line: 117 ActivityThread$NewIntentData(ActivityThread$H).handleMessage(Message) line: 978 ActivityThread$H(Handler).dispatchMessage(Message) line: 99 Looper.loop() line: 130
これでは一度だけはNFCタグを読み込めるものの、2回目以降はForegroundDispatchが無効になってしまう。
これを回避するには、ForegroundDispatchを無効にするのはを明確にすることだ。例えばActivityが完全に終了したら無効にするのであれば、onResumeメソッドはそのままでonPauseメソッドを以下のように書き換える。
@Override
public void onPause() {
if ( this.isFinishing() ) {
mAdapter.disableForegroundDispatch(this);
}
super.onPause();
}
これで対象のActivityが終了するまでForegroundDispatchを有効にしたままにできる。