1. はじめに
1.1 NFC は、Wi-Fi や Bluetooth に次ぐ新しい近距離無線伝送技術です。この種の制限は比較的大きく、データ伝送を実現するにはデバイスに NFC ハードウェアが搭載されている必要があり、伝送距離は非常に短く、4cm以内のみ送信でき、実際のシーンは触れたときにのみ送信される可能性があります。
1.2 NFCの長所と短所
メリット:通信速度が速い、接続が便利、タッチするだけで通信可能、セキュリティが高い
短所: 伝送距離が非常に短い、伝送データ量が少ない、すべてに普及しているわけではない、一部の携帯電話はサポートしていない
1.3 NFC、Bluetooth、赤外線コントラスト
比較項目 | NFC | ブルートゥース | 赤外線 |
ネットワークタイプ | ピアツーピア | ポイントツーマルチポイント | ピアツーピア |
有効距離 | <=0.1m | <=10m、最新の Bluetooth 4.0 有効距離は最大 100m | 通常1m以内、サーマルテクノロジー接続、不安定 |
転送速度 | 最大424kbps | 最大24Mbps | 低速 115.2kbp5、高速 4Mbps |
接続時間 | <0.1秒 | 6秒 | 0.5秒 |
安全性 | セキュリティ、ハードウェア実装 | セキュリティ、ソフトウェア実装 | 安全性、IRFM 使用時を除く |
通信モード | アクティブ-アクティブ/パッシブ | アクティブ-アクティブ | アクティブ-アクティブ |
料金 | 低い | 真ん中 | 低い |
1.4 NFC通信モード
カードリーダーモード(リーダー/ライターモード)、エミュレーションカードモード(Card Emulation Mode)、ピアツーピアモード(P2Pモード)。
カードリーダーモード
NFC チップ内のデータは、単純に「スワイプ タグ」として理解できます。本質的には、NFC をサポートする携帯電話やその他の電子機器を介して、NFC チップを搭載したラベル、ステッカー、名刺などのメディアから情報を読み書きすることです。通常、NFC タグは外部電源を必要としません。NFC 対応の周辺機器が NFC にデータを読み書きするとき、ある種の磁場が送信され、この磁場が NFC タグに自動的に電力を供給します。
エミュレーションカードモード
NFC をサポートする携帯電話またはその他の電子デバイス内のデータは、単純に「携帯電話をスワイプする」と理解できます。本質的には、NFC 対応の携帯電話やその他の電子デバイスを、デビット カード、バス カード、アクセス コントロール カードなどの IC カードとして使用することです。基本原理は、対応するICカード内の情報証明書をデータパケットにカプセル化し、NFC対応周辺機器に格納することです。
ご利用の際にはNFC高周波デバイス(カードリーダーに相当)も必要となります。携帯電話をNFC無線デバイスに近づけると、携帯電話はNFC無線デバイスから送信された信号を受信し、一連の複雑な認証を通過した後、ICカードの対応する情報がNFC無線デバイスに送信されます。 IC カードのデータは、NFC 無線周波数デバイスに送信され、コンピュータは無線周波数デバイスに接続され、対応する処理 (電子転送、ドアの開閉など) を実行します。
ピアツーピアモード
このモードは Bluetooth や赤外線に似ており、異なる NFC デバイス間のデータ交換に使用されますが、このモードには「スワイプ」の感覚がありません。有効距離は一般に 4 cm を超えることはできませんが、伝送確立速度は赤外線や Bluetooth テクノロジーよりもはるかに速く、双方が Android 4.2 を使用している場合、NFC は Bluetooth を直接使用して伝送します。この技術は Android ビームと呼ばれます。そのため、Android ビームを使用してデータを転送する 2 台のデバイスは 4cm 以内に制限されなくなりました。
ピアツーピア モードの一般的な用途は、2 台の NFC 対応携帯電話またはタブレット間のピアツーピア データ送信 (写真の交換やデバイスの連絡先の同期など) です。したがって、NFC を介して、デジタル カメラ、コンピュータ、携帯電話などの複数のデバイスを迅速に接続し、データやサービスを交換できます。
2、NFC通信ステップ
2.1 AndroidManifest.xml は動的リクエストなしで NFC 権限を追加します
<!--NFC 相关权限-->
<!--描述所需硬件特性-->
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<uses-permission android:name="android.permission.NFC" />
2.2 NFC タグフィルタリングの追加 (2 つの方法)
方法 1: AndroidManifest.xml の Activity タグに XML 設定を追加する
res ディレクトリに新しい xml フォルダーを作成し、新しい nfc_tech_filter.xml ファイルを作成します。
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- 可以处理所有Android支持的NFC类型 -->
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.NfcB</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.NfcF</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.NfcV</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.Ndef</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.NdefFormatable</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.MifareClassic</tech>
</tech-list>
</resources>
アクティビティタグは XML を構成します
<activity
android:name=".MainActivity"
android:exported="true">
<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.TECH_DISCOVERED" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
方法 2: NFC の初期化時にタグを動的に構成する
/**
* 初始化nfc设置
*/
public static void NfcInit(Activity activity) {
Intent intent = new Intent(activity, activity.getClass());
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
mPendingIntent = PendingIntent.getActivity(activity, 0, intent, 0);
//做一个IntentFilter过滤你想要的action 这里过滤的是ndef
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
//如果你对action的定义有更高的要求,比如data的要求,你可以使用如下的代码来定义intentFilter
// IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
// try {
// filter.addDataType("*/*");
// } catch (IntentFilter.MalformedMimeTypeException e) {
// e.printStackTrace();
// }
// mIntentFilter = new IntentFilter[]{filter, filter2};
// mTechList = null;
try {
filter.addDataType("*/*");
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
mTechList = new String[][]{
{MifareClassic.class.getName()},
{NfcA.class.getName()}};
//生成intentFilter
mIntentFilter = new IntentFilter[]{filter};
}
2.3 NFCアダプターの作成
NFC のすべての操作は NfcAdapter を通じて完了します。NfcAdapter を作成した後、同時に待機中の PendingIntent を作成します。これは主に NFC を起動してページを開くために使用されます。
private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;
@Override
protected void onStart() {
super.onStart();
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
2.4 onResume でフォアグラウンド スケジューリングをオンにして NFC を初期化する
//在onResume中开启前台调度
@Override
protected void onResume() {
super.onResume();
//设定intentfilter和tech-list。如果两个都为null就代表优先接收任何形式的TAG action。也就是说系统会主动发TAG intent。
if (mNfcAdapter != null) {
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); //启动 }
}
}
2.5 onNewIntent で NFC デバイスによって渡されたインテントを処理する
//在onNewIntent中处理由NFC设备传递过来的intent
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processIntent(intent);
}
2.6 カード ID (通常はデバイスのシリアル番号、16 進形式 09:D1:E6:6B) を含む読み取りデータを処理します。
NFC カードのラベルの情報は入手できない場合がありますが、NFC カードを一意に識別するシリアル番号は入手できる必要があります。
//这块的processIntent() 就是处理卡中数据的方法
public void processIntent(Intent intent) {
try {
tvContent.setText("");
// 检测卡的id
String id = readNFCId(intent);
// NfcUtils中获取卡中数据的方法
String result = readNFCFromTag(intent);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("卡ID十六进制:" + id).append("\r\n");
stringBuilder.append("卡ID十进制:" + hexToDec(id)).append("\r\n");
stringBuilder.append("信息:").append("\r\n");
stringBuilder.append(result).append("\r\n");
tvContent.setText(stringBuilder);
// 往卡中写数据
//String data = "this.is.write";
//writeNFCToTag(data, intent);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 读取nfcID
*/
public String readNFCId(Intent intent) throws UnsupportedEncodingException {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String id = ByteArrayToHexString(tag.getId());
return id;
}
/**
* 读取NFC的数据
*/
public String readNFCFromTag(Intent intent) throws UnsupportedEncodingException {
Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
StringBuilder stringBuilder = new StringBuilder();
if (rawArray != null) {
for (int i = 0; i < rawArray.length; i++) {
NdefMessage mNdefMsg = (NdefMessage) rawArray[i];
for (int j = 0; j < mNdefMsg.getRecords().length; i++) {
NdefRecord mNdefRecord = mNdefMsg.getRecords()[j];
if (mNdefRecord != null) {
String readResult = new String(mNdefRecord.getPayload(), "UTF-8");
stringBuilder.append(readResult).append("\r\n");
}
}
}
}
return stringBuilder.toString();
}
/**
* 十六进制转10进制
* @param s
* @return
*/
public static int hexToDec(String s) {
String s1 = s.toUpperCase(); // 全转大写
char[] chars = s1.toCharArray(); // 转成 char 数组
Stack<Character> stack = new Stack<>();
for (int i = 0; i < chars.length; i++) {
stack.push(chars[i]); // 放入栈中,倒序遍历
}
int sum = 0; // 定义总和
int size = stack.size(); // 要先赋值给 size ,不然 stack.pop() 之后 size 会变
for (int i = 0; i < size; i++) {
Character pop = stack.pop();
if (String.valueOf(pop).matches("[A-F]")) { // 如果是 A-F
sum += (Math.pow(16, i) * ((pop - 55))); // A的ASCII码为 65,取偏移量
} else { // 如果是纯数字
sum += Math.pow(16, i) * Integer.parseInt(String.valueOf(pop));
}
}
return sum;
}
/**
* 将字节数组转换为字符串
*/
private String ByteArrayToHexString(byte[] inarray) {
int i, j, in;
String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
String out = "";
for (j = 0; j < inarray.length; ++j) {
in = (int) inarray[j] & 0xff;
i = (in >> 4) & 0x0f;
out += hex[i];
i = in & 0x0f;
out += hex[i];
}
return out;
}
2.7 NFC へのデータの書き込み
/**
* 往nfc写入数据
*/
public static void writeNFCToTag(String data, Intent intent){
try {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Ndef ndef = Ndef.get(tag);
ndef.connect();
NdefRecord ndefRecord = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
ndefRecord = NdefRecord.createTextRecord(null, data);
}
NdefRecord[] records = {ndefRecord};
NdefMessage ndefMessage = new NdefMessage(records);
ndef.writeNdefMessage(ndefMessage);
}catch (Exception e){
}
}
// 往卡中写数据
String data = "this.is.write";
writeNFCToTag(data, intent);
2.8 NfcAdapter スケジュールのキャンセル
@Override
protected void onPause() {
super.onPause();
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
}
2.9 NfcAdapter の破棄
@Override
protected void onDestroy() {
super.onDestroy();
mNfcAdapter = null;
}
2.10 概要
NFC は、アクセス コントロール カード、支払いカード、地下鉄カードなどのカード スワイプ データ送信シナリオに適しており、高速かつ便利です。