Android硬件通信之 NFC通信

一,简介

1.1 NFC是继wifi和蓝牙之后,又一种新的近距离无线传输技术,这种局限性比较大,设备必须装有NFC硬件才能实现数据传输,而且传输距离非常短,4cm之内才能传输,真实场景可能触碰才能传输。

1.2 NFC优缺点

优点:传输速度快;连接方便,触碰就能实现通信;安全性高

缺点:传输距离非常短;传输数据量小;并不是全部普及,部分手机不支持

1.3 NFC,蓝牙,红外对比

对比项NFC蓝牙红外
网络类型点对点单点对多点点对点
有效距离<=0.1m<=10m,最新的蓝牙4.0有效距离可达100m一般在1m以内,热技术连接,不稳定
传输速度最大424kbps最大24Mbps慢速115.2kbp5,快速4Mbps
连接时间<0.1s6s0.5s
安全性安全,硬件实现安全,软件实现个安全,使用IRFM时除外
通信模式主动-主动/被动主动-主动主动-主动
成本

1.4 NFC通信模式

读卡器模式(Reader/writer mode)、仿真卡模式(Card Emulation Mode)、点对点模式(P2P mode)。

读卡器模式

数据在NFC芯片中,可以简单理解成“刷标签”。本质上就是通过支持NFC的手机或其它电子设备从带有NFC芯片的标签、贴纸、名片等媒介中读写信息。通常NFC标签是不需要外部供电的。当支持NFC的外设向NFC读写数据时,它会发送某种磁场,而这个磁场会自动的向NFC标签供电。

仿真卡模式

数据在支持NFC的手机或其它电子设备中,可以简单理解成“刷手机”。本质上就是将支持NFC的手机或其它电子设备当成借记卡、公交卡、门禁卡等IC卡使用。基本原理是将相应IC卡中的信息凭证封装成数据包存储在支持NFC的外设中 。

在使用时还需要一个NFC射频器(相当于刷卡器)。将手机靠近NFC射频器,手机就会接收到NFC射频器发过来的信号,在通过一系列复杂的验证后,将IC卡的相应信息传入NFC射频器,最后这些IC卡数据会传入NFC射频器连接的电脑,并进行相应的处理(如电子转帐、开门等操作)。

点对点模式

该模式与蓝牙、红外差不多,用于不同NFC设备之间进行数据交换,不过这个模式已经没有有“刷”的感觉了。其有效距离一般不能超过4厘米,但传输建立速度要比红外和蓝牙技术快很多,传输速度比红外块得多,如过双方都使用Android4.2,NFC会直接利用蓝牙传输。这种技术被称为Android Beam。所以使用Android Beam传输数据的两部设备不再限于4厘米之内。

点对点模式的典型应用是两部支持NFC的手机或平板电脑实现数据的点对点传输,例如,交换图片或同步设备联系人。因此,通过NFC,多个设备如数字相机,计算机,手机之间,都可以快速连接,并交换资料或者服务。

二,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标签过滤,两种方式

方式一: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>

activity标签配置该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>

 方式二:初始化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);
	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设备传递过来的intent


//在onNewIntent中处理由NFC设备传递过来的intent
@Override
protected void onNewIntent(Intent intent) {
	super.onNewIntent(intent);
	processIntent(intent);
}

2.6 处理读取过来的数据,包括卡ID(通常为设备序列号,16进制形式09:D1:E6:6B)

然后NFC卡其它信息不一定有,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适合卡片类刷卡数据传输场景,比如门禁卡,支付卡,地铁卡等,快捷方便

三 NFC读卡器通信

3.1  一些手机是不支持NFC的,那么可以选择备选方案,读卡器usb接口插入手机使用。

3.2 但读卡器种类也有很多种,有些是支持键盘模式的,有些是支持usb通信模式的,还有些是支持串口,wifi通信模式。下面主要说下键盘模式,和usb模式通信

3.3 键盘模式比较简单,简单键盘输入事件就可以,大部分扫码枪,读卡器是支持这种模式的,蛀牙源码

private StringBuffer KeyStringBuffer;

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            int unicodeChar = event.getUnicodeChar();
            String number = String.valueOf((char) unicodeChar);
            if (KeyStringBuffer == null) {
                KeyStringBuffer = new StringBuffer();
            }

            KeyStringBuffer.append(number);

            if (KeyStringBuffer != null && KeyStringBuffer.toString().length() >= 8) {
                //都取到的数据
                String resultString = KeyStringBuffer.toString();
               
                KeyStringBuffer = null;

                Log.e("EEE", "dispatchKeyEvent: " + resultString);
                .........自己业务
            }
        }
        return super.dispatchKeyEvent(event);
    }

3.4 USB模式,通过转接器连接手机,usb连接后可能通过读卡器内部协议来进行usb通信,所以需要读卡器厂的协议指令。下面是通用usb通信:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private static final String ACTION_USB_PERMISSION =
            "com.android.example.USB_PERMISSION";

    private UsbManager manager;
    private UsbDevice device;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


    }


    public void openUsbClick(View view) {
        initUsbManager();
    }

    /**
     * 初始化UsbManager
     */
    private void initUsbManager() {
        //获取UsbManager
        manager = (UsbManager) getSystemService(Context.USB_SERVICE);

        //查找设备
        HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
        Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
        while (deviceIterator.hasNext()) {
            UsbDevice device = deviceIterator.next();
            Log.e(TAG, "onCreate: " + device.getDeviceName());
            requestPermission(device);
        }


    }

    /**
     * 请求权限
     */
    private void requestPermission(UsbDevice device) {
        //请求权限
        PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(usbReceiver, filter);
        manager.requestPermission(device, permissionIntent);
    }

    /**
     * 权限回调
     */
    private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {

        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if (device != null) {
                            //call method to set up device communication
                            openDevice();
                        }
                    } else {
                        Log.d(TAG, "permission denied for device " + device);
                    }
                }
            }
        }
    };

    /**
     * 打开设备,接口数据
     */
    private static int TIMEOUT = 100;
    private boolean forceClaim = true;
    private UsbDeviceConnection connection;
    private UsbInterface usbInterface;
    private   UsbEndpoint usbEndpoint;
    private void openDevice() {
        if (connection == null) {
            //打开设备
            connection = manager.openDevice(device);
            // 配置设备接口
            usbInterface = device.getInterface(0);
            connection.claimInterface(usbInterface, forceClaim);
            // 获取设备端点
            usbEndpoint= usbInterface.getEndpoint(0);
            readData();
        }



        //发送数据
        //byte[] dataToSend = "aa".getBytes(); // 需要发送的数据
        //int bytesSent = connection.bulkTransfer(usbEndpoint, dataToSend, dataToSend.length, TIMEOUT);


    }

    //读取数据
    boolean isReading = true;
    private void readData(){
        new Thread(new Runnable(){
            @Override
            public void run() {
                while (isReading){
                    // 接收数据
                    byte[] buffer = new byte[1024];
                    int byteCount = connection.bulkTransfer(usbEndpoint, buffer, buffer.length, 1000);
                    if (byteCount > 0) {
                        // 读取到有效数据
                        String data = new String(buffer, 0, byteCount);
                        // 处理数据
                        Log.e(TAG, "data: "+data );
                    }
                }
            }
        }).start();
    }

    /**
     * 关闭设备
     */
    public void closeConnect() {
        if (connection != null) {
            connection.releaseInterface(usbInterface);
            connection.close();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        closeConnect();
    }

}
  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值