編輯:關於Android編程
NFC簡介:
Near Field Communication 近場通信,是一種數據傳輸技術。
與wifi、藍牙、紅外線等數據傳輸技術的一個主要差異就是有效距離一般不能超過4cm。
NFC支持3種工作模式:
1.讀卡器模式;
2.仿真卡模式;
3.點對點模式;
1.讀卡器模式:
通過NFC設備(支持NFC的Android手機)從帶有NFC芯片的標簽、貼紙、報紙、明信片等媒介讀取信息,或將數據寫到這些媒介中。
2.仿真卡模式:
是將支持NFC的手機或其他電子設備當成借記卡、信用卡、公交卡、門禁卡等IC卡使用;基本原理是將相應的IC卡中的信息(支付憑證)封裝成數據包存儲在支持NFC的手機中,在使用時還需要一個NFC射頻器(相當於刷傳統IC卡時使用的刷卡器),將手機靠近NFC射頻器,手機就會收到NFC射頻器發過來的信號,在通過一系列復雜的驗證後,將IC卡的相應信息傳入NFC射頻器,最後這些IC卡數據會傳入NFC射頻器連接的計算機,並進行相應的處理(如電子轉賬、開門等操作)。
3.點對點模式:
與藍牙、紅外差不多,可以用於不同的NFC設備之間進行數據交換,只是NFC的點對點模式有效距離更短,不能超過4cm;但是如果兩個設備使用的都是Android4.2及以上版本,NFC會直接利用藍牙傳輸,這種技術被稱為Android Beam,所以Android Beam傳輸數據的兩部設備不局限於4cm之內。
基礎知識:
1.Android SDK API主要支持NFC論壇標准(Forum Standard),這種標准被稱為NDEF(NFC Data Exchange Format,NFC數據交換格式);
2.Android SDK API支持如下三種NDEF數據的操作:
a.從NFC標簽讀取NDEF格式的數據;
b.向NFC標簽寫入NDEF格式的數據;
c.通過Android Beam技術將NDEF數據發送到另一部NFC設備;
3.在一個NFC設備讀取NFC標簽或另一個NFC設備中的數據之前會在0.1秒的時間之內建立NFC連接,然後數據會自動從被讀取一端流向讀取數據的一端;數據接收端會根據具體的數據格式和標簽類型調用相應的Activity(這種行為也稱為Tag Dispatch),這些Activity都需要定義Intent Filter,這些Intent Filter中就會指定不同的過濾機制,分為三個級別,也稱為NFC的三重過濾機制。
4.NDEF_DISCOVERED:
只過濾固定格式的NDEF數據。例如:純文本、指定協議(http、ftp、smb等)的URI等;
TECH_DISCOVERED:
當ACTION_NDEF_DISCOVERED指定的過濾機制無法匹配Tag時,就會使用這種過濾機制進行匹配,這種過濾機制並不是通過Tag中的數據格式進行匹配的,而是根據Tag支持的數據存儲格式進行匹配,因此這種過濾機制的范圍更廣;
TAG_DISCOVERED:
如果將NFC過濾機制看成if...else if...else語句的話,那麼這種過濾機制就相當於else部分,當前面兩種過濾機制都匹配失敗後,系統就會利用這種過濾機制來處理,這種過濾機制用來處理未識別的Tag(數據格式不對,而且Tag支持的格式也不匹配)。
5.Android系統會依次匹配NDEF_DISCOVERED、TECH_DISCOVERED和TAG_DISCOVERED;如果通過三重過濾機制仍然無法匹配Tag,則什麼都不做;通常在成功匹配Tag後,Android設備會發出比較清脆的聲音,而未成功匹配Tag,就會發出比較沉悶的聲音。
此過程的處理流程如下圖所示:
6.在manifest文件中需要設置的部分有:
設置權限:
<uses-permission android:name="android.permission.NFC" />
限制Android版本:
android:minSdkVersion="14"
限制安裝的設備:
<uses-feature android:name="android.hardware.nfc" android:required="true" />
設置Activity的Intent Filter,比如設置為三種過濾機制的一種:
<intent-filter> <action android:name="android.nfc.action.TECH_DISCOVERED" /> </intent-filter>
接下來,我們來第一個例子,這個例子是屬於讀卡器模式,從NFC芯片中讀取和寫入數據。
它的manifest文件內容如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.r8c.nfc_demo" android:versionCode="110" android:versionName="1.1.0" > <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="17" /> <!-- NFC權限聲明 --> <uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.r8c.nfc_demo.NfcDemoActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- TECH_DISCOVERED類型的nfc --> <intent-filter> <action android:name="android.nfc.action.TECH_DISCOVERED" /> </intent-filter> <!-- 後設資源 調用自己建立的文件夾xml中的文件 --> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" /> </activity> </application> </manifest>
它的Activity的內容如下,包括讀取、寫入、刪除三大功能:(其中刪除功能是通過寫入空值來實現的)
importjava.io.IOException; importjava.io.UnsupportedEncodingException; importjava.nio.charset.Charset; importandroid.media.AudioManager; importandroid.media.MediaPlayer; importandroid.media.RingtoneManager; importandroid.net.Uri; importandroid.nfc.FormatException; importandroid.nfc.NdefMessage; importandroid.nfc.NdefRecord; importandroid.nfc.NfcAdapter; importandroid.nfc.Tag; importandroid.nfc.tech.MifareUltralight; importandroid.nfc.tech.Ndef; importandroid.nfc.tech.NfcA; importandroid.os.Bundle; importandroid.app.Activity; importandroid.app.PendingIntent; importandroid.content.Context; importandroid.content.Intent; importandroid.content.IntentFilter; importandroid.graphics.Color; importandroid.util.Log; importandroid.view.Menu; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; importandroid.widget.TextView; importandroid.widget.Toast; publicclassNfcDemoActivityextendsActivityimplementsOnClickListener{ //NFC適配器 privateNfcAdapternfcAdapter=null; //傳達意圖 privatePendingIntentpi=null; //濾掉組件無法響應和處理的Intent privateIntentFiltertagDetected=null; //文本控件 privateTextViewpromt=null; //是否支持NFC功能的標簽 privatebooleanisNFC_support=false; //讀、寫、刪按鈕控件 privateButtonreadBtn,writeBtn,deleteBtn; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_nfc_demo); setupViews(); initNFCData(); } @Override protectedvoidonResume(){ super.onResume(); if(isNFC_support==false){ //如果設備不支持NFC或者NFC功能沒開啟,就return掉 return; } //開始監聽NFC設備是否連接 startNFC_Listener(); if(NfcAdapter.ACTION_TECH_DISCOVERED.equals(this.getIntent() .getAction())){ //注意這個if中的代碼幾乎不會進來,因為剛剛在上一行代碼開啟了監聽NFC連接,下一行代碼馬上就收到了NFC連接的intent,這種幾率很小 //處理該intent processIntent(this.getIntent()); } } @Override protectedvoidonPause(){ super.onPause(); if(isNFC_support==true){ //當前Activity如果不在手機的最前端,就停止NFC設備連接的監聽 stopNFC_Listener(); } } @Override protectedvoidonNewIntent(Intentintent){ super.onNewIntent(intent); //當前app正在前端界面運行,這個時候有intent發送過來,那麼系統就會調用onNewIntent回調方法,將intent傳送過來 //我們只需要在這裡檢驗這個intent是否是NFC相關的intent,如果是,就調用處理方法 if(NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())){ processIntent(intent); } } @Override publicvoidonClick(Viewv){ //點擊讀按鈕後 if(v.getId()==R.id.read_btn){ try{ Stringcontent=read(tagFromIntent); if(content!=null&&!content.equals("")){ promt.setText(promt.getText()+"nfc標簽內容:\n"+content +"\n"); }else{ promt.setText(promt.getText()+"nfc標簽內容:\n"+"內容為空\n"); } }catch(IOExceptione){ promt.setText(promt.getText()+"錯誤:"+e.getMessage()+"\n"); Log.e("myonclick","讀取nfc異常",e); }catch(FormatExceptione){ promt.setText(promt.getText()+"錯誤:"+e.getMessage()+"\n"); Log.e("myonclick","讀取nfc異常",e); } //點擊寫後寫入 }elseif(v.getId()==R.id.write_btn){ try{ write(tagFromIntent); }catch(IOExceptione){ promt.setText(promt.getText()+"錯誤:"+e.getMessage()+"\n"); Log.e("myonclick","寫nfc異常",e); }catch(FormatExceptione){ promt.setText(promt.getText()+"錯誤:"+e.getMessage()+"\n"); Log.e("myonclick","寫nfc異常",e); } }elseif(v.getId()==R.id.delete_btn){ try{ delete(tagFromIntent); }catch(IOExceptione){ promt.setText(promt.getText()+"錯誤:"+e.getMessage()+"\n"); Log.e("myonclick","刪除nfc異常",e); }catch(FormatExceptione){ promt.setText(promt.getText()+"錯誤:"+e.getMessage()+"\n"); Log.e("myonclick","刪除nfc異常",e); } } } privatevoidsetupViews(){ //控件的綁定 promt=(TextView)findViewById(R.id.promt); readBtn=(Button)findViewById(R.id.read_btn); writeBtn=(Button)findViewById(R.id.write_btn); deleteBtn=(Button)findViewById(R.id.delete_btn); //給文本控件賦值初始文本 promt.setText("等待RFID標簽"); //監聽讀、寫、刪按鈕控件 readBtn.setOnClickListener(this); writeBtn.setOnClickListener(this); deleteBtn.setOnClickListener(this); } privatevoidinitNFCData(){ //初始化設備支持NFC功能 isNFC_support=true; //得到默認nfc適配器 nfcAdapter=NfcAdapter.getDefaultAdapter(getApplicationContext()); //提示信息定義 StringmetaInfo=""; //判定設備是否支持NFC或啟動NFC if(nfcAdapter==null){ metaInfo="設備不支持NFC!"; Toast.makeText(this,metaInfo,Toast.LENGTH_SHORT).show(); isNFC_support=false; } if(!nfcAdapter.isEnabled()){ metaInfo="請在系統設置中先啟用NFC功能!"; Toast.makeText(this,metaInfo,Toast.LENGTH_SHORT).show(); isNFC_support=false; } if(isNFC_support==true){ init_NFC(); }else{ promt.setTextColor(Color.RED); promt.setText(metaInfo); } } @Override publicbooleanonCreateOptionsMenu(Menumenu){ //Inflatethemenu;thisaddsitemstotheactionbarifitispresent. getMenuInflater().inflate(R.menu.nfc_demo,menu); returntrue; } //字符序列轉換為16進制字符串 privateStringbytesToHexString(byte[]src){ returnbytesToHexString(src,true); } privateStringbytesToHexString(byte[]src,booleanisPrefix){ StringBuilderstringBuilder=newStringBuilder(); if(isPrefix==true){ stringBuilder.append("0x"); } if(src==null||src.length<=0){ returnnull; } char[]buffer=newchar[2]; for(inti=0;i buffer[0]=Character.toUpperCase(Character.forDigit( (src[i]>>>4)&0x0F,16)); buffer[1]=Character.toUpperCase(Character.forDigit(src[i]&0x0F, 16)); System.out.println(buffer); stringBuilder.append(buffer); } returnstringBuilder.toString(); } privateTagtagFromIntent; /** *ParsestheNDEFMessagefromtheintentandprintstotheTextView */ publicvoidprocessIntent(Intentintent){ if(isNFC_support==false) return; //取出封裝在intent中的TAG tagFromIntent=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); promt.setTextColor(Color.BLUE); StringmetaInfo=""; metaInfo+="卡片ID:"+bytesToHexString(tagFromIntent.getId())+"\n"; Toast.makeText(this,"找到卡片",Toast.LENGTH_SHORT).show(); //TechList Stringprefix="android.nfc.tech."; String[]techList=tagFromIntent.getTechList(); //分析NFC卡的類型:MifareClassic/UltraLightInfo StringCardType=""; for(inti=0;i if(techList[i].equals(NfcA.class.getName())){ //讀取TAG NfcAmfc=NfcA.get(tagFromIntent); try{ if("".equals(CardType)) CardType="MifareClassic卡片類型\n不支持NDEF消息\n"; }catch(Exceptione){ e.printStackTrace(); } }elseif(techList[i].equals(MifareUltralight.class.getName())){ MifareUltralightmifareUlTag=MifareUltralight .get(tagFromIntent); StringlightType=""; //TypeInfo switch(mifareUlTag.getType()){ caseMifareUltralight.TYPE_ULTRALIGHT: lightType="Ultralight"; break; caseMifareUltralight.TYPE_ULTRALIGHT_C: lightType="UltralightC"; break; } CardType=lightType+"卡片類型\n"; Ndefndef=Ndef.get(tagFromIntent); CardType+="最大數據尺寸:"+ndef.getMaxSize()+"\n"; } } metaInfo+=CardType; promt.setText(metaInfo); } //讀取方法 privateStringread(Tagtag)throwsIOException,FormatException{ if(tag!=null){ //解析Tag獲取到NDEF實例 Ndefndef=Ndef.get(tag); //打開連接 ndef.connect(); //獲取NDEF消息 NdefMessagemessage=ndef.getNdefMessage(); //將消息轉換成字節數組 byte[]data=message.toByteArray(); //將字節數組轉換成字符串 Stringstr=newString(data,Charset.forName("UTF-8")); //關閉連接 ndef.close(); returnstr; }else{ Toast.makeText(NfcDemoActivity.this,"設備與nfc卡連接斷開,請重新連接...", Toast.LENGTH_SHORT).show(); } returnnull; } //寫入方法 privatevoidwrite(Tagtag)throwsIOException,FormatException{ if(tag!=null){ //新建NdefRecord數組,本例中數組只有一個元素 NdefRecord[]records={createRecord()}; //新建一個NdefMessage實例 NdefMessagemessage=newNdefMessage(records); //解析TAG獲取到NDEF實例 Ndefndef=Ndef.get(tag); //打開連接 ndef.connect(); //寫入NDEF信息 ndef.writeNdefMessage(message); //關閉連接 ndef.close(); promt.setText(promt.getText()+"寫入數據成功!"+"\n"); }else{ Toast.makeText(NfcDemoActivity.this,"設備與nfc卡連接斷開,請重新連接...", Toast.LENGTH_SHORT).show(); } } //刪除方法 privatevoiddelete(Tagtag)throwsIOException,FormatException{ if(tag!=null){ //新建一個裡面無任何信息的NdefRecord實例 NdefRecordnullNdefRecord=newNdefRecord(NdefRecord.TNF_MIME_MEDIA, newbyte[]{},newbyte[]{},newbyte[]{}); NdefRecord[]records={nullNdefRecord}; NdefMessagemessage=newNdefMessage(records); //解析TAG獲取到NDEF實例 Ndefndef=Ndef.get(tag); //打開連接 ndef.connect(); //寫入信息 ndef.writeNdefMessage(message); //關閉連接 ndef.close(); promt.setText(promt.getText()+"刪除數據成功!"+"\n"); }else{ Toast.makeText(NfcDemoActivity.this,"設備與nfc卡連接斷開,請重新連接...", Toast.LENGTH_SHORT).show(); } } //返回一個NdefRecord實例 privateNdefRecordcreateRecord()throwsUnsupportedEncodingException{ //組裝字符串,准備好你要寫入的信息 Stringmsg="BEGIN:VCARD\n"+"VERSION:2.1\n"+"中國湖北省武漢市\n" +"武漢大學計算機學院\n"+"END:VCARD"; //將字符串轉換成字節數組 byte[]textBytes=msg.getBytes(); //將字節數組封裝到一個NdefRecord實例中去 NdefRecordtextRecord=newNdefRecord(NdefRecord.TNF_MIME_MEDIA, "text/x-vCard".getBytes(),newbyte[]{},textBytes); returntextRecord; } privateMediaPlayerring()throwsException,IOException{ //TODOAuto-generatedmethodstub Urialert=RingtoneManager .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); MediaPlayerplayer=newMediaPlayer(); player.setDataSource(this,alert); finalAudioManageraudioManager=(AudioManager)getSystemService(Context.AUDIO_SERVICE); if(audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION)!=0){ player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION); player.setLooping(false); player.prepare(); player.start(); } returnplayer; } privatevoidstartNFC_Listener(){ //開始監聽NFC設備是否連接,如果連接就發pi意圖 nfcAdapter.enableForegroundDispatch(this,pi, newIntentFilter[]{tagDetected},null); } privatevoidstopNFC_Listener(){ //停止監聽NFC設備是否連接 nfcAdapter.disableForegroundDispatch(this); } privatevoidinit_NFC(){ //初始化PendingIntent,當有NFC設備連接上的時候,就交給當前Activity處理 pi=PendingIntent.getActivity(this,0,newIntent(this,getClass()) .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0); //新建IntentFilter,使用的是第二種的過濾機制 tagDetected=newIntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); tagDetected.addCategory(Intent.CATEGORY_DEFAULT); } }
已經提過事件在分發前要做攔截的事情,只不過當時沒有展開來分析,因此這篇文章的主要目的就是分析事件在分發前的攔截過程。(注:Android源碼版本為6.0)我們分析到Inp
導語內存洩漏問題大約是Android開發者最煩惱的問題之一了,項目中連續遇到幾個內存洩漏問題,這裡簡單總結下檢查分析內存洩漏的一些工具與方法。一、什麼是內存洩漏?大家都知
1.概述 Android中關於控制開關和頁面/狀態切換的使用場景還是比較多的。源生做的支持也有比如RadioGroup 和Tabhost等。這裡准備通過自定
先看效果: 京東商城底部菜單欄 新浪微博底部菜單欄本次學習效果圖:第一,主布局文件(啟動頁main.xml,位於res/layout目錄下)代碼&