[NFC]NFC App开发

本文详细介绍了NFC App的开发,包括TAG和P2P模式。通过分析NfcReader开源项目,揭示了AndroidManifest.xml中Activity入口及TagView的onResume()和onPause()函数在NFC Tag流程中的作用。此外,还探讨了P2P模式下enableForegroundNdefPush()、setNdefPushMessage()和setNdefPushMessageCallback()等API的使用及其影响,强调了setNdefPushMessageCallback()的优先级。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        前面描述了NFC的Tag流程和P2P流程,从实用的角度来看,我们会在介绍一下Tag和P2P APP开发的流程的一些介绍。


TAG APP开发介绍

        为了清楚的描述问题,使用GITHUB上的开源项目NfcReader作为讲解对象,如果有兴趣的,可以下载到local研究一下。


分析方法:

1. 查看AndroidManifest.xml文件,查找到Activity的入口

2. 从入口文件开始分析 


        依据AndroidManifest.xml内容,我们会进入到TagView中,默认android SDK提供的android.nfc和android.nfc.tech两个包,APP的操作都是基于SDK暴露的接口来实现的。


        在TagView.java的onCreate()函数中:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.tag_viewer);
	mTagContent = (LinearLayout) findViewById(R.id.list);
	//@Paul: APP创建的时候,分析当前APP接收到的intent信息
	//        getIntent():用于获取当前的Intent信息
	resolveIntent(getIntent());

	//@Paul:产生提示消息的对话框,后续有提示消息时,直接更新内容
	mDialog = new AlertDialog.Builder(this).setNeutralButton("Ok", null).create();

	//@Paul:产生NfcAdaptor,在SDK中所有对NFC的操作,都是透过NFCAdaptor来实现的
	//       NFCAdapter调用到NfcManager中的mAdaptor
	mAdapter = NfcAdapter.getDefaultAdapter(this);
	if (mAdapter == null) {
		//@Paul: 创建出错的处理
		showMessage(R.string.error, R.string.no_nfc);
		finish();
		return;
	}

	//@Paul:创建PendingIntent,后续会在onResume中使用
	mPendingIntent = PendingIntent.getActivity(this, 0,
			new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
	//@Paul:创建NdefPushMessage,用于发送消息
	mNdefPushMessage = new NdefMessage(new NdefRecord[] { newTextRecord(
			"Message from NFC Reader :-)", Locale.ENGLISH, true) });
}

        在onCreate()函数主要是生成一些后续会用到的变量,最关键部分就是生成了mAdaptor和mPendingIntent,这两个变量在后面的地位比较重要。所有对NFC的操作,都是基于mAdaptor来实现,而mPendingIntent在Tag分发时起到了重要的作用。


        接下来将分析onResume()和onPause()函数:

    @Override
    protected void onResume() {
	//@Paul: 回到前台时,判断NFC是否enable
	//        如果没有enable,则显示setting界面
        super.onResume();
        if (mAdapter != null) {
            if (!mAdapter.isEnabled()) {
                showWirelessSettingsDialog();
            }
	    //@Paul: 调用类:NFCAdaptor->NFCService->NfcDispatcher
	    //        最终调用NfcDispatcher.setForegroundDispatch()
            //        更新: mOverrideIntent 
	    //        	    mOverrideFilters 
	    //        	    mOverrideTechLists 
            mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);<span style="font-family: Arial, Helvetica, sans-serif;">	    </span>
            //@Paul: 调用类:NFCAdaptor->NFCActivityManager
	    //        NFCActivityManager.setNdefPushMessage()
            mAdapter.enableForegroundNdefPush(this, mNdefPushMessage);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mAdapter != null) {
	    //@Paul: 将onResume中设置的值都设为null
            mAdapter.disableForegroundDispatch(this);
	    //@Paul: mNfcActivityManager.setNdefPushMessage(null)
   	    //		  mNfcActivityManager.setNdefPushMessageCallback( null)
	    //		  mNfcActivityManager.setOnNdefPushCompleteCallback(null)
            mAdapter.disableForegroundNdefPush(this);
        }
    }

	@Override
	//@Paul: 和程序的启动方式有关,多个调用希望只有一个Activity的实例存在时
    public void onNewIntent(Intent intent) {
	//@Paul:务必更新当前的intent值
        setIntent(intent);
	//@Paul: 此例单独实现,将收到的数据解析成NDEF Message
        resolveIntent(intent);
    }

       关于NFC Tag流程上面几乎都有介绍到,总结一下:

        1. 创建NFCAdaptor和PendingIntnet对象

        2. 在onPause()和onResume()中调用enable和disable,将上层APP的参数设置下去。 一旦底层Driver侦测到有Tag设备进入范围,就会调用到NfcDispatcher.java中的dispatchTag(),该函数就会调用到:

if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters,
		overrideTechLists)) {
	return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS;
}

        

        最终会调用到overrideIntent.send(mContext, Activity.RESULT_OK, intent), 此函数会将底层侦听的Tag信息发送到APP注册了enableForegroundDispatch()的app,并且上述app会进行Tag信息的解析和处理(当前例子就是进入onNewIntent()函数)。


        基本上Tag的流程就分析完成了。


