关于项目中的NFC使用的流程
第一次做NFC,主要是做一个记录。
初始化的时候是这样:
nfcUtils = new NfcUtils(this,getApplicationContext());
------------------------------------------------------
/**
* 构造函数,用于初始化nfc
*/
public NfcUtils(Activity activity,Context context) {
this.mContext = context;
mNfcAdapter = NfcCheck(activity,mContext);
NfcInit(activity,mContext);
}
---------------------------------------------------
/**
* 检查NFC是否打开
*/
public static NfcAdapter NfcCheck(Activity activity,Context mContext) {
NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(activity);
if (mNfcAdapter == null) {
return null;
} else {
if (!mNfcAdapter.isEnabled()) {
Intent setNfc = new Intent(Settings.ACTION_NFC_SETTINGS);
activity.startActivity(setNfc);
}
}
return mNfcAdapter;
}
----------------------------------------------------------
/**
* 初始化nfc设置
*/
public static void NfcInit(Activity activity,Context mContext) {
Intent targetIntent = new Intent(activity, activity.getClass());
targetIntent .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
mPendingIntent = PendingIntent.getActivity(activity, 0,targetIntent, 0);
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
try {
filter.addDataType("*/*");
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
mIntentFilter = new IntentFilter[]{filter, filter2};
mTechList = null;
}
-------------------------------------------------------
//开启前台调度系统
NfcUtils.mNfcAdapter.enableForegroundDispatch(this, NfcUtils.mPendingIntent,
NfcUtils.mIntentFilter, NfcUtils.mTechList);
然后我们在页面唤醒的任何时候将电子卡片靠近扫描的时候,会执行这样的方法:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
intent1 = intent;
//当该Activity接收到NFC标签时,运行该方法
//调用工具方法,读取NFC数据
try {
String str = nfcUtils.readNFCFromTag(intent);
et_code.setText(str);
showToast(str);
Log.d("122",str);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
刚开始的时候一脸懵逼,因为之前我做扫描的时候,差不多流程都是在页面注册一个广播接收者,然后每次扫描之后发出广播,然后把数据传回来,我看了好久也没看到类似这样广播服务的东西。
我只能在初始化的时候下点功夫,很明显,我们在开始的时候把Activity传到了NFCUtil中,然后添加了NfcAdapter.ACTION_NDEF_DISCOVERED和NfcAdapter.ACTION_TAG_DISCOVERED的过滤器,还构造了PendingIntent对象
pendingIntent的getActivity(Context, int, Intent, int)表示跳转到一个activity组件,真正的跳转是由参数intent决定的。
在我们这个例子中,真正的intent是跳转本页面,而且页面是以sigleTop的方式显示。
然后我们看resume中的开启前台调度系统方法,将我们的过滤器和这个intent都放进去了,这个mNfcAdapter.enableForegroundDispatch是系统的方法。
猜想
整个流程完了,不难看出是Application内部的服务每次收到NFC扫描操作的时候,就执行这个intent。在我这个项目中,就是以sigleton方式加载本页面,然后执行onNewIntent,读取intent中带的数据:intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);解析之后将数据回填。
验证
------------------------------Adapter源码 ---------------------------
public void enableForegroundDispatch(Activity activity, PendingIntent intent,
IntentFilter[] filters, String[][] techLists) {
synchronized (NfcAdapter.class) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
}
if (activity == null || intent == null) {
throw new NullPointerException();
}
// 只能在Resume这个方法中用这个方法
if (!activity.isResumed()) {
throw new IllegalStateException("Foreground dispatch can only be enabled " +
"when your activity is resumed");
}
try {
TechListParcel parcel = null;
if (techLists != null && techLists.length > 0) {
parcel = new TechListParcel(techLists);
}
//暂停的时候自动调用disableForegroundDispatchInternal方法
ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,mForegroundDispatchListener);
//终于找到了一个service
sService.setForegroundDispatch(intent, filters, parcel);
} catch (RemoteException e) {
// 发生异常之后恢复NFC服务
attemptDeadServiceRecovery(e);
}
}
就上面所说,终于找到了一个Service,这个sService是INfcAdapter类,INfcAdapter是JNI实现的,这个setForegroundDispatch方法又把我们的intent和filter都丢进去了,JNI的实现我们看不见,但是这差不多已经很明显:我们类会被service重新跳转,然后进入onNewIntent方法,基本符合猜想。