简单实现通过NFC标签打开固定应用功能

一.功能需求

当手机检测到NFC标签后,可打开相应应用,不管应用是在后台还是不在后台都可打开,当NFC标签离开手机时,会有相应的提示。

 

二.开发环境

Android studio

具有NFC功能的手机

NFC标签纸或NFC卡

 

三.实现步骤

1.首先在AndroidManifest.xml文件中添加如下配置,分别指定安卓SDK版本,NFC权限,要求当前设备必须要有NFC芯片

 <uses-sdk android:minSdkVersion="10" />

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

 <uses-feature
       android:name="android.hardware.nfc"
       android:required="true" />

2.使用intent-filter过滤NFC标签,有三种过滤方式ACTION_NDEF_DISCOVERED,ACTION_TECH_DISCOVERED,ACTION_TAG_DISCOVERED

  可同时配置。如下

 <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.DEFUALT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.TECH_DISCOVERED" />
                <category android:name="android.intent.category.DEFUALT" />
                <data android:resource="@xml/nfc_tech_filter" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="vnd.android.nfc"
                    android:host="ext"
                    android:pathPrefix="/com.example.nfc_test"/>
            </intent-filter>

            <intent-filter>
                <action android:name="android.nfc.action.TAG_DISCOVERED"/>
            </intent-filter>

其中ACTION_TECH_DISCOVERED需要自己新建过滤器nfc_tech_filter.xml,过滤NFC卡的类型如下:

<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>
View Code

完整AndroidManifest.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nfc_test">

    <uses-sdk android:minSdkVersion="10" />

    <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/Theme.NFC_TEST">
        <meta-data
            android:name="com.google.android.actions"
            android:resource="@xml/nfc_tech_filter" />

        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop">
            <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.DEFUALT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.TECH_DISCOVERED" />
                <category android:name="android.intent.category.DEFUALT" />
                <data android:resource="@xml/nfc_tech_filter" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="vnd.android.nfc"
                    android:host="ext"
                    android:pathPrefix="/com.example.nfc_test"/>
            </intent-filter>

            <intent-filter>
                <action android:name="android.nfc.action.TAG_DISCOVERED"/>
            </intent-filter>
        </activity>
    </application>

</manifest>
View Code

3.创建一个基类BaseNfcActivity

package com.example.nfc_test;

import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class BaseNfcActivity extends AppCompatActivity {
    public NfcAdapter mNfcAdapter;
    public PendingIntent mPendingIntent;

    private String TAG = "Android";
    @Override
    protected void onStart() {
        super.onStart();
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);

        if(mNfcAdapter == null){
            //to to sth..
            return;
        }
        else{
            if(!mNfcAdapter.isEnabled()){
                Intent nfcSet = new Intent(Settings.ACTION_NFC_SETTINGS);
                startActivity(nfcSet);
            }
        }
        // 用于感应到NFC时启动该Activity
        // 这里建议将处理NFC的子类的launchMode设置成singleTop模式,这样感应到标签时就会回调onNewIntent,而不会重复打开页面
        mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
    }
    /**
     * 获得焦点,按钮可以点击
     */
   /* @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "bbbbbbbbbbbbbbbbbbbbbbb");
        // 设置当该页面处于前台时,NFC标签会直接交给该页面处理
        if (mNfcAdapter != null) {
         //   Toast.makeText(getApplicationContext(),"NFC标签已离开",Toast.LENGTH_SHORT).show();
            mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
        }

    }*/
    /**
     * 暂停Activity,界面获取焦点,按钮可以点击
     */
    @Override
    public void onPause() {
        super.onPause();
        // 当页面不可见时,NFC标签不交给当前页面处理
        if (mNfcAdapter != null) {
            mNfcAdapter.disableForegroundDispatch(this);
        }
    }
}
View Code

4.创建主活动窗口类MainActivity继承BaseNfcActivity

package com.example.nfc_test;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.nfc.FormatException;
import android.os.Bundle;

