在安卓系统中读取 NFC 标签卡上的 ID
场景
APP中读取NFC卡中的标签ID,作为用户的唯一标识进行登录验证。
首先需要确保手机支持NFC功能。其次具备一张NFC卡片。
读取id就是利用的读卡器模式,当把卡片靠近手机的NFC天线的时候,NFC会识别到卡,
然后把卡对象装到intent里面,
并发送广播NfcAdapter.ACTION_TECH_DISCOVERED,
应用程序接到这个广播之后,通过intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)来获取到卡对象,
然后就可以对卡进行读写
注:
博客:
https://blog.****.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
实现
1、新建项目,添加权限
打开Android Studio新建一个项目,在AndroidManifest.xml中添加权限。
<!-- NFC所需权限-->
<uses-permission android:name="android.permission.NFC" />
<!-- 要求当前设备必须要有NFC芯片 -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
2、将要读取NFC的Activity设置为singleTop
这里是在MainActivity中
<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>
</activity>
Activity共有四种启动模式:
standard
标准模式,也是Activity的默认启动模式,允许存在多个Activity实例,
每次启动页面时都会生成一个新的Activity实例。
singleTop
相比于standard,有新的页面启动请求时,当目标Activity处于当前栈顶时,
会调用Activity的onNewIntent()方法,但不创建新实例;其他情况都和standard一致。
其他两种不做介绍。
NFC检测到对象时,会在系统startActivity,那么目标activity已经是启动了,
所以我们需要在onNewIntent方法中接受tag对象,同时activity启动模式设为singleTop或singleTask也为了避免重复创建实例
3、设计页面布局
打开activity_main.xml,修改如下
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#151414"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true"
>
<TextView
android:layout_width="wrap_content"
android:text="读取到的卡UID: "
android:textColor="#fff"
android:layout_height="wrap_content" />
<TextView
android:textColor="#fff"
android:id="@+id/tv_uid"
android:text=" "
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>
4、修改Activity
在OnCreate方法中,获取NfcAdapter实例,然后获取通知,判断支持NFC并且打开后,当获取通知后会调用onNewIntent方法。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取显示文本框
tvUid = (TextView) findViewById(R.id.tv_uid);
//获取NfcAdapter实例
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
//获取通知
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
//如果获取不到则不支持NFC
if (nfcAdapter == null) {
Toast.makeText(MainActivity.this,"设备不支持NFC",Toast.LENGTH_LONG).show();
return;
}
//如果获取到的为不可用状态则未启用NFC
if (nfcAdapter!=null&&!nfcAdapter.isEnabled()) {
Toast.makeText(MainActivity.this,"请在系统设置中先启用NFC功能",Toast.LENGTH_LONG).show();
return;
}
//因为启动模式是singleTop,于是会调用onNewIntent方法
onNewIntent(getIntent());
}
在onNewIntent中,解析intent携带的卡对象
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//获取、传递、解析intent对象,intent中携带卡对象
resolveIntent(intent);
}
//解析intent
void resolveIntent(Intent intent) {
//获取intent中携带的标签对象
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null) {
//处理标签对象
processTag(intent);
}
}
在处理标签对象的方法中获取携带的数据中的ID字节数组并转换成十六进制字符串显示。
//处理tag
public void processTag(Intent intent) {
//获取到卡对象
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//获取卡id这里即uid,字节数组类型
byte[] aa = tagFromIntent.getId();
//字节数组转十六进制字符串
String str = ByteArrayToHexString(aa);
tvUid.setText(str);
}
完整Activity代码
package com.badao.nfcdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private NfcAdapter nfcAdapter;
private PendingIntent pendingIntent;
private TextView tvUid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取显示文本框
tvUid = (TextView) findViewById(R.id.tv_uid);
//获取NfcAdapter实例
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
//获取通知
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
//如果获取不到则不支持NFC
if (nfcAdapter == null) {
Toast.makeText(MainActivity.this,"设备不支持NFC",Toast.LENGTH_LONG).show();
return;
}
//如果获取到的为不可用状态则未启用NFC
if (nfcAdapter!=null&&!nfcAdapter.isEnabled()) {
Toast.makeText(MainActivity.this,"请在系统设置中先启用NFC功能",Toast.LENGTH_LONG).show();
return;
}
//因为启动模式是singleTop,于是会调用onNewIntent方法
onNewIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//获取、传递、解析intent对象,intent中携带卡对象
resolveIntent(intent);
}
//解析intent
void resolveIntent(Intent intent) {
//获取intent中携带的标签对象
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null) {
//处理标签对象
processTag(intent);
}
}
//字节数组转换十六进制
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;
}
//处理tag
public void processTag(Intent intent) {
//获取到卡对象
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//获取卡id这里即uid,字节数组类型
byte[] aa = tagFromIntent.getId();
//字节数组转十六进制字符串
String str = ByteArrayToHexString(aa);
tvUid.setText(str);
}
@Override
protected void onPause() {
super.onPause();
if (nfcAdapter != null)
//设置程序不优先处理
nfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onResume() {
super.onResume();
if (nfcAdapter != null)
//设置程序优先处理
nfcAdapter.enableForegroundDispatch(this, pendingIntent,
null, null);
}
}
5、运行app,打开nfc,将NFC卡片靠近手机
可以以debug模式运行,依次打断点查看效果
推荐阅读
-
什么是数据库事物?为什么需要数据库事物,事物有哪些特征?事物的隔离级别是什么?-1.什么是数据库事务? 1.事务是作为一个逻辑单元执行的一系列操作。一个逻辑工作单元必须具备四个属性,即ACID(原子性、一致性、隔离性和持久性)属性,只有这样才能成为事务: 原子性 2.事务必须是一个原子工作单元;它的数据修改要么全部执行,要么全部不执行。 一致性 3.事务完成时,所有数据必须保持一致。在相关数据库中,所有规则都必须适用于事务的修改,以保持所有数据的完整性。事务结束时,所有内部数据结构(如 B 树索引或双向链接表)必须正确无误。 隔离 4.并发事务的修改必须与其他并发事务的修改隔离。一个事务会在另一个并发事务修改之前或之后查看某一状态下的数据,而不会查看中间状态下的数据。这就是所谓的可序列化,因为它允许重新加载起始数据和重放一系列事务,从而使数据最终处于与原始事务执行时相同的状态。 持久性 5.事务完成后,它对系统的影响是永久性的。即使在系统发生故障的情况下,修改也会保留。 2. 为什么需要数据库事物,事物有哪些特征? 事物对数据库的作用是对数据进行一系列操作,要么全部成功,要么全部失败,防止出现中间状态,确保数据库中的数据始终处于正确、和谐的状态。 特征:原子性、一致性、隔离性、持久性,以及其他特征 原子性(Atomicity):所有操作在事务开始后,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出现错误时,会回滚到事务开始前的状态,所有操作就像没有发生一样。也就是说,事务是一个不可分割的整体,就像化学中的原子一样,是物质的基本单位。 一致性(Consistency):在事务开始之前和结束之后,数据库的完整性约束都没有被破坏。例如,如果 A 转钱给 B,A 不可能扣除这笔钱,但 B 却没有收到这笔钱。 隔离:在同一时间内,只允许一个事务请求相同的数据,不同事务之间没有干扰。例如,甲正在从一张银行卡上取款,在甲取款过程结束之前,乙不能向这张卡转账。 持久性(耐用性):事务完成后,事务对数据库的所有更新都将保存到数据库中,无法回滚 3.事务的隔离级别有哪些? 数据库事务有四种隔离级别,从低到高分别是未提交读取(Read uncommitted)、已提交读取(Read committed)、可重复读取(Repeatable read)、可序列化(Serializable)。此外,事务的并发操作中可能会出现脏读、不可重复读、幽灵读等情况。事务并发问题 脏读:事务 A 读取事务 B 更新的数据,然后事务 B 回滚操作,那么事务 A 读取的数据就是脏数据。 不可重复读取:事务 A 多次读取同一数据,事务 B 在事务 A 多次读取期间更新并提交数据,导致事务 A 多次读取同一数据时结果不一致。 幻影读取:系统管理员 A 将数据库中所有学生的具体分数改为 ABCDE 等级,但系统管理员 B 在此时插入了具体分数的记录,当系统管理员 A 更改结束后发现仍有一条记录未被更改,仿佛发生了幻觉,这称为幻影读取。 小结:不可重复读和幻读容易混淆,不可重复读侧重于修改,幻读侧重于增删。解决不可重复读问题只需锁定满足条件的行,解决幻读问题则需要锁定表 MySQL 事务隔离级别
-
抖音的新版抓包程序,绕过sslpinning直接修改所以抓取https数据包--一般需要抓取https数据包,只需要在电脑上安装抓包工具如fiddler、charles。然后在手机上安装代理,保证手机和电脑在同一个网络上,再在手机上安装证书抓包工具,基本上就可以抓https包了。(安卓版本控制在6.0想兼容,高于6.0就不能抓https数据包了,因为谷歌在安卓N(24)及以上版本中改变了安全行为,系统默认不再信任用户app或系统自定义添加的证书)。
-
在安卓系统中读取 NFC 标签卡上的 ID
-
Android 开发中 nodpi、xhdpi、hdpi、mdpi、ldpi 的概念 - 术语和概念 屏幕尺寸 屏幕的物理尺寸,基于屏幕的对角线长度(如 2.8 英寸、3.5 英寸)。 简而言之,安卓系统将所有屏幕尺寸简化为三大类:大、普通和小。 程序可以为这三种屏幕尺寸提供三种不同的布局选项,然后系统会以合适的方式将布局选项呈现到相应的屏幕上,这个过程不需要程序员用代码进行干预。 屏幕纵横比 屏幕的物理长度与物理宽度之比。程序只需使用系统提供的资源分类器 long(长)和 notlong(不长),就能为具有特定长宽比的屏幕提供配制材料。 分辨率 屏幕的像素总数。请注意,分辨率并不意味着长宽比,尽管在大多数情况下,分辨率表示为 "宽度 x 长度"。在安卓系统中,程序一般不直接处理分辨率。 密度 根据屏幕分辨率,沿屏幕宽度和长度排列的像素数量。 密度较低的屏幕在长度和宽度方向上的像素都相对较少,而密度较高的屏幕通常会在同一区域内排列很多甚至非常非常多的像素。屏幕的密度非常重要;例如,一个界面元素(如按钮)的长度和宽度以像素为单位,在低密度屏幕上会显得很大,但在高密度屏幕上就会显得很小。 独立于密度的像素(DIP)是指程序用来定义界面元素的抽象意义上的像素。它作为一个与实际密度无关的单位,帮助程序员构建布局方案(界面元素的宽度、高度和位置)。 与密度无关的像素在逻辑上与像素密度为 160 DPI 的屏幕上的像素大小相同,而 160 DPI 是安卓平台默认的显示设备。在运行时,平台会以目标屏幕的密度为基准,"透明 "地处理所有所需的 DIP 缩放操作。要将与密度无关的像素转换为屏幕像素,可以使用一个简单的公式:像素 = DIP * (密度 / 160)。例如,在 240 DPI 的屏幕上,1 个 DIP 等于 1.5 个物理像素。强烈建议使用 DIP 来定义程序界面的布局,因为这样可以确保用户界面在所有分辨率的屏幕上都能正常显示。 为了简化程序员在面对各种分辨率时的麻烦,也为了让各种分辨率的平台都能直接运行这些程序,Android 平台将所有屏幕以密度和分辨率作为分类方式,分别分为三类:- 三大尺寸:大、普通、小;- 三种不同密度:高(hdpi)、中(mdpi)和低(ldpi)。DPI 表示 "每英寸点数",即每英寸的像素数。如果需要,程序可以为不同的屏幕尺寸提供不同的资源(主要是布局),为不同的屏幕密度提供不同的资源(主要是位图)。除此之外,程序无需对屏幕尺寸或密度进行任何额外处理。执行时,平台会根据屏幕本身的尺寸和密度特性自动加载相应的资源,并将其从逻辑像素(DIP,用于定义界面布局)转换为屏幕上的物理像素。