关于NFC P2P模式

来源:互联网 发布:农村淘宝 广告 编辑:程序博客网 时间:2024/06/05 09:34

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;    }}
原创粉丝点击