P2P APP开发介绍

        其实在Tag的APP介绍中,隐含了部分P2P的处理,比如代码中enableForegroundNdefPush()的调用和处理。实际上调用enableForegroundNdefPush()就会进入到下面部分setNdefPushMessage()。


        此部分将重点介绍一下,一旦调用了setNdefPushMessage(),  setNdefPushMessageCallback(),  setOnNdefPushCompleteCallback()后,会影响到底层哪些模块。


        先从setNdefPushMessage()开始,在此函数中会依次将上层APP要传递的消息以及回调函数信息更新到framework层,最终这些信息会在prepareMessageToSend()中得到处理,并组成待发送数据。

public void setNdefPushMessage(Activity activity, NdefMessage message, int flags) {
	boolean isResumed;
	synchronized (NfcActivityManager.this) {
		//@Paul: 将当前APP设置的信息存储在NfcActivityState
		//       后续会NFCActivityManager/createBeamShareData()中使用到此消息
		NfcActivityState state = getActivityState(activity);
		state.ndefMessage = message;
		state.flags = flags;
		isResumed = state.resumed;
	}
	if (isResumed) {
		//@Paul: 一般来说,在此Activity中还会定义createBeamShareData(),前提是要支持Android Beam
		requestNfcServiceCallback();
	}
}

void requestNfcServiceCallback() {
	try {
		//@Paul: 调用NFCService中的setAppCallback中
		NfcAdapter.sService.setAppCallback(this);
	} catch (RemoteException e) {
		mAdapter.attemptDeadServiceRecovery(e);
	}
}

@Override
public void setAppCallback(IAppCallback callback) {
	NfcPermissions.enforceUserPermissions(mContext);

	UserInfo userInfo = mUserManager.getUserInfo(UserHandle.getCallingUserId());
	if(!userInfo.isManagedProfile()
			&& !mUserManager.hasUserRestriction(
					UserManager.DISALLOW_OUTGOING_BEAM, userInfo.getUserHandle())) {
		//@Paul: 此函数是关键,将更新mCallbackNdef的值,如果没有更新时,此值为null
		//       一旦此值不为null,就会调用mCallbackNdef.createBeamShareData()和mCallbackNdef.onNdefPushComplete()
		mP2pLinkManager.setNdefCallback(callback, Binder.getCallingUid());
	} else if (DBG) {
		Log.d(TAG, "Disabling default Beam behavior");
	}
}


        需要注意的是,如果需要阻止系统发送默认的NDEF消息,那可以在AndroidManifest.xml中添加:

<meta-data android:name="android.nfc.disable_beam_defaulte" android:value="true"/>


        下面开始介绍setNdefPushMessageCallback()函数,此函数的作用就是设置产生Beam数据的回调,代码分析如下:

public void setNdefPushMessageCallback(Activity activity,
		NfcAdapter.CreateNdefMessageCallback callback, int flags) {
	boolean isResumed;
	synchronized (NfcActivityManager.this) {
		NfcActivityState state = getActivityState(activity);
		//@Paul: 更新NfcActivityState中的ndefMessageCallback
		state.ndefMessageCallback = callback;
		state.flags = flags;
		isResumed = state.resumed;
	}
	if (isResumed) {
		requestNfcServiceCallback();
	}
}

        其中ndefMessageCallback变量会最终在下面的函数中被调用到:

void prepareMessageToSend(boolean generatePlayLink) {
	synchronized (P2pLinkManager.this) {
		mMessageToSend = null;
		mUrisToSend = null;
		if (!mIsSendEnabled) {
			return;
		}

		List<Integer> foregroundUids = mForegroundUtils.getForegroundUids();
		if (foregroundUids.isEmpty()) {
			Log.e(TAG, "Could not determine foreground UID.");
			return;
		}

		//@Paul: 如果定义了callback
		if (mCallbackNdef != null) {
			if (foregroundUids.contains(mNdefCallbackUid)) {
				try {
					//@Paul: 调用callback对应的createBeamShareData()函数
					BeamShareData shareData = mCallbackNdef.createBeamShareData();
					mMessageToSend = shareData.ndefMessage;
					mUrisToSend = shareData.uris;
					mSendFlags = shareData.flags;
					return;
				} catch (Exception e) {
					Log.e(TAG, "Failed NDEF callback: " + e.getMessage());
				}
			} else {
				...
			}
		}

		...
	}
}

        上面createBeamShareData()需要在APP中进行定义。 


        补充一下,setNdefPushMessage()和setNdefPushMessageCallback()在系统同时被调用时,setNdefPushMessageCallback()的优先级较高,系统会优先选择callback产生NDEF Message; 这两个API的差异是:setNdefPushMessage()发送的数据为固定数据,而setNdefPushMessageCallback()则是APP可以更新的。


        上述基本有描述完APP在编写代码时需要注意的地方,后续有其他的问题,也欢迎大家一起讨论一下。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值