import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

/*
public class MainActivity extends AppCompatActivity {

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

public class MainActivity extends BaseNfcActivity {

    private String TAG = "Debug";
    public boolean flag = false;

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

    // 将Activity的launchMode设置成singleTop,这样当感应到NFC标签时不会重复打开页面,而是回调该方法
    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        String action = intent.getAction();
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)// NDEF类型
                || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)// 其他类型
                || NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
            // get tag
            Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            String CardId = ByteArrayToHexString(detectedTag.getId());
            Log.d(TAG, "ID:" + CardId);
            writeTag(detectedTag);
        } else {
            //...
        }


        //write tag
        //  writeTag(detectedTag);
    }

    /**
     * 写标
     *
     * @param tag
     */
    public void writeTag(Tag tag) {
        if (tag == null) {
            return;
        }

        // 利用应用包名创建待写入的数据
        String mPackageName = "com.example.nfc_test";
        NdefMessage ndefMessage = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord
                    .createApplicationRecord(mPackageName)});
        }

//        NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord
//                .createUri(Uri.parse("http://www.baidu.com"))});

        // 获取内容字节大小
        int size = ndefMessage.toByteArray().length;
        try {
            // 获取Nedf
            Ndef ndef = Ndef.get(tag);
            // 不为空表示该标签为Nedf格式
            if (ndef != null && (!ndef.isConnected())) {
                ndef.connect();

                // 是否可写
                if (!ndef.isWritable()) {
                    Toast.makeText(this, "标签不支持写入", Toast.LENGTH_SHORT).show();
                    return;
                }
                // 判断写入内容大小是否超出允许写入的最大值
                if (ndef.getMaxSize() < size) {
                    Toast.makeText(this, "写入内容过大", Toast.LENGTH_SHORT).show();
                    return;
                }
                // 写入数据
                ndef.writeNdefMessage(ndefMessage);
                ndef.close();
                Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
            } else { // 标签非Nedf格式的情况
                NdefFormatable format = NdefFormatable.get(tag);
                // 不为空表示该标签允许格式化成Ndef格式
                if (format != null) {
                    format.connect();
                    // 格式化并写入Nedf内容
                    format.format(ndefMessage);
                    Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "标签不支持Nedf格式", Toast.LENGTH_SHORT).show();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    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;
    }

    @Override
    public void onResume() {
        super.onResume();
        // 设置当该页面处于前台时,NFC标签会直接交给该页面处理
        if (mNfcAdapter != null) {
            //   Toast.makeText(getApplicationContext(),"NFC标签已离开",Toast.LENGTH_SHORT).show();
            mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
            LoopCheckNfc();
        }
    }

    public void LoopCheckNfc() {
        Intent intent = getIntent();
        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

        if (detectedTag != null) {
            Ndef ndef = Ndef.get(detectedTag);
            new Thread(new Runnable() {
                public void run() {
                    try {
                        while (true) {
                            try {
                                Thread.sleep(1000);
                                if (ndef != null && (!ndef.isConnected())) {
                                    ndef.connect();
                                    NdefMessage msg = ndef.getNdefMessage();
                                    // TODO: do something
                                }

                            } catch (IOException e) {
                                // if the tag is gone we might want to end the thread:
                                Looper.prepare();
                                Toast.makeText(getApplicationContext(), "NFC标签已不存在,请确认!", Toast.LENGTH_SHORT).show();
                                Looper.loop();
                                break;
                            } catch (FormatException e) {
                                e.printStackTrace();
                            } finally {
                                try {
                                    ndef.close();
                                } catch (Exception e) {
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        else
        {
            Log.d(TAG, "invalid pointer!");
        }
    }
    

}
View Code

 

综上就是NFC功能打开指定应用的实现。以上只为简单记录,如有错误之处望指出。

posted @ 2021-03-25 14:45  子非魚!  阅读(3009)  评论(0编辑  收藏  举报