关于NFC P2P模式

P2P模式是NFC的三种工作模式之一,主要完成在两个NFC设备之间数据的传递,传输的一方同时也可以接收数据。P2P模式是在Android2.3.3+(API 10)中开始加入的,之后在Android4.0+(API 14)后又重新提供了一套新的API函数。
如上所述,在API 10以上的系统开发p2p功能时,Android系统提供两套API函数。在API 10到API 13之间提供的是enableForegroundNdefPush方法,在API 14之后提供的则是setNdefPushMessageCallback方法和setNdefPushMessage方法,本文中主要就是对这两种新的方法的介绍。

一. Android Beam功能
在Android API 14+后,将两个NFC设备之间通过p2p模式传输数据称为Beam,Android Beam功能允许设备把一个NDEF消息推送到物理上相互监听的另一个设备上。这种交互提供了比其他无线技术(如蓝牙)更容易的发送数据的方法。因为 NFC不需要手动的设备发现或配对要求。两个设备在接近到一定范围时会自动的连接。Android Beam通过一组NFC API来使用,以便应用程序能够在设备之间来传输信息。例如,通信录、浏览器以及YouTube等应用程序都使用Android Beam来跟其他设备共享通信录、网页和视频。
在两个NFC设备之间实现Beam功能必须满足以下条件:
1) 两台设备的Android系统设置里的Android beam必须打开;
2) 想要发送数据的应用程序必须是在前台;
3) 接收数据的设备必须不锁屏;
4) 当发射设备跟接收设备的距离足够近的时候,发射设备会显示”Touch to Beam(触摸发射)”的UI。然后,用户能够选择是否把消息发射给接收设备。
利用Android Beam功能,你可以传输NDEF消息给另一台设备。为了传输NDEF消息,必须要生成一个NDEF消息。而且如果发射设备上当前打开的应用程序没有实现Android Beam功能, 系统会自动发送一个默认的包含你这个app的URI的NDEFmessage给接收端。如果目标设备的API level在9到13之间,这台设备会提醒用户该app未打开。如果API level在14之上,设备就会自动跳转到Google Play中这个应用的下载链接页供用户下载。基于这个功能,用户同样可以和其他用户分享app而不需要在谷歌商店中搜索,这个功能适用于API level 14 或更高。
通过调用下列两个方法中的任意一个,就能够为你的应用程序启用Android Beam:
1) setNdefPushMessage():这个方法把接收到的NdefMessage对象作为一个消息 设置给Beam。当两个设备足够近的时候,就会自动的发送消息。
2) setNdefPushMessageCallback():接收包含createNdefMessage()方法的回调, 当设备在发射数据的范围内时,这个回调方法会被调用。回调会让你只在需要的时候创建NDEF消息。
一个Activity一次只能推送一条NDEF消息,因此如果同时使用了这两种方法,那么setNdefPushMessageCallback()方法的优先级要高于setNdefPushMessage()方法。 要使用Android Beam,通常必须满足以下条件:
1) 发射数据的Activity必须是在前台。两个设备的屏幕都必须没有被锁定;
2) 必须要把发射的数据封装到一个NdefMessage对象中;
3) 接收发射数据的NFC设备必须支持Android NDEF推送协议或是NFC论坛的SNEP协议(简单的NDEF交换协议)。在API Level9(Android2.3)到API Level 13(Android3.2)的设备上需要NDEF推送协议。在API Level 14(Android4.0)和以后的设备上NDEF推送协议和SNEP都需要。NDEF Push Protocol 是Android独有的协议,而SNEP是NFC论坛的标准,所以其他操作系统也支持。如果android设备有NFC功能,则两个协议支持。
注意:如果在前台的Activity 启用了Android Beam,那么标准的Intent调度系统就会被禁用,这时Android是不能扫描标签的。但是,如果该Activity还启用了前台调度,那么在前台调度系统中,它依然能够扫描到跟Intent过滤器匹配的NFC标签。

二、 发送数据-setNdefPushMessageCallback()方法
1. 启动Beam,发送NDEF message的步骤:
1) 在你的activity类对象中实现CreateNdefMessageCallback接口;
2) 调用setNdefPushMessageCallback(NfcAdapter.CreateNdefMessageCallback callback, Activity activity, Activity … activities)方法,当这个方法被调用,activity接收到一个回调而且如果一个要传输数据的设备被发现,方法creatNdefMessage(NfcEvent)会被自动调用。

3) 在creatNdefMessage方法中,创建一个NDEF消息并返回这个message。这个NDEF message会被传输到目标设备上。
setNdefPushMessageCallback()方法动态的生成NDEF消息。你可以在activity中的任意地方调用这个方法。当然最好是在activity中的onCreate方法中调用它。
当目标设备被发现后,creatNdefMessage方法会被调用,它会返回一个NDEF消息。而这个NDEF消息会被传输给目标设备。在这期间,Touch to Beam的UI会显示在你的activity之上,所以在这个方法中你不能得到任意用户的输入。因此只能生成你想要传输的NDEF消息,然后返回这个消息。

