使用NFC功能,芯片是NDEF格式的芯片,在4.4版本下的手机一直没有问题,新采购了一批4.4版本的手机,从此麻烦就来了。手机读取芯片时经常会发生卡机的状况,先找到了一部分的原因。
4.4以下系统用onNewIntent(),4.4系统就要通过enableReaderMode()方法,如果4.4系统用onNewIntent()的话,会导致一直P2P,而不是卡与读卡器的关系。
public class ReadActivity extends Activity { /** Called when the activity is first created. */ private NfcAdapter mNfcAdapter; private PendingIntent mPendingIntent; private String[][] techList; private IntentFilter[] intentFilters; private Tag tag; private int sdkVersion; public static final String TXT_FILEPATH = Environment.getExternalStorageDirectory().getPath() + "/files/"; /** * 开始的生命周期:onCreate-->onStart-->OnResume * 从此页面跳转至详细页面:onPause-->OnStop , OnRestart-->OnStart -->OnResume * nfc onPause --> onNewIntent -- >onResume * 从其他页面返回 onNewIntent-->OnRestart-->OnStart -->OnResume */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //屏幕变暗—>黑,但不锁屏 // getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_read_tag); // 支持nfc tag所有的标准 techList = new String[][] { new String[] { android.nfc.tech.NfcV.class.getName() }, new String[] { android.nfc.tech.NfcF.class.getName() }, new String[] { android.nfc.tech.NfcA.class.getName() }, new String[] { android.nfc.tech.NfcB.class.getName() }, new String[] { android.nfc.tech.Ndef.class.getName() }, new String[] { android.nfc.tech.NdefFormatable.class.getName() }, new String[] { android.nfc.tech.MifareClassic.class.getName() }, new String[] { android.nfc.tech.MifareUltralight.class.getName() } }; // 当任意tag被检测到时,你将收到TAG_DISCOVERED intent。因此请注意你应该只处理你想要的Intent。 intentFilters = new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED), new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED), new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED) }; mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); openNfcManager(); sdkVersion = Build.VERSION.SDK_INT; } private void openNfcManager(){ NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE); mNfcAdapter = manager.getDefaultAdapter(); // 实例化NFC设备 // mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { Toast.makeText(this, "您的设备不支持NFC", Toast.LENGTH_SHORT).show(); } else if (mNfcAdapter != null) { if (!mNfcAdapter.isEnabled()) { Toast.makeText(this, "请在系统设置中先启用NFC功能", Toast.LENGTH_SHORT).show(); startActivity(new Intent(Settings.ACTION_NFC_SETTINGS)); } else { Toast.makeText(this, "启动NFC注册成功......", Toast.LENGTH_SHORT).show(); } } } // 当窗口的创建模式是singleTop或singleTask时调用,用于取代onCreate方法 // 当NFC标签靠近手机,建立连接后调用 @SuppressLint("NewApi") @Override public void onNewIntent( Intent intent) { //nfc标签靠近手机,建立连接后调用 if(sdkVersion < Build.VERSION_CODES.KITKAT){ //19 4.4 tagShowMethod(intent); } } @Override public void onResume() { super.onResume(); if(mNfcAdapter != null){ // 使用前台发布系统 mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, intentFilters, techList); enableReaderMode(); } } @TargetApi(19) private void enableReaderMode() { if(sdkVersion < 19){return;} if(sdkVersion >= Build.VERSION_CODES.KITKAT){ //19 4.4 int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS; // | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS; Bundle options = new Bundle(); options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 50);// 延迟对卡片的检测 if (mNfcAdapter != null) { mNfcAdapter.enableReaderMode(ReadActivity.this, new ReaderCallback() { @Override public void onTagDiscovered(Tag tag) { //这里不是主线程,不能直接让textview设置内容, //api19 不从onNewIntent走,而是直接跳到这个回调函数里 tagShowMethodAPI19(tag); } },READER_FLAGS, null); } } } @Override protected void onStart() { super.onStart(); } @Override protected void onRestart() { super.onRestart(); } @Override protected void onPause() { super.onPause(); if(mNfcAdapter != null){ mNfcAdapter.disableForegroundDispatch(this); disableReaderMode(); } } @TargetApi(19) private void disableReaderMode() { if(sdkVersion < 19){return;} mNfcAdapter.disableReaderMode(this); } @Override protected void onDestroy() { super.onDestroy(); mNfcAdapter = null; this.finish(); } @Override protected void onStop() { super.onStop(); } private void tagShowMethod(final Intent intent){ // 获取Tag对象后读取里面的信息 String tagInfo = getTagInfo(intent); Log.e("nfc", "tagInfo =" + tagInfo); if (!TextUtils.isEmpty(tagInfo)) { Date curDate = new Date(); String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.CHINA).format(curDate); writeToTxt(tagInfo, dateStr); } } private void tagShowMethodAPI19(final Tag tag){ // 获取Tag对象后读取里面的信息 String tagInfo = getTagInfoAPI19(tag); Log.e("nfc", "tagInfo === api19 ==" + tagInfo); if (!TextUtils.isEmpty(tagInfo)) { Date curDate = new Date(); String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS", Locale.CHINA).format(curDate); writeToTxt(tagInfo, dateStr); } } private void writeToTxt( String recordNo, String dateStr) { String fileNamePrefix = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(new Date()); String fileName = fileNamePrefix + ".txt"; String s = recordNo + "," + dateStr.substring(11, dateStr.length()); // 创建文件的对象,必须指向创建文件的路径 FileOutputStream fos = null; try { File file = new File(TXT_FILEPATH + fileName); // 如果文件不存在,则创建文件 if (!file.exists()) { if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } file.createNewFile(); } else { // 文件中有内容写入前加入回车符 // FileOutputStream无法写入回车 s = "\r\n" + s; } fos = new FileOutputStream(file, true); byte[] usemms = s.getBytes(); fos.write(usemms, 0, usemms.length); } catch (Exception e) { e.printStackTrace(); }finally { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } private String getTagInfo(Intent intent) { tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); String tagInfo = ""; // nfc tag支持哪几种格式 if (null != tag) { String[] techListStrings = tag.getTechList(); for (String tech : techListStrings) { if (tech == android.nfc.tech.Ndef.class.getName()) { // 解析Ndef格式 tagInfo = parseNDEFMsg(intent); } else if (tech == android.nfc.tech.NfcV.class.getName()) { // 解析NfcV格式 tagInfo = parseNFCVMsg(); } else if (tech == android.nfc.tech.NfcA.class.getName()) { // 解析NfcA格式 } else if (tech == android.nfc.tech.MifareClassic.class.getName()) { // 解析MifareClassic格式 tagInfo = parseMifareClassicMsg(); } } } return tagInfo; } private String getTagInfoAPI19(Tag tag) { // tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); String tagInfo = ""; // nfc tag支持哪几种格式 if (null != tag) { String[] techListStrings = tag.getTechList(); for (String tech : techListStrings) { if (tech == android.nfc.tech.Ndef.class.getName()) { // 解析Ndef格式 tagInfo = parseNDEFMsgAPI19(tag); } else if (tech == android.nfc.tech.NfcV.class.getName()) { // 解析NfcV格式 tagInfo = parseNFCVMsg(); } else if (tech == android.nfc.tech.NfcA.class.getName()) { // 解析NfcA格式 } else if (tech == android.nfc.tech.MifareClassic.class.getName()) { // 解析MifareClassic格式 tagInfo = parseMifareClassicMsg(); } } } return tagInfo; } private synchronized String parseNDEFMsg(Intent intent) { String tagText = ""; Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); // 创建NdefMessage对象和NdefRecord对象 if (rawMsgs != null && rawMsgs.length > 0) { NdefMessage ndefMessage = (NdefMessage) rawMsgs[0]; NdefRecord mNdefRecord = null; if (null != ndefMessage) { mNdefRecord = ndefMessage.getRecords()[0]; } if (null != mNdefRecord) { try { tagText = new String(mNdefRecord.getPayload(), "UTF-8").trim(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } return tagText; } private synchronized String parseNDEFMsgAPI19(Tag tag) { String tagText = ""; Ndef ndef = Ndef.get(tag); try { if(ndef == null){ return tagText; } ndef.connect(); NdefMessage ndefMessage = ndef.getNdefMessage(); NdefRecord mNdefRecord = null; if (null != ndefMessage) { mNdefRecord = ndefMessage.getRecords()[0]; } if (null != mNdefRecord) { tagText = new String(mNdefRecord.getPayload(), "UTF-8").trim(); } } catch (Exception e) { e.printStackTrace(); }finally{ if(ndef != null){ try { ndef.close(); } catch (IOException e) { e.printStackTrace(); } } } return tagText; } public void writeNdefTag(Intent in) { Tag tag = in.getParcelableExtra(NfcAdapter.EXTRA_TAG); Ndef ndef = Ndef.get(tag); try { // 这一句别丢了,读nfc标签的时候不需要这句,因为那时数据直接就在intent中。 ndef.connect(); // 构造一个合适的NdefMessage。你可以看到代码里用了NdefRecord数组,只不过这个数组里只有一个record NdefMessage ndefMsg = new NdefMessage(new NdefRecord[] { createTextRecord("Test-10112") }); ndef.writeNdefMessage(ndefMsg); } catch (IOException e) { e.printStackTrace(); } catch (FormatException e) { e.printStackTrace(); } } // 创建一个封装要写入的文本的NdefRecord对象 public NdefRecord createTextRecord(String text) { // 生成语言编码的字节数组,中文编码 byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII")); // 将要写入的文本以UTF_8格式进行编码 Charset utfEncoding = Charset.forName("UTF-8"); // 由于已经确定文本的格式编码为UTF-8,所以直接将payload的第1个字节的第7位设为0 byte[] textBytes = text.getBytes(utfEncoding); int utfBit = 0; // 定义和初始化状态字节 char status = (char) (utfBit + langBytes.length); // 创建存储payload的字节数组 byte[] data = new byte[1 + langBytes.length + textBytes.length]; // 设置状态字节 data[0] = (byte) status; // 设置语言编码 System.arraycopy(langBytes, 0, data, 1, langBytes.length); // 设置实际要写入的文本 System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); // 根据前面设置的payload创建NdefRecord对象 NdefRecord record = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, NdefRecord.RTD_TEXT, new byte[0], data); return record; } private String parseMifareClassicMsg() { String tagInfo = ""; MifareClassic mc = MifareClassic.get(tag);// 通过intent拿到EXTRA_TAG并转化成MirareClassic格式。 int bCount = 0; int bIndex = 0; try { mc.connect(); // 1K: 16个分区(sector),每个分区4个块(block),每个块(block) 16个byte数据 // 2K: 32个分区,每个分区4个块(block),每个块(block) 16个byte数据 // 4K: 64个分区,每个分区4个块(block),每个块(block) 16个byte数据 // 获得sector总数 int sectorCount = mc.getSectorCount(); System.out.println(sectorCount); for (int i = 0; i < sectorCount; i++) { // 尝试去获得每个sector的认证,只有认证通过才能访问 boolean auth = mc.authenticateSectorWithKeyA(i, MifareClassic.KEY_DEFAULT); if (auth) { // 这句其实不是必须的,因为每个sector中本来就只有4个block bCount = mc.getBlockCountInSector(i); // 我们可以得到每一个sector中的第一个block的编号 bIndex = mc.sectorToBlock(i); for (int j = 0; j < bCount; j++) {// 循环四次拿出一个sector中所有的block // 每次循环bIndex会去++,然后可以得出每一个block的数据。这些数据是字节码,所以你还有一个翻译的工作要做。 byte[] data = mc.readBlock(bIndex); tagInfo += data.toString(); System.out.println(tagInfo); bIndex++; } } else { System.out.println(i); } } } catch (IOException e) { e.printStackTrace(); } return tagInfo; } }AndroidManifest.xml 的配置
<activity android:name=".ReadActivity" android:label="@string/app_name" android:launchMode="singleTask" android:windowSoftInputMode="stateHidden|stateAlwaysHidden" > <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" /> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" /> <intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true" />nfc_tech_filter.xml 的内容
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- 以下是所有的支持 --> <tech-list> <tech>android.nfc.tech.IsoDep</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcV</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcF</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.Ndef</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NdefFormatable</tech> </tech-list> <tech-list> <tech>android.nfc.tech.MifareClassic</tech> </tech-list> <tech-list> <tech>android.nfc.tech.MifareUltralight</tech> </tech-list> </resources>