要点:

以下示例是:

1:入参和读取到的返回结果:都采用Android与js交互的方式处理

2:读取卡片等的 某一扇区(该扇区带有秘钥)的信息并解析,扇区和秘钥都可以在前端手动入参

3:如果想要获取所有扇区的信息,可以放开 processIntent 方法中的一些注释(我保留了获取所有扇区信息的代码)稍加修改测试即可。

//主干代码



package com.xxxx.xxx;



import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.os.Build;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.greencloud.gcsunmi.utils.ICallbackUtil;
import com.greencloud.gcsunmi.utils.ToHexStringUtils;

public class MainActivity extends Activity {
  WebView mWebView;
  NfcAdapter nfcAdapter;
  PendingIntent mPendingIntent;

  //读卡 扇区+秘钥
  String Sector = "";
  String Key = "";

  

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

    initViews();
    mWebView.getSettings().setDefaultTextEncodingName("utf-8");// 设置编码
    mWebView.getSettings().setJavaScriptEnabled(true);// 支持js
    mWebView.setWebChromeClient(new WebChromeClient());
    mWebView.setWebViewClient(new WebViewClientDemo());//添加一个页面相应监听类
    mWebView.getSettings().setDomStorageEnabled(true);//支持HTML5中的一些控件标签
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      mWebView.getSettings().setMediaPlaybackRequiresUserGesture(false);//支持前端音频自动播放
    }
    // 载入包含js的html
    mWebView.loadData("", "text/html", null);
    mWebView.loadUrl("file:///android_asset/www/index.html");
    

    //nfc功能初始化
    nfccheck();
    mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

  }

  /**
   * 注册
   */
  class WebViewClientDemo extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      // 当打开新链接时,使用当前的 WebView,不会使用系统其他浏览器
      view.loadUrl(url);
      return true;
    }

    @Override
    public void onPageFinished(WebView view, String url) {
      super.onPageFinished(view, url);
      /**
       * 注册JavascriptInterface,其中"lee"的名字随便取,如果你用"lee",那么在html中只要用  lee.方法名()
       * 即可调用MyJavascriptInterface里的同名方法,参数也要一致
       */
      mWebView.addJavascriptInterface(new JsObject(), "lee");
    }

  }

  public void initViews() {
    mWebView = (WebView) findViewById(R.id.wv_view);
  }


  /**
   * js调用的安卓方法
   */
  class JsObject {

    ICallbackUtil callback = new ICallbackUtil();
    //读卡接口
    //sector扇区 key秘钥
    @JavascriptInterface
    public void funSunmiReadCard(final String sector, final String key) {
      Sector = sector;
      Key = key;
    }
    
  }


  /**
   * 安卓调用js的方法 输出结果
   *
   * @param me
   */

  //读卡
  public void forJSrecard(final String me) {
    mWebView.post(new Runnable() {
      @Override
      public void run() {
        mWebView.loadUrl("javascript:recardresult(" + me + ")");
      }
    });
  }

  /**
   * 相关服务配置:
   */

  private void nfccheck() {
    // 获取默认的NFC控制器
    nfcAdapter = NfcAdapter.getDefaultAdapter(MainActivity.this);
    if (nfcAdapter == null) {
      forJSrecard("'设备不支持NFC!'");
      finish();
      return;
    }
    if (!nfcAdapter.isEnabled()) {
      forJSrecard("'请在系统设置中先启用NFC功能!'");
      finish();
      return;
    }
  }

  //启动前台调度系统
  @Override
  protected void onResume() {
    super.onResume();
    nfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
  }

  //关闭前台调度系统
  @Override
  protected void onPause() {
    super.onPause();
    if (nfcAdapter != null) {
      nfcAdapter.disableForegroundDispatch(this);
    }
  }

  //onNewIntent回调方法
  @Override
  protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
    // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
    if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
      if (!Key.equals("") && Key != null) {
        processIntent(intent, Integer.valueOf(Sector), Key);
      }
    }
  }

  /**
   * 读卡
   * int sectorIndex 指定扇区
   * String authKey 对应的秘钥
   * //注意,如果扇区传0,秘钥只传字符串"ID",说明只是想获取物理卡号,返回给前端物理卡号即可
   */
  private void processIntent(Intent intent, int sectorIndex, String authKey) {

    //取出封装在intent中的TAG
    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    byte[] bytesId = tagFromIntent.getId();// 获取id数组
    String idinfo = ToHexStringUtils.ByteArrayToHexString(bytesId);
    for (String tech : tagFromIntent.getTechList()) {
    }
    boolean auth = false;
    //如果密钥只传了"ID",返回给前端会员卡的物理ID即可
    if (authKey != null && authKey.equals("ID")) {
      forJSrecard("'" + idinfo + "'");
    } else {
      //如果密钥只传了"NID",秘钥读卡,后台秘钥写死即可
      //读取TAG
      MifareClassic mfc = MifareClassic.get(tagFromIntent);
      try {
        String metaInfo = "";
        String msg = "";
        mfc.connect();
        int type = mfc.getType();//获取TAG的类型
        int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
        String typeS = "";
        switch (type) {
          case MifareClassic.TYPE_CLASSIC:
            typeS = "TYPE_CLASSIC";
            break;
          case MifareClassic.TYPE_PLUS:
            typeS = "TYPE_PLUS";
            break;
          case MifareClassic.TYPE_PRO:
            typeS = "TYPE_PRO";
            break;
          case MifareClassic.TYPE_UNKNOWN:
            typeS = "TYPE_UNKNOWN";
            break;
        }
        //            metaInfo += "卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共"
        //                    + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n"+"Id标签是"+info+"\n+"+"\n";
        //            for (int j = 0; j < sectorCount; j++) {
       
        //解析扇区秘钥信息
        auth = mfc.authenticateSectorWithKeyA(sectorIndex, ToHexStringUtils.hexStringToByte("568G789F334"));
        int bCount;
        int bIndex;
        if (auth) {
          //                    metaInfo += "Sector " + j + ":验证成功\n";
          // 读取扇区中的块
          bCount = mfc.getBlockCountInSector(sectorIndex);
          bIndex = mfc.sectorToBlock(sectorIndex);
          for (int i = 0; i < 3; i++) {
            byte[] data = mfc.readBlock(bIndex);
            metaInfo += ToHexStringUtils.toStringHex2(ToHexStringUtils.bytesToHexString(data));
            bIndex++;
          }
        } else {
          //                    metaInfo += "sector:" + sectorIndex + ":验证失败\n";
          metaInfo += "";
        }
        //            }
        forJSrecard("'" + metaInfo + "'");
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
  
}



AndroidMainfest.xml文件配置



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



<application
        android:allowBackup="true"
        android:icon="@mipmap/logo_cy"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.Translucent.NoTitleBar">
        <activity android:name=".MainActivity"
            android:hardwareAccelerated="true">
            <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.TAG_DISCOVERED" />
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>

    </application>



JS前端交互代码



<body>
  <a id="sn">读卡</a>
  <div id="snback"></div>

    <script type="text/javascript">
        document.querySelector('#sn').addEventListener('click', function () {
            javascript: lee.funSunmiReadCard(sector,key);
            return false;
        }, false);
        function recardresult(str) {
            document.querySelector("#snback").innerHTML = str
        }
        getidresult("ddd")
    </script>

</body>



Android如果要读取前端代码,需要在与java目录,同等级目录下新增 assets 文件夹(注意,文件夹命名必须是这个名称,安卓规定的)



安卓原生读取nfcjava代码 原生安卓 nfc_安卓



Mainactivity页面的代码



// 载入包含js的html
mWebView.loadData("", "text/html", null);
mWebView.loadUrl("file:///android_asset/www/index.html");



就是为了读取js等前端页面(这里我又在assets文件夹下新增了一个www的文件夹)