三、 发送数据-setNdefPushMessage()方法
1. 启动Beam,发送NDEF message的步骤:
1) 创建一个NDEF消息;
2) 调用setNdefPushMessage (NdefMessage message, Activity activity, Activity … activities)方法。当这个方法被调用时,activity会将接收到的NdefMessage参数作为一个NDEF消息发送出去。
其中,第2步中,setNdefPushMessage()中的NDEF消息的生成是静态的,即由用户选择生成然后作为参数进行传递。

四、 接收数据
两个NFC设备之间通过Beam实现数据传递时,数据接受端即接受Beam消息端。接受Beam消息的实现步骤如下:
1.在你的activity中实现onNewIntent(Intent)方法,该方法会调用setIntent(Intent),由Android生命周期描述可知,在调用onNewIntent方法后,onResume()方法会自动调用。
2.在activity的onResume()方法中,检测当前消息是否来自Beam,如果是,获取并处理该NDEF消息。
3.调用自己定义的消息解析函数,将获取的NDEF消息解析并获取Payload,再对Payload进行进一步UI操作。

代码如下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nfcdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.NFC" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data
                    android:host="*"
                    android:pathPrefix=""
                    android:scheme="http" />
            </intent-filter>
        </activity>
    </application>

</manifest>

1) 实现NFC的beam功能需要在AndroidManifest.xml中添加NFC权限。

2) 中的 应用程序支持的最低的skd版本应该是14,因为本文的两种方法都是在API 14之后新提供的。
3) 在中添加应用程序要处理的intent类型

import java.nio.charset.Charset;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * NFC test ClassName: MainActivity
 * @Description: TODO
 * @author fzq
 * @date 2017-11-25
 */
public class MainActivity extends Activity implements CreateNdefMessageCallback {

    private TextView messagetv;
    private NfcAdapter nfcAdapter;
    private String payload = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        messagetv = (TextView) findViewById(R.id.message_tv);
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (nfcAdapter == null) {
            messagetv.setText("nfc error");
            return;
        }
        messagetv.setText("touch another device to beam nfc");
        nfcAdapter.setNdefPushMessageCallback(this, this, this);
    }

    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {
        Log.e("fzq", "--hasCapture  :  "+hasCapture);
    }

    /**
     * 发送nfc message
     */
    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        byte[] uriFiled = "fengzhiqi say hello".getBytes(Charset
                .forName("US-ASCII"));
        byte[] payload = new byte[uriFiled.length + 1];
        payload[0] = 0x01;
        System.arraycopy(uriFiled, 0, payload, 1, uriFiled.length);
        NdefRecord URIRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
                NdefRecord.RTD_URI, new byte[0], payload);
        NdefMessage message = new NdefMessage(new NdefRecord[] { URIRecord });
        return message;
    }

    /**
     * 接受nfc
     */
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
            ProcessIntenet(getIntent());
        }
    }

    /**
     * 处理nfc消息
     * 
     * @Description: TODO
     * @param @param intent
     * @return void
     * @throws
     * @author fzq
     * @date 2017-11-25
     */
    private void ProcessIntenet(Intent intent) {
        NdefMessage[] messages = getNdefMessage(getIntent());
        for (int i = 0; i < messages.length; i++) {
            for (int j = 0; j < messages[i].getRecords().length; j++) {
                NdefRecord record = messages[i].getRecords()[j];
                payload = new String(record.getPayload(), 1,
                        record.getPayload().length - 1,
                        Charset.forName("UTF-8"));
                messagetv.setText(payload);

            }
        }
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        Button button = new Button(this);
        this.addContentView(button, params);
        messagetv.setText("");
        button.setText("OPEN LINK: " + payload);
        //点击跳转网页
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_VIEW);
                intent.setData(Uri.parse("http://www." + payload));
                try {
                    startActivity(intent);
                } catch (ActivityNotFoundException e) {
                }
            }
        });
    }

    /**
     * 接受数据方法
     * @Description: TODO
     * @param @param intent
     * @param @return   
     * @return NdefMessage[]  
     * @throws
     * @author fzq
     * @date 2017-11-25
     */
    private NdefMessage[] getNdefMessage(Intent intent) {
        NdefMessage[] msgs = null;
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
            Parcelable[] rawMsgs = intent
                    .getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMsgs != null) {
                msgs = new NdefMessage[rawMsgs.length];
                for (int i = 0; i < rawMsgs.length; i++) {
                    msgs[i] = (NdefMessage) rawMsgs[i];
                }
            } else {
                byte[] empty = new byte[] {};
                NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN,
                        empty, empty, empty);
                NdefMessage msg = new NdefMessage(new NdefRecord[] { record });
                msgs = new NdefMessage[] { msg };

            }
        } else {
            Log.e("fzq", "no message data");
        }
        return msgs;
    }
}

猜你喜欢

转载自blog.csdn.net/fengzhiqi1993/article/details/78630971
P2P