NFCAndroid

【Android】NFC启动指定应用

2019-06-21  本文已影响0人  小八八八八八八

前言

最近公司要实现一个效果,提供几张白卡,通过NFC刷卡来启动我们的应用,找了不少办法:
第一种是直接读取卡id,然后进入应用,但这种方法有个弊端,就是如果有其他监听NFC的应用,会弹出选择框,让你选择使用哪个应用来接收刷卡事件。
第二种是将包名写入到卡中,这次确实不会再弹出选择框了,但是却无法读取到卡内的数据。
第三种就是下面的方法,既可以直接跳转到应用,还可以读取卡的数据。

实现效果

先来看一下效果图:

未写入.gif
这是未写入数据时,刷NFC会跳转到写卡界面
已写入.gif
这是写入成功后,刷卡会直接跳转到读卡界面,读取卡ID并显示。

代码实现

先定义写卡界面:NfcWriteActivity

public class NfcWriteActivity extends AppCompatActivity {
    private static final String TAG = "NfcWriteActivity";
    private NfcAdapter mNfcAdapter;
    private PendingIntent mPendingIntent;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nfc_write);
        if (mNfcAdapter == null) {
            mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        }
        mPendingIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, NfcWriteActivity.class), 0);
        doSomethingWithIntent(getIntent());
    }

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

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

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        doSomethingWithIntent(intent);
    }

    private void doSomethingWithIntent(Intent intent) {
        final Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (tag == null) {
            return;
        }
        writeNFC(tag);
    }

    private void writeNFC(Tag tag) {
        // 这里是将数据写入NFC卡中
        NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord.createExternal("com.bxy.nfc", "nfc", "nfc".getBytes()),NdefRecord.createApplicationRecord("com.bxy.nfc")});
        int size = ndefMessage.toByteArray().length;
        try {
            Ndef ndef = Ndef.get(tag);
            if (ndef != null) {
                ndef.connect();
                if (!ndef.isWritable()) {
                    return;
                }
                if (ndef.getMaxSize() < size) {
                    return;
                }
                try {
                    ndef.writeNdefMessage(ndefMessage);
                    Toast.makeText(this, "写入成功", Toast.LENGTH_LONG).show();
                } catch (FormatException e) {
                    e.printStackTrace();
                }
            } else {
                NdefFormatable format = NdefFormatable.get(tag);
                format.connect();
                format.format(ndefMessage);
                if (format.isConnected()) {
                    format.close();
                }
                Toast.makeText(this, "写入成功", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "写入失败", Toast.LENGTH_LONG).show();
        }
    }
}

读卡界面:NfcReadActivity

public class NfcReadActivity extends AppCompatActivity {
    TextView nfcContentTv;
    private NfcAdapter nfcAdapter;
    private PendingIntent pendingIntent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nfc_read);
        nfcContentTv = findViewById(R.id.tv_nfc_read_content);
        if (nfcAdapter == null) {
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        }
        pendingIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, NfcReadActivity.class), 0);
        disposeIntent(getIntent());
    }

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

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

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        disposeIntent(intent);
    }

    private void disposeIntent(Intent intent){
        String cardId = getCardId(intent);
        if (cardId != null) {
            nfcContentTv.setText(String.format("NFC ID:%s", cardId));
        } else {
            Toast.makeText(this, "未读取到卡ID", Toast.LENGTH_SHORT).show();
        }
    }

    private String getCardId(Intent intent) {
        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        byte[] bytesId = tagFromIntent.getId();// 获取id数组
        return byteArrayToHexString(bytesId);
    }

    private static String byteArrayToHexString(byte[] bytesId) {   //Byte数组转换为16进制字符串
        int i, j, in;
        String[] hex = {
                "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
        String output = "";
        for (j = 0; j < bytesId.length; ++j) {
            in = bytesId[j] & 0xff;
            i = (in >> 4) & 0x0f;
            output += hex[i];
            i = in & 0x0f;
            output += hex[i];
        }
        return output;
    }
}

这里只是把NFC卡的ID拿了出来。
AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bxy.nfc">
    <!-- 一定要注意声明权限 -->
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature
        android:name="android.hardware.nfc"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".activity.NfcReadActivity"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                 <!-- 这里要匹配之前写入卡里的数据 -->
                <data
                    android:host="ext"
                    android:pathPrefix="/com.bxy.nfc:nfc"
                    android:scheme="vnd.android.nfc" />
            </intent-filter>
        </activity>

        <activity
            android:name=".activity.NfcWriteActivity"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <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/techs" />
            <intent-filter>
                <action android:name="android.nfc.action.TAG_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

techs.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.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.MifareClassic</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

如果希望只写入一次,可以在写入成功后调用

Ndef.makeReadOnly()

format.formatReadOnly()

如果有什么建议或问题可以在下面留言,大家一起讨论。

上一篇 下一篇

猜你喜欢

热点阅读