NFC framework introduce(二)

来源:互联网 发布:java常见错误 编辑:程序博客网 时间:2024/06/05 02:27

  

5.2 大数据量的传送

大数据量的传送,是指图片等数据量比较大的资源,需要通过NFC启动蓝牙的匹配,通过蓝牙来传送数据。

5.2.1 读写流程图

5.2.2 发送端发送蓝牙请求和发送数据流程
5.2.2.1时序图


   大数据量的写操作跟小数据量的类似,我们这里主要关注差异的部分,我们从P2pLinkManager.doSenpProtocol()开始。前面部分的时序图,请查看5.1.2.1小数据量写操作的时序图.

5.2.2.2 代码分析

在看P2pLinkManager.doSenpProtocol()之前,我们先看看发送数据的Apk是如何设置数据的吧。

mNfcAdapter =NfcAdapter.getDefaultAdapter(mActivity.getAndroidContext());

mNfcAdapter.setBeamPushUris(newUri[]{manager.getContentUri(path)},Activity);

以上代码是在Gallery2中设置图片资源的代码,将图片的路径封装在Uri数组中,并调用NfcAdapter. setBeamPushUris()进行设置。这个跟小数据量的设置类似,是将数据保存在NfcActivityState. Uris变量中。P2pLinkManager将回调NfcActivityManager.getUris()获取到该数据。我们看看代码吧:

void prepareMessageToSend(){

   ……

           if (mCallbackNdef != null) {

               try {

                   mMessageToSend = mCallbackNdef.createMessage();

                   mUrisToSend =mCallbackNdef.getUris();

                   return;

               } catch (RemoteException e) {

               }

           }

       }

   }

P2pLinkManager. prepareMessageToSend()方法相信已经不再陌生,前面也见到过,这里就是通过Binder回调了NfcActivityManager.getUris()方法,读取数据,并暂存在mUrisToSend变量中。

好了,经过简单的介绍,那么现在我们可以从P2pLinkManager. doSenpProtocol()开始了,代码如下:

static intdoSnepProtocol(HandoverManager handoverManager,

           NdefMessage msg, Uri[] uris) throws IOException {

       SnepClient snepClient = new SnepClient();//创建新的客户端

       try {

           snepClient.connect();//socket连接

       } catch (IOException e) {

       }

       try {

           if (uris != null) {//说明有大数据量要发送

               NdefMessage response = null;

               //封装蓝牙标志的请求信息在NdefMessage

               NdefMessagerequest = handoverManager.createHandoverRequestMessage();

               if (request != null) {

                   SnepMessage snepResponse= snepClient.get(request);//发送蓝牙请求,并读取另外设备回应数据保存在snepResponse

                   response =snepResponse.getNdefMessage();//SnepMessage数据转换成NdefMessage

               } // else, handover not supported

               if (response != null) {//有相应,可以发送数据

                   handoverManager.doHandoverUri(uris,response);//通过蓝牙发送数据

               } else if(msg != null) {

                   snepClient.put(msg);

               } else {

                   return SNEP_HANDOVER_UNSUPPORTED;

               }

           } ……

   }

在大数据量传送流程图中也说到,发送端要发送图片之前,需要创建标准的蓝牙请求信息,然后将信息封装在Ndefmessage中,发送给接收端,当收到接收端回应之后才能发送真正的图片数据。

下面我们来看看标准蓝牙请求数据的创建代码如下:

HandoverManager.createHandoverRequestMessage()

public NdefMessagecreateHandoverRequestMessage() {

       if (mBluetoothAdapter == null) return null;//是否支持蓝牙设备

       return new NdefMessage(createHandoverRequestRecord(),createBluetoothOobDataRecord());//将数据封装在NdefMessage

   }

当然,需要设备有蓝牙的支持,否则面谈,接着调用两个接口创建两个NdefRecord,封装在NdefMessage中。

先看看HandoverManager.createHandoverRequestRecord()方法,蓝牙请求数据:

   NdefRecord createHandoverRequestRecord() {

       NdefMessage nestedMessage = newNdefMessage(createCollisionRecord(),

               createBluetoothAlternateCarrierRecord(false));

       byte[] nestedPayload = nestedMessage.toByteArray();

 

       ByteBuffer payload = ByteBuffer.allocate(nestedPayload.length +1);

       payload.put((byte)0x12);  // connection handoverv1.2

       payload.put(nestedMessage.toByteArray());

 

       byte[] payloadBytes = new byte[payload.position()];

       payload.position(0);

       payload.get(payloadBytes);

       return new NdefRecord(NdefRecord.TNF_WELL_KNOWN,NdefRecord.RTD_HANDOVER_REQUEST, null,

               payloadBytes);

   }

接着看HandoverManager.createBluetoothOobDataRecord(),创建当前设备蓝牙地址数据:

   NdefRecord createBluetoothOobDataRecord() {

       byte[] payload = new byte[8];

       payload[0] = 0;

       payload[1] = (byte)payload.length;

 

       synchronized (HandoverManager.this) {

           if (mLocalBluetoothAddress == null) {

               mLocalBluetoothAddress =mBluetoothAdapter.getAddress();//获取当前设备蓝牙地址

           }

 

           byte[]addressBytes =addressToReverseBytes(mLocalBluetoothAddress);

           System.arraycopy(addressBytes, 0,payload, 2, 6);//地址数据拷贝

       }

 

       return new NdefRecord(NdefRecord.TNF_MIME_MEDIA, TYPE_BT_OOB, newbyte[]{'b'},payload);//将地址封装在NdefRecord

   }

上面两个方法,创建了两个NdefRecord,一个是蓝牙请求数据,另一个是当前蓝牙地址数据。好了,将量数据封装在NdefMessage中返回。我们回头继续看doSnepProtocol()方法。createHandoverRequestMessage()之后就到SnepClient.get(request)发送请求了:

  publicSnepMessage get(NdefMessage msg) throws IOException {

       SnepMessenger messenger;

       synchronized (this) {

           messenger = mMessenger;

       }

 

       synchronized (mTransmissionLock) {

           try {

               messenger.sendMessage(SnepMessage.getGetRequest(mAcceptableLength,msg));//发送请求

               returnmessenger.getMessage();//获取回应

           } catch (SnepException e) {

               throw new IOException(e);

           }

       }

   }

在发送数据之前,先调用SnepMessage.getGetRequest(mAcceptableLength,msg)NdefMessage数据转换成SnepMessage数据,然后调用SnepMessenger.sendMessage开始发送数据,SnepMessenger中包含了Socket的端口。

发送数据之后直接调用SnepMessenger.getMessage();获取另一设备的回应信息。这里有个疑问,一直不明白,发送完数据之后立刻获取回应,这样是怎么做到同步的呢?求解释…....

到此,我们继续回到P2pLinkManager.doSnepProtocol()中,SnepClient.get()get请求有了回应,回应的信息还是封装在SnepMessage中,接着调用SnepMessage的方法getNdefMessage()将回应的数据转换成NdefMessage数据。

有了回应,说明蓝牙可以匹配,调用HandoverManager.doHandoverUri(uris,response),开始通过蓝牙发送Uri

  // Thisstarts sending an Uri over BT

   public void doHandoverUri(Uri[] uris, NdefMessage m) {

       if (mBluetoothAdapter == null) return;

 

       BluetoothHandoverDatadata = parse(m);//解析回应出蓝也数据

       if (data != null && data.valid){

           // Register a new handover transfer object

           getOrCreateHandoverTransfer(data.device.getAddress(), false,true);//创建蓝牙转换器

           BluetoothOppHandover handover = new BluetoothOppHandover(mContext,data.device,

               uris, mHandoverPowerManager, data.carrierActivating);

           handover.start();//开始发送数据

       }

   }

首先需要调用HandoverManager.parse()将回应数据解析为蓝牙数据,里面当然包含了接收设备的蓝牙地址,接着创建了一个BluetoothOppHandover()实例,这样,该实例就包含了接收设备的蓝牙地址,Uris数据,然后就调用其start()开始传送数据了。

下面就需要看看接收端是怎么回应蓝牙请求了的。

5.2.3 接收端回应蓝牙请求流程
5.2.3.1时序图

5.2.3.2 代码分析

SnepServer我们这里也不陌生了的,里面有ConnectionThread线程读取收到的数据,在run方法中调用SnepServer.handleRequest()处理请求数据:

   static boolean handleRequest(SnepMessenger messenger, Callbackcallback) throws IOException {

       SnepMessage request;

       try {

           request =messenger.getMessage();//读取收到的数据

       } catch (SnepException e) {

          ……

       }

       if (((request.getVersion() & 0xF0)>> 4) != SnepMessage.VERSION_MAJOR){

           ……

       } else if (request.getField() == SnepMessage.REQUEST_GET){//get请求

           messenger.sendMessage(callback.doGet(request.getAcceptableLength(),

                   request.getNdefMessage()));

       } else if (request.getField() == SnepMessage.REQUEST_PUT){//put请求

           if (DBG) Log.d(TAG, "putting message " +request.toString());

           messenger.sendMessage(callback.doPut(request.getNdefMessage()));

       } else {

       ……

       }

       return true;

   }

SnepMessenger.getMessage()这里也不陌生了,是用来读取收到的数据的,将数据保存在SnepMessage中。

要清楚的是,我们这里是要回应蓝牙的请求,所以这里我们满足了条件request.getField() ==SnepMessage.REQUEST_GET,即get请求,意思是,接收到得到的数据是其他设备的请求信息,当前设备作为接收端,需要解析其请求数据,满足条件后,将发送回应信息的请求端。

callback.doGet()就是去处理请求的信息,然后返回回应的信息,通过SnepMessenger. sendMessage()回应发送给请求端。

先来看看callback.doGet()。这个前面也见到过,Callback接口在P2pLinkManager被实现了:

  finalSnepServer.Callback mDefaultSnepCallback = newSnepServer.Callback() {

       @Override

       public SnepMessage doPut(NdefMessage msg) {

           onReceiveComplete(msg);

           returnSnepMessage.getMessage(SnepMessage.RESPONSE_SUCCESS);

       }

 

       @Override

       public SnepMessagedoGet(intacceptableLength, NdefMessage msg) {//处理请求信息

           NdefMessageresponse =mHandoverManager.tryHandoverRequest(msg);//尝试处理请求信息,并返回回应信息。

if (response != null){

               onReceiveHandover();

               returnSnepMessage.getSuccessResponse(response);返回响应信息给SnepServer

              }else {

               returnSnepMessage.getMessage(SnepMessage.RESPONSE_NOT_FOUND);

           }

       }

};

代码简单,我们只需要关注HandoverManager.tryHandoverRequest(),参数类型是NdefMessage

public NdefMessagetryHandoverRequest(NdefMessage m) {

       NdefRecord r = m.getRecords()[0];

       //判断数据是否是蓝牙请求数据

       if (r.getTnf() != NdefRecord.TNF_WELL_KNOWN) return null;

       if (!Arrays.equals(r.getType(), NdefRecord.RTD_HANDOVER_REQUEST)) return null;

       BluetoothHandoverData bluetoothData = null;

       for (NdefRecord oob : m.getRecords()) {

           if (oob.getTnf() == NdefRecord.TNF_MIME_MEDIA&&

                   Arrays.equals(oob.getType(), TYPE_BT_OOB)) {

               bluetoothData =parseBtOob(ByteBuffer.wrap(oob.getPayload()));//解析蓝牙数据,当然包含了发送端的蓝牙地址

               break;

           }

       }

 

       synchronized(HandoverManager.this) {

           if (!mHandoverPowerManager.isBluetoothEnabled()) {

               if (!mHandoverPowerManager.enableBluetooth()) {//启用当前设备的蓝牙

                   return null;

               }

           }

           // Create the initial transfer object

           HandoverTransfer transfer =getOrCreateHandoverTransfer(//创建蓝牙转换器

                   bluetoothData.device.getAddress(), true, true);

           transfer.updateNotification();//发送通知准备接收图片数据,状态栏看到了进度条

       }

 

       // BT OOB found, whitelist it for incoming OPP data

       whitelistOppDevice(bluetoothData.device);//将蓝牙请求端设备添加到列表中

 

       // return BT OOB record so they can perform handover

       return(createHandoverSelectMessage(bluetoothActivating));创建并返回响应的数据

   }

该方法的参数是NdefMessage,第一步需要判断数据是否是蓝牙请求数据。

第二步,符合标准之后,读取出蓝牙地址数据bluetoothData

第三步,启用当前设备的蓝牙。

第四步,将获取到的蓝牙请求端的蓝牙地址数据,创建蓝牙转换器

第五步,发送通知准备接收图片数据,这时候状态栏那里就可以看到进图条了。

第六步,将蓝牙请求端设备添加到列表中。

第七步,创建并返回响应的数据。这里跟请求端创建请求数据类似,里面也包含了当前蓝牙设备的地址和回应数据。都封装在NdefMessage中。

Ok,接收端蓝牙就开始等待接收数据了。HandoverManager.tryHandoverRequest()方法,就是完成两件事情,第一件事情就是第一到第六步,完成接收端蓝牙的匹配工作,第二件事情就是第七步,创建响应信息,并返回创建的回应信息给到doGet()方法中,在doGet()中,将NdefMessage转换成SnepMessage,然后返回到SnepServer中的handleRequest()方法中:

 messenger.sendMessage(callback.doGet(request.getAcceptableLength(),

                   request.getNdefMessage()));

接着调用SnepMessenger.sendMessage(SnepMessage),发送响应数据给请求端。

请求端接收到相应数据后,就开始通过匹配蓝牙发送图片等大数据量的数据了。简单吧。

 

6 Tag设备读写流程

6.1 Tag设备读写流程图


6.2 时序图



6.2 代码分析

4.2中,NFC的启动将调用NativeNfcManager.enableDiscovery(),然后将调用到JNI方法com_android_nfc_NfcManager_enableDiscovery()扫描tagP2p设备。在该方法中,调用phLibNfc_RemoteDev_NtfRegister()方法注册回调函数nfc_jni_Discovery_notification_callback()。当扫面到tagP2p的时候将回调该方法。下面看看JNI钟开始NFC设备扫描的方法com_android_nfc_NfcManager_enableDiscovery():

static voidcom_android_nfc_NfcManager_enableDiscovery(JNIEnv *e, jobject o){

   NFCSTATUS ret;

   struct nfc_jni_native_data *nat;

   CONCURRENCY_LOCK();

    nat= nfc_jni_get_nat(e, o);

  

  REENTRANCE_LOCK();

   ret = phLibNfc_RemoteDev_NtfRegister(&nat->registry_info,nfc_jni_Discovery_notification_callback, (void *)nat);//注册回调函数

  REENTRANCE_UNLOCK();

   ……

   nfc_jni_start_discovery_locked(nat,false);//开始扫描

clean_and_return:

   CONCURRENCY_UNLOCK();

}

   我们这里主要看到其注册回调函数,然后就开始扫描NFC设备了,当发现有NFC设备的时候,将会回调nfc_jni_Discovery_notification_callback()方法,代码如下:

static voidnfc_jni_Discovery_notification_callback(void *pContext,

  phLibNfc_RemoteDevList_t *psRemoteDevList,

   uint8_t uNofRemoteDev,NFCSTATUS status)

{

 

 

       

     TRACE("Notify Nfc Service");

     if((remDevInfo->RemDevType ==phNfc_eNfcIP1_Initiator)

         || (remDevInfo->RemDevType ==phNfc_eNfcIP1_Target))

     {

        

        hLlcpHandle = remDevHandle;

        

        

        e->CallVoidMethod(nat->manager,cached_NfcManager_notifyLlcpLinkActivation, tag);

       ……  

     }

     else

     {

        

        e->CallVoidMethod(nat->manager,cached_NfcManager_notifyNdefMessageListeners,tag);

      …… 

     }

     e->DeleteLocalRef(tag);

   }

}

