Android NFC(近程通信)使用

添加權(quán)限

    <uses-permission android:name="android.permission.NFC" />

1. 讀寫文本

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入要寫入的文本"/>

    <TextView
        android:hint="卡中內(nèi)容"
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    /**寫入內(nèi)容的文本框*/
    private EditText mEditText;
    /**顯示NFC卡中的內(nèi)容*/
    private TextView mTextView;

    /**NFC相關(guān)*/
    /**NFC適配器*/
    private NfcAdapter mNfcAdapter;
    /**延時意圖*/
    private PendingIntent mPendingIntent;
    /**檢測的卡類型*/
    private static String[][] sTechArr = null;
    /**意圖過濾器*/
    private static IntentFilter[] sFilters = null;
    static {
        try {
            sTechArr = new String[][]{
                    {IsoDep.class.getName()}, {NfcV.class.getName()}, {NfcF.class.getName()},
                    {MifareUltralight.class.getName()}, {NfcA.class.getName()}
            };
            // 如果這里不設(shè)置過濾器,就必須要在AndroidManifest.xml中為MainActivity設(shè)置過濾器
            // 這里的ACTION在NfcAdapter中有四個選中,個人認(rèn)為是在設(shè)置響應(yīng)優(yōu)先級
            /**
             *
             <intent-filter>
                 <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:mimeType="text/plain" />
             </intent-filter>
             */
            sFilters = new IntentFilter[]{
                    new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED, "*/*")
            };
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

        initView();
        initNfc();
        // 判斷當(dāng)前手機(jī)是否支持NFC
        if (null == mNfcAdapter) {
            // 如果為null,則為不支持。
            Toast.makeText(this, "當(dāng)前設(shè)備不支持NFC功能!", Toast.LENGTH_SHORT).show();
            return;
        }
    }

    /**
     * 初始化NFC
     */
    private void initNfc() {
        // 1. 初始化Nfc適配器
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        // 2. 初始化延時意圖
        mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 3. 允許后臺喚醒--此處是指應(yīng)用處于不可見的狀態(tài)時,將卡片貼近手機(jī)時,喚醒當(dāng)前App。
        // 如果sFilters為null,則為不過濾任何意圖;如果sTechArr為null時,則為接收所有類型的卡片。
        mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, sFilters, sTechArr);
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 4. 禁止后臺喚醒
        mNfcAdapter.disableForegroundDispatch(this);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 5. 重寫OnIntent方法, 讀取到的數(shù)據(jù)存儲在intent中
        // 獲取到意圖中的Tag數(shù)據(jù)
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//        TAG: Tech [android.nfc.tech.NfcV, android.nfc.tech.Ndef]
        Log.e(TAG, "onNewIntent: " + tag);
        // 此處判斷輸入框mEditText的內(nèi)容是否為空,如果為空,則為讀取數(shù)據(jù);如果不為空,則為寫入數(shù)據(jù)
        if (TextUtils.isEmpty(mEditText.getText().toString())) {
            // 數(shù)據(jù)為空,讀取數(shù)據(jù)
            readData(tag);
        } else {
            // 數(shù)據(jù)不為空,寫入數(shù)據(jù)
            writeData(tag);
        }

    }

    /**
     * 讀取數(shù)據(jù)
     * @param tag Tag
     */
    private void readData(Tag tag) {
        // 從Tag中獲取Ndef信息
        Ndef ndef = Ndef.get(tag);
        try {
            // 連接
            ndef.connect();
//            readData: type: android.ndef.unknown, size: 250, message: NdefMessage [NdefRecord tnf=1 type=54 payload=027A68E4BDA0E5A5BD]
            Log.e(TAG, "readData: type: " + ndef.getType() + ", size: " + ndef.getMaxSize() + ", message: " + ndef.getNdefMessage().toString());
            // 獲取NdefMessage消息
            NdefMessage ndefMessage = ndef.getNdefMessage();
            // 獲取NdefRecord記錄
            //ndefMessage.getRecords();
            // 遍歷數(shù)組
            StringBuilder stringBuilder = new StringBuilder();
            // 文本
            String text;
            for (NdefRecord ndefRecord : ndefMessage.getRecords()) {
                text = parseNdefRecord(ndefRecord);
                if (!TextUtils.isEmpty(text)) {
                    stringBuilder.append(text);
                }
            }
            // 設(shè)置文本
            mTextView.setText(stringBuilder.toString());
        } catch (IOException | FormatException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析NdefRecord數(shù)據(jù)
     * @param ndefRecord NdefRecord記錄
     */
    private String parseNdefRecord(NdefRecord ndefRecord) {
        // 判斷是否為文本格式數(shù)據(jù)
        if (NdefRecord.TNF_WELL_KNOWN != ndefRecord.getTnf()) {
            return "";
        }
        if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
            return "";
        }

        try {
            // 獲得字節(jié)流
            byte[] payload = ndefRecord.getPayload();
            // 獲得編碼格式
            String textEncoding = ((payload[0] & 0x80) == 0) ? "utf-8" : "utf-16";
            // 獲得語言編碼長度
            int languageCodeLength = payload[0] & 0x3f;
            // 語言編碼
            String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
            // 獲取文本
            return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength -1, textEncoding);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 寫數(shù)據(jù)
     * @param tag Tag
     */
    private void writeData(Tag tag) {
        // 創(chuàng)建一個NdefMessage消息
        NdefMessage ndefMessage = new NdefMessage(createTextRecord(mEditText.getText().toString()));
        // 寫入Tag
        if (writeTag(ndefMessage, tag)) {
            mEditText.setText("");
            Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "寫入失敗", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 將NdefMessage消息寫入Tag
     * @param ndefMessage Ndef消息
     * @param tag Tag
     */
    private boolean writeTag(NdefMessage ndefMessage, Tag tag) {
        try {
            // 獲取Ndef
            Ndef ndef = Ndef.get(tag);
            // 連接
            ndef.connect();
            // 寫入數(shù)據(jù)
            ndef.writeNdefMessage(ndefMessage);
            return true;
        } catch (IOException | FormatException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 創(chuàng)建文本信息
     * @param text 要寫入的文本信息
     */
    private NdefRecord createTextRecord(String text) {
        // 設(shè)置語言
        byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));
        // 設(shè)置編碼
        Charset utfEncoding = Charset.forName("UTF-8");
        // 要寫入的二進(jìn)制數(shù)據(jù)
        byte[] textBytes = text.getBytes(utfEncoding);
        int utfBit = 0;
        // 將語言長度轉(zhuǎn)化對應(yīng)的字符
        char status = (char)(utfBit + langBytes.length);
        // 創(chuàng)建一個大小為 1(即語言長度轉(zhuǎn)化對應(yīng)的字符) + 語言字節(jié)長度 + 文本字節(jié)長度的字節(jié)數(shù)組
        byte[] data = new byte[1 + langBytes.length + textBytes.length];
        // 寫入第一位數(shù)據(jù)
        data[0] = (byte) status;
        // 寫入語言字節(jié)
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        // 寫入文本數(shù)據(jù)
        System.arraycopy(textBytes, 0, data, langBytes.length + 1, textBytes.length);
        // 創(chuàng)建一個NdefRecord記錄
        return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
    }

    /**
     * 初始化布局
     */
    private void initView() {
        mEditText = (EditText) this.findViewById(R.id.editText);
        mTextView = (TextView) this.findViewById(R.id.textView);
    }
}

2. NFC讀寫Uri

對NFC的配置信息相同,此處只是更改讀取與寫入的操作。

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 5. 重寫OnIntent方法, 讀取到的數(shù)據(jù)存儲在intent中
        // 獲取到意圖中的Tag數(shù)據(jù)
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//        TAG: Tech [android.nfc.tech.NfcV, android.nfc.tech.Ndef]
        Log.e(TAG, "onNewIntent: " + tag);
        // 此處判斷輸入框mEditText的內(nèi)容是否為空,如果為空,則為讀取數(shù)據(jù);如果不為空,則為寫入數(shù)據(jù)
        if (TextUtils.isEmpty(mEditText.getText().toString())) {
            // 數(shù)據(jù)為空,讀取數(shù)據(jù)
            readData(tag);
        } else {
            // 數(shù)據(jù)不為空,寫入數(shù)據(jù)
            writeData(tag);
        }

    }

    /**
     * 寫入Uri
     * http://www.baidu.com/
     * @param tag
     */
    private void writeData(Tag tag) {
        // 創(chuàng)建一個Uri
        Uri uri = Uri.parse(mEditText.getText().toString());
        // 創(chuàng)建一個NdefMessage格式數(shù)據(jù)
        NdefMessage ndefMessage = new NdefMessage(NdefRecord.createUri(uri));
        if (writeTag(ndefMessage, tag)) {
            Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();
            mEditText.setText("");
        }
    }

    /**
     * 寫入數(shù)據(jù)
     * @param ndefMessage Ndef格式數(shù)據(jù)
     * @param tag Tag
     * @return true,寫入成功;false,寫入失敗
     */
    private boolean writeTag(NdefMessage ndefMessage, Tag tag) {
        // 計(jì)算寫入數(shù)據(jù)的長度
        int size = ndefMessage.toByteArray().length;
        try {
            // 獲取Ndef
            Ndef ndef = Ndef.get(tag);
            if (null != ndef) {
                // 連接NFC卡片
                ndef.connect();
                // 判斷NFC芯片是否可寫
                if (!ndef.isWritable()) {
                    Toast.makeText(this, "卡片不可寫", Toast.LENGTH_SHORT).show();
                    return false;
                }
                // 判斷要寫入的數(shù)據(jù)是否大于NFC芯片最大字節(jié)數(shù)
                if (ndef.getMaxSize() < size) {
                    Toast.makeText(this, "寫入數(shù)據(jù)過大", Toast.LENGTH_SHORT).show();
                    return false;
                }
                ndef.writeNdefMessage(ndefMessage);
                return true;
            } else {
                // 獲取一個格式化工具
                NdefFormatable formatable = NdefFormatable.get(tag);
                // 判斷是否為空
                if (null != formatable){
                    // 連接
                    formatable.connect();
                    // 格式化并寫入數(shù)據(jù)
                    formatable.format(ndefMessage);
                    Toast.makeText(this, "格式化成功,并成功寫入數(shù)據(jù)", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(this, "無法格式化", Toast.LENGTH_SHORT).show();
                }
            }
        } catch (IOException | FormatException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 讀取數(shù)據(jù)
     * @param tag Tag
     */
    private void readData(Tag tag) {
        try {
            // 獲取Ndef
            Ndef ndef = Ndef.get(tag);
            // 連接
            ndef.connect();
            Log.e(TAG, "readData: type: " + ndef.getType() + ", size: " + ndef.getMaxSize()
                    + ", message: " + ndef.getNdefMessage().toString());
            // 獲取Ndef格式數(shù)據(jù)
            NdefMessage ndefMessage = ndef.getNdefMessage();
            // 獲取信息中的記錄
            NdefRecord[] records = ndefMessage.getRecords();
            // 判斷是否有數(shù)據(jù)
            if (null != records && records.length > 0) {
                StringBuilder stringBuilder = new StringBuilder();
                // 迭代
                for (NdefRecord ndefRecord : records) {
                    Uri uri = ndefRecord.toUri();
                    if (null != uri) {
                        stringBuilder.append(uri.toString());
                    }
                }
                String string = stringBuilder.toString();
                mTextView.setText(string);
                if (string.startsWith("http://")) {
                    // 跳轉(zhuǎn)網(wǎng)頁
                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(stringBuilder.toString())));
                }
            }
            // 斷開連接
            ndef.close();
        } catch (IOException | FormatException e) {
            e.printStackTrace();
        }
    }

3. 讀寫非Ndef格式數(shù)據(jù)


    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 5. 重寫OnIntent方法, 讀取到的數(shù)據(jù)存儲在intent中
        // 獲取到意圖中的Tag數(shù)據(jù)
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        // 可打印出當(dāng)前NFC芯片支持的NFC類型
//        onNewIntent: TAG: Tech [android.nfc.tech.NfcA, android.nfc.tech.MifareUltralight, android.nfc.tech.NdefFormatable]
        Log.e(TAG, "onNewIntent: " + tag);
        // 此處判斷輸入框mEditText的內(nèi)容是否為空,如果為空,則為讀取數(shù)據(jù);如果不為空,則為寫入數(shù)據(jù)
        if (TextUtils.isEmpty(mEditText.getText().toString())) {
            // 數(shù)據(jù)為空,讀取數(shù)據(jù)
            readData(tag);
        } else {
            // 數(shù)據(jù)不為空,寫入數(shù)據(jù)
            writeData(tag);
        }
    }

    /**
     * 寫數(shù)據(jù)
     * @param tag Tag
     */
    private void writeData(Tag tag) {
        // 獲取到MifareUltralight
        MifareUltralight ultralight = MifareUltralight.get(tag);
        try {
            // 連接
            ultralight.connect();
            // Write 1 page (4 bytes).
            // 往每一頁寫數(shù)據(jù),最多寫四個字節(jié)
            ultralight.writePage(4,"中國".getBytes(Charset.forName("gb2312")));
            ultralight.writePage(5,"美國".getBytes(Charset.forName("gb2312")));
            ultralight.writePage(6,"英國".getBytes(Charset.forName("gb2312")));
            ultralight.writePage(7,"法國".getBytes(Charset.forName("gb2312")));
            Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();
            mEditText.setText("");
            // 關(guān)閉連接
            ultralight.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 讀數(shù)據(jù)
     * @param tag Tag
     */
    private void readData(Tag tag) {
        // 獲取連接
        MifareUltralight ultralight = MifareUltralight.get(tag);
        try {
            // 連接
            ultralight.connect();
            byte[] bytes = ultralight.readPages(4);
            mTextView.setText(new String(bytes, Charset.forName("gb2312")));
            // 斷開連接
            ultralight.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

4. AndroidBeam打開其他手機(jī)應(yīng)用

/**
 * 測試方法, 將兩部手機(jī)靠近,AndroidBeam接觸時,兩部手機(jī)界面同時縮小居中,輕觸處理數(shù)據(jù)的手機(jī)一方,即可完成發(fā)送,
 * 放松數(shù)據(jù)期間手機(jī)不能離開,離開之后即會斷開連接,終止發(fā)送。
 */
public class MainActivity extends AppCompatActivity implements
        NfcAdapter.CreateNdefMessageCallback, NfcAdapter.OnNdefPushCompleteCallback{
    private static final String TAG = "MainActivity";
    private NfcAdapter mNfcAdapter;
    private PendingIntent mPendingIntent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);

        // 設(shè)置發(fā)送消息的回調(diào)
        mNfcAdapter.setNdefPushMessageCallback(this, this);
        // 設(shè)置發(fā)送完成回調(diào)
        mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Toast.makeText(this, "NFC靠近", Toast.LENGTH_SHORT).show();
    }

    /**
     * 當(dāng)有NFC靠近時調(diào)用該方法
     */
    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        // 將"com.mazaiting.nfc2"更換為另一部手機(jī)中已存在的應(yīng)用包名
        return new NdefMessage(NdefRecord.createApplicationRecord("com.mazaiting.nfc2"));
    }

    /**
     * 數(shù)據(jù)發(fā)送完成時調(diào)用方法
     */
    @Override
    public void onNdefPushComplete(NfcEvent event) {
        Toast.makeText(this, "發(fā)送完成", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,
                null,null);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mNfcAdapter.disableForegroundDispatch(this);
    }
}

AndroidBeam接觸時如下圖:


圖1.png

5. AndroidBeam傳送大文件

1). 添加權(quán)限,一定不能忘記

    <uses-permission android:name="android.permission.NFC"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2). 在assets文件夾放一張圖片或者文件,或者直接指定手機(jī)中的文件地址。

public class MainActivity extends AppCompatActivity implements
        NfcAdapter.CreateBeamUrisCallback, NfcAdapter.OnNdefPushCompleteCallback {
    private static final String TAG = "MainActivity";
    private NfcAdapter mNfcAdapter;
    private PendingIntent mPendingIntent;

    private final String targetFilename = "/sdcard/image.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        mPendingIntent = PendingIntent.getActivity(this,0,
                new Intent(this,getClass()),0);
        try{
            InputStream is = getResources().getAssets().open("image.jpg");
            FileOutputStream fos = new FileOutputStream(targetFilename);
            byte[] buffer = new byte[1024];
            int len = 0;
            while((len = is.read(buffer))!=-1){
                fos.write(buffer,0,len);
            }
            fos.close();
            is.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        // 設(shè)置回調(diào)
        mNfcAdapter.setBeamPushUrisCallback(this, this);
        mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
    }

    @Override
    public Uri[] createBeamUris(NfcEvent event) {
        Uri[] uris = new Uri[1];
        Uri uri =  Uri.parse("file://" + targetFilename);
        uris[0]=uri;
        return uris;
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Toast.makeText(this, "接觸", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,
                null,null);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mNfcAdapter.disableForegroundDispatch(this);
    }

    @Override
    public void onNdefPushComplete(NfcEvent event) {
        // 另一部手機(jī)接收的文件位置--/storage/emulated/0/beam/傳輸?shù)奈募?        Log.e(TAG, "onNdefPushComplete: ");
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容