我一直在Android Studio中开发应用程序来读写NFC标签,特别是Mifare Classic标签。我在2016年初(一年前)在我的智能手机(使用S.O. KitKat)上设法开发并测试了它。
正如我所提到的,请不要使用该应用程序,并在更新Android Studio版本,SDK和S.O.之后。从我的智能手机到MarshMallow,尝试写入标签时出现此错误:" java.lang.NullPointerException:尝试调用虚方法' void android.nfc.tech.MifareClassic.connect()&# 39;在空对象引用"。
尝试连接MifareClassic标记时显然会生成此错误。
附上我的活动代码,用我认为无关的部分代替部分。
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.view.View.OnClickListener;
...
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@SuppressLint("Escribir")
public class escribir extends Activity {
NfcAdapter adapter;
PendingIntent pendingIntent;
IntentFilter writeTagFilters[];
boolean writeMode;
Tag myTag;
MifareClassic mfc;
Context context;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_datospropietario);
context = this;
...
Button btnWrite = (Button)findViewById(R.id.button);
final String b = getIntent().getExtras().getString("datos");
btnWrite.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final String mensaje = (b + ...);
if (first.getText().toString().isEmpty()) {
Toast.makeText(context, context.getString(R.string.missing_fields), Toast.LENGTH_SHORT).show();
} else {
if (myTag == null) {
Toast.makeText(context, context.getString(R.string.error_notag), Toast.LENGTH_LONG).show();
} else {
MifareClassic tmpMFC = null;
try {
tmpMFC = MifareClassic.get(myTag);
} catch (Exception e) {
Toast.makeText(context, context.getString(R.string.error_notag), Toast.LENGTH_LONG).show();
e.printStackTrace();
}
mfc = tmpMFC;
int sect;
if (mfc != null) {
sect = mfc.getSectorCount();
}
try {
mfc.connect();
...
} catch (IOException e) {
Toast.makeText(context, context.getString(R.string.error_notag), Toast.LENGTH_LONG).show();
e.printStackTrace();
myTag = null;
}
}
}
});
adapter = NfcAdapter.getDefaultAdapter(this);
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
writeTagFilters = new IntentFilter[]{tagDetected};
}
private void write(String text, Tag tag, int sector) throws IOException, FormatException {
NdefRecord[] records = {createRecord(text), NdefRecord.createApplicationRecord("my_app")};
NdefMessage mensaje = new NdefMessage(records);
NdefFormatable formatable = NdefFormatable.get(tag);
if (formatable != null) {
formatable.connect();
formatable.format(mensaje);
formatable.close();
} else {
Ndef ndef = Ndef.get(tag);
ndef.connect();
ndef.writeNdefMessage(mensaje);
ndef.close();
}
MifareClassic mfc = MifareClassic.get(tag);
...
}
@SuppressLint("Escribir") private NdefRecord createRecord(String text) throws UnsupportedEncodingException{
String lang = "es";
byte[] textBytes = text.getBytes();
byte[] langBytes = lang.getBytes("US-ASCII");
int langLength = langBytes.length;
int textLength = textBytes.length;
byte[] payLoad = new byte[1 + langLength + textLength];
payLoad[0] = (byte) langLength;
System.arraycopy(langBytes, 0, payLoad, 1, langLength);
System.arraycopy(textBytes, 0, payLoad, 1 + langLength, textLength);
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payLoad);
}
@SuppressLint("Escribir") protected void onNewIntent(Intent intent){
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
}
}
public void onPause(){
super.onPause();
WriteModeOff();
}
public void onResume(){
super.onResume();
WriteModeOn();
}
@SuppressLint("Escribir") private void WriteModeOn(){
writeMode = true;
adapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null);
}
@SuppressLint("Escribir") private void WriteModeOff(){
writeMode = false;
adapter.disableForegroundDispatch(this);
}
}
答案 0 :(得分:1)
在研究了互联网这个问题之后,我发现NFC标签上的写问题出现在各种设备上,例如一些HTC或索尼Xperia型号。将Android版本升级到SDK 5.1(Lollipop)后,会出现这些问题。
这个问题的起源是因为一些制造商修改了堆栈顺序,其中发现了不同的Tag类型,在请求TechExtras并返回SAK null或错误值时导致冲突。
我发现的详细解释如下:
HTC One:看来,这个错误的原因是NfcA的TechExtras是null。 但是,TechList包含MifareClassic。
Sony Xperia Z3(+ emmulated MIFARE Classic标签):有缺陷的标签在TechList中有两个具有不同SAK值的NfcA和一个MifareClassic(带有第二个NfcA的Extra)。第二种NfcA和MifareClassic技术都具有0x20的SAK。根据恩智浦关于识别MIFARE标签的指南(第11页),这是一个MIFARE Plus或MIFARE DESFire标签。此方法使用NfcA出现的SAK值ORed(如NXP的MIFARE类型识别程序指南中所述)创建一个新的额外值,并将第一个NfcA的Extra替换为新的。
有关详细信息,请参阅https://github.com/ikarus23/MifareClassicTool/issues/52
bildin用户提出的补丁修复了我的问题:
public Tag patchTag(Tag oTag)
{
if (oTag == null)
return null;
String[] sTechList = oTag.getTechList();
Parcel oParcel, nParcel;
oParcel = Parcel.obtain();
oTag.writeToParcel(oParcel, 0);
oParcel.setDataPosition(0);
int len = oParcel.readInt();
byte[] id = null;
if (len >= 0)
{
id = new byte[len];
oParcel.readByteArray(id);
}
int[] oTechList = new int[oParcel.readInt()];
oParcel.readIntArray(oTechList);
Bundle[] oTechExtras = oParcel.createTypedArray(Bundle.CREATOR);
int serviceHandle = oParcel.readInt();
int isMock = oParcel.readInt();
IBinder tagService;
if (isMock == 0)
{
tagService = oParcel.readStrongBinder();
}
else
{
tagService = null;
}
oParcel.recycle();
int nfca_idx=-1;
int mc_idx=-1;
for(int idx = 0; idx < sTechList.length; idx++)
{
if(sTechList[idx] == NfcA.class.getName())
{
nfca_idx = idx;
}
else if(sTechList[idx] == MifareClassic.class.getName())
{
mc_idx = idx;
}
}
if(nfca_idx>=0&&mc_idx>=0&&oTechExtras[mc_idx]==null)
{
oTechExtras[mc_idx] = oTechExtras[nfca_idx];
}
else
{
return oTag;
}
nParcel = Parcel.obtain();
nParcel.writeInt(id.length);
nParcel.writeByteArray(id);
nParcel.writeInt(oTechList.length);
nParcel.writeIntArray(oTechList);
nParcel.writeTypedArray(oTechExtras,0);
nParcel.writeInt(serviceHandle);
nParcel.writeInt(isMock);
if(isMock==0)
{
nParcel.writeStrongBinder(tagService);
}
nParcel.setDataPosition(0);
Tag nTag = Tag.CREATOR.createFromParcel(nParcel);
nParcel.recycle();
return nTag;
}
此补丁由bildin(https://github.com/bildin)提供。
虽然我测试的设备不是早期的品牌,但是我的Moto X(第一代)的修补程序完美地使用了修改后的ROM Marshmallow,所以我想它也适用于各种各样的设备恩智浦芯片PN544
答案 1 :(得分:1)
在我的种姓中,必须先格式化 NFC 标签。
String[] techList = tag.getTechList();
不包含所需的技术:
TagTechnology.NDEF
但是
TagTechnology.NDEF_FORMATABLE
已列出。我将标签格式化为:
NdefFormatable ndefFormatable = NdefFormatable.get(tag);
ndefFormatable.connect();
ndefFormatable.format(message);
ndefFormatable.close();
答案 2 :(得分:0)
错误可能出在你做的时候:
int sect = mfc.getSectorCount();
由于mfc
在执行
MifareClassic mfc = MifareClassic.get(myTag);
答案 3 :(得分:0)
MifareClassic mfc = MifareClassic.get(myTag);
仅在卡片技术人员列表具有android.nfc.tech.MifareClassic
时创建mfc对象,否则为MifareClassic对象返回null。
您可以通过myTag.getTechList();
在技术清单中使用具有上述技术的卡,就可以了。
也请参考此答案,它解释了mifareclassic卡的内存结构: Reading Mifare Classic returns strange characters
如果有人来这里弄清楚如何阅读MifareClassic卡,那么这里是一个存储库。 https://github.com/codes29/RFIDReader/blob/master/app/src/main/java/com/codes29/rfidreader/MainActivity.java