前面也介绍过了,发现NFC设备分为两种,一种是Tag设备,即公交卡等卡类,另一种是手机、平板等设备,完成点对点(P2p)的数据交换。在以上方法中,分别对这两类型的NFC设备做不同的处理。

当发现P2P设备的时候,回调了java层的NativeNfcManager.notifyLlcpLinkActivation(),当发现一个新的tag的时候,回调了java层的NativeNfcManager.notifyNdefMessageListeners().这里我们主要关注发现新的tag,消息是如何传送的呢?

NativeNfcManager.notifyNdefMessageListeners()代码如下:

 

   private void notifyNdefMessageListeners(NativeNfcTag tag){

       mListener.onRemoteEndpointDiscovered(tag);

   }

mListener其实就是NfcService的实例,调用了其onRemoteEndpointDiscovered()方法,代码如下:

@Override

   public void onRemoteEndpointDiscovered(TagEndpoint tag){

       sendMessage(NfcService.MSG_NDEF_TAG,tag);

   }

这里是发送了MSG_NDEF_TAGHandler消息到NfcServiceHandler,是NfcService的子类。在其handleMessage()方法中处理:

case MSG_NDEF_TAG:

                   TagEndpoint tag = (TagEndpoint) msg.obj;

                   playSound(SOUND_START);

                   NdefMessage ndefMsg =tag.findAndReadNdef();

                   if(ndefMsg != null) {

                       tag.startPresenceChecking();

                       dispatchTagEndpoint(tag);

                   } else {

                         if(tag.reconnect()) {

                           tag.startPresenceChecking();

                           dispatchTagEndpoint(tag);

                          }else {

                           ……

                       }

                   }

                   break;

这里的tag,其实是NativeNfcTag的实例,调用了其findAndReadNdef()方法读取NDEF信息,在这个方法中,调用NativeNfcTag. readNdef(),读取消息。接着调用JNI方法,doRead()JNI中读取消息,然后返回给NdefMessage。因为这里发现的是TAG设备,所以,返回了ndefMgs=null

接下来看tag消息的传送。

NfcServiceHandler.dispatchTagEndPoint()

private voiddispatchTagEndpoint(TagEndpoint tagEndpoint) {

           Tag tag = new Tag(tagEndpoint.getUid(),tagEndpoint.getTechList(),

                   tagEndpoint.getTechExtras(), tagEndpoint.getHandle(),mNfcTagService);

           registerTagObject(tagEndpoint);

           if (!mNfcDispatcher.dispatchTag(tag)) {

               unregisterObject(tagEndpoint.getHandle());

               playSound(SOUND_ERROR);

           } else {

               playSound(SOUND_END);

           }

       }

这里面将NativeNfcTag消息用于构造一个TagtagEndpoint.getTechList()取出该Tag设备支持的technologymNfcDispatcherNfcDispatcher的实例,用于向Activity派发消息的。然后调用mNfcDispatcher.dispatchTag派发Tag消息。代码如下:

 

   public boolean dispatchTag(Tag tag) {

       NdefMessage message = null;

       Ndef ndef = Ndef.get(tag);

      ……

       PendingIntent overrideIntent;

       IntentFilter[] overrideFilters;

       String[][] overrideTechLists;

 

       DispatchInfo dispatch = newDispatchInfo(mContext, tag, message);

       synchronized (this) {

           overrideFilters= mOverrideFilters;

           overrideIntent = mOverrideIntent;

           overrideTechLists = mOverrideTechLists;

       }

       ……

       if (tryTech(dispatch, tag)) {

           return true;

       }

 

 

   }

这个方法已经不再陌生了,前面也看到过了的。因为我们这里发现的是Tag设备,所以将选择调用了NfcDispatcher.tryTech()方法,代码如下:

boolean tryTech(DispatchInfodispatch, Tag tag) {

       dispatch.setTechIntent();//设置IntentActionACTION_TECH_DISCOVERED

       String[] tagTechs = tag.getTechList();//获取Tag设备支持的technology

       Arrays.sort(tagTechs);

 

       // Standard tech dispatch path

       ArrayList<ResolveInfo> matches = newArrayList<ResolveInfo>();

       List<ComponentInfo> registered =mTechListFilters.getComponents();//获取ActionACTION_TECH_DISCOVEREDActivity列表

 

       // Check each registered activity to see if it matches

       for (ComponentInfo info : registered) {

           // Don't allow wild card matching

           if (filterMatch(tagTechs, info.techs)&&//Activity支持解析该technology

                   isComponentEnabled(mPackageManager,info.resolveInfo)) {

               // Add the activity as a match if it's not already in thelist

               if (!matches.contains(info.resolveInfo)) {

                   matches.add(info.resolveInfo);//满足条件,添加到列表中

               }

           }

       }

 

       if (matches.size() == 1) {//只有一个Activity满足条件

           // Single match, launch directly

           ResolveInfo info = matches.get(0);

           dispatch.intent.setClassName(info.activityInfo.packageName,info.activityInfo.name);

           if(dispatch.tryStartActivity()) {

               if (DBG) Log.i(TAG, "matched single TECH");

               return true;

           }

           dispatch.intent.setClassName((String)null, null);

       } else if (matches.size()> 1) {//多个Activity满足条件

           // Multiple matches, show a custom activity chooserdialog

           Intent intent = new Intent(mContext,TechListChooserActivity.class);

           intent.putExtra(Intent.EXTRA_INTENT, dispatch.intent);

           intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS,

                   matches);

           if (dispatch.tryStartActivity(intent)){

               if (DBG) Log.i(TAG, "matched multiple TECH");

               returntrue;

           }

       }

       return false;

   }

第一步,还是需要设置IntentActionACTION_TECH_DISCOVERED

第二步,获取该Tag设备支持的technology

第三步,读取系统中ActionACTION_TECH_DISCOVEREDActivity列表,并获取该Apk支持解析的technology列表,这个是在apkXml文件中定义的:

<?xmlversion="1.0" encoding="utf-8"?>

<resourcesxmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

   <tech-list>

       <tech>android.nfc.tech.NfcA</tech>

   </tech-list>

   <tech-list>

       <tech>android.nfc.tech.NfcB</tech>

   </tech-list>

</resources>

第四步,做一个匹配,将支持解析该Tag设备technologyApk添加到matches列表中

第五步,如果只有一个Activity满足条件,直接启动该Activity

第六步,如果有多个Activity满足条件,发送Intent消息供用户选择启动哪里Activity

 

到此,Tag消息就已经传送到Activity了。那么接下来就需要在Activity中,发起对Tag设备的读写了。

6.3Activity中发起对Tag设备读写

6.3.1 三个Intent启动Activity

当设备扫描发现有tagP2p设备的时候,将数据封装好后发送Intent启动Activity处理数据,有三类型Intent

n        ACTION_NDEF_DISCOVERED:处理NDEF数据,包含MIMEURI数据类型

n        ACTION_TECH_DISCOVERED:处理非NDEF数据或者NDEF数据但不能映射为MIMEURI数据类型

n        ACTION_TAG_DISCOVERED:如果没有Activity相应上面两个Intent,就由该Intent处理

官网上调度图如下:



6.3.2 Activity中对Tag设备读写

经过前面一大串的分析,对于Tag设备,首先需要获取Tag信息,也说过,Tag信息包含了支持的technology类型。获取方式如下:

Tag tagFromIntent =intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

我们以MifareUltralighttechnology类型为例子,根据Tag构造MifareUltralight

MifareUltralight.get(tagFromIntent);

看看MifareUltralight.get()接口吧:

   public staticMifareUltralight get(Tag tag) {

       if(!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) returnnull;//判断该Tag是否支持

       try {

           return new MifareUltralight(tag);

       } catch (RemoteException e) {

           returnnull;

       }

   }

get()方法中,需要判断Tag设备是否支持MifareUltralight类型的technology,如果支持,那么就真正的构造它。

得到了MifareUltralight实例后,就可以开始完成读写操作了。

public void writeTag(Tagtag, String tagText) {

       MifareUltralight ultralight = MifareUltralight.get(tag);

       try {

           ultralight.connect();

           ultralight.writePage(4,"abcd".getBytes(Charset.forName("US-ASCII")));

           ultralight.writePage(5,"efgh".getBytes(Charset.forName("US-ASCII")));

           ultralight.writePage(6,"ijkl".getBytes(Charset.forName("US-ASCII")));

           ultralight.writePage(7,"mnop".getBytes(Charset.forName("US-ASCII")));

       } catch (IOException e) {

           Log.e(TAG, "IOException while closing MifareUltralight...",e);

       } finally {

           try {

               ultralight.close();

           } catch (IOException e) {

               Log.e(TAG, "IOException while closing MifareUltralight...",e);

           }

       }

   }

 

   public String readTag(Tag tag) {

       MifareUltralight mifare = MifareUltralight.get(tag);

       try {

           mifare.connect();

           byte[] payload = mifare.readPages(4);

           return new String(payload, Charset.forName("US-ASCII"));

       } catch (IOException e) {

           Log.e(TAG, "IOException while writing MifareUltralight

           message...", e);

       } finally {

           if (mifare != null) {

              try {

                  mifare.close();

              }

              catch (IOException e) {

                  Log.e(TAG,"Error closing tag...", e);

              }

           }

       }

       return null;

   }

 

不管是read还是write,都会调用到TagService中,然后是NativeNfcTag,最终到JNI中。详细代码就不分析了,感兴趣就自己看。

 

 

 

 

 

 

 

 

 

参考:

http://www.eoeandroid.com/thread-173822-1-1.html

http://www.cnblogs.com/doandroid/archive/2011/11/29/2267404.html

http://blog.csdn.net/wchinaw/article/details/6542831

http://leave001.blog.163.com/blog/static/16269129320122283648327/

http://wiki.eoeandroid.com/index.php?title=NFC_Basics&diff=4589&oldid=4580

http://doandroid.info/android中nfc功能解析及代码演示/

P2p设备http://blog.csdn.net/karen2lotus/article/details/7922948

NFC理解很好:http://www.360doc.com/content/11/0524/13/474846_119019554.shtml