需要的拿去—Android手表和手机通讯研究

来源:互联网 发布:重低音增强软件 编辑:程序博客网 时间:2024/05/17 08:35

手机和手表的通讯是通过数据层来完成的,数据层这个概念这里解释一下:手机和手表通过蓝牙连接起来,相同包的应用(比如手机有一个包名为com.soufun.app的应用,手表也有一个相同包名的应用)由系统自动分配一个数据层的概念,所有涉及手机手表之间的数据通讯的工作都通过数据层来实现。只要有相同包名的应用同时安装在了手机和手表上,则系统就自动生成一个数据层来管理手机和手表的数据变化。

手机和手表之间的数据交流通过数据层来完成,两者都可以向数据层发送消息,从而引起数据层数据事件发生,也可以分别设置各自的监听器来监听数据层的这些事件,从而实现接收数据的功能。

手机和手表的监听器可以看做是客户端,两者共同监听‘数据层’的数据变化,无论是从手机还是手表发送消息,都会引起数据层的状态变化。这个仅限于发送数据项请求,发送消息请求 sendMessage 是单方向的请求,发送请求之前需要事先知道接收方的节点信息,这个稍后会讲到。

同步数据项的时候(即发送dataItemRequest),凡是有监听器的地方都可以接收到这个请求。这一点类似Android中的intent机制,只不过需要程序员自己去管理和筛选这些请求。

数据的发送

数据的发送分两部分,一个是数据项DataItem的同步,这个是多向的,这个发送方明确,接收方不明确;还有一种是定向发送,即指定一台设备发送数据,这个发送方和接收方都是明确的。前者发送出消息之后在手机和手表的数据层事件监听器中都会触发数据事件,后者,只会触发接收端与消息监听相关的回调方法。

同步数据项(DataItem)介绍

同步数据之前需要先与数据层建立一个连接,建立连接的一般代码如下:

GoogleApiClient mGoogleAppiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(new ConnectionCallbacks() {@Overridepublic void onConnected(Bundle connectionHint) {Log.d(TAG, "onConnected: " + connectionHint);//连接成功}@Overridepublic void onConnectionSuspended(int cause) {Log.d(TAG, "onConnectionSuspended: " + cause);}}).addOnConnectionFailedListener(new OnConnectionFailedListener() {@Overridepublic void onConnectionFailed(ConnectionResult result) {Log.d(TAG, "onConnectionFailed: " + result);}}).addApi(Wearable.API).build();mGoogleAppiClient.connect();

在调用了connect()方法之后,监听onConnected事件。 至于发送数据的方法可以在onConnected方法中写。也可以在确保连上之后后续的代码中写。在手表上这部分的代码需要做一个处理,就是在onConnected方法中写发送数据的逻辑,在发送数据的结果有一个回调接口,在这个接口中再把这个已经和数据层建立的连接断开,官方demo这么写的,估计是为了省电。当然也可以保持一个长连接,所有向数据层发送数据的方法都可以通过这个连接发送。

连接已经建好之后,再要了解一下数据通信的载体Dataitem:

一个DataItem定义了手机和手表之间的数据交互接口,一个DataItem由两部分组成:1、payload 存放数据;2、Path 用于数据层监听器的识别 路径唯一,DataItem可以看成是对手机手表之间交互数据的一个载体。

但是通常不直接使用Dataitem,而是使用一些系统API

使用方法:

  1. 创建一个PutDataMapRequest对象,设置DataItem路径path。
  2. 调用PutDataMapRequest.getDataMap() 方法来获取一个DataMap
  3. 调用DataMap的各种put...()方法来设置值。
  4. 调用PutDataMapRequest.asPutDataRequest()方法来获得一个PutDataRequest对象。
  5. 调用DataApi.putDataItem()方法来请求系统创建DataItem。 如果手机和可穿戴设备断开了连接,那么数据会被缓存并在下次重建连接的时候同步数据。

一般的使用示例:

if (gaApiClient.isConnected()) {        PutDataMapRequest mapRequest = PutDataMapRequest.create("/samedata");        mapRequest.getDataMap().putString("key", "soufun" + (flag++));        PutDataRequest dataRequest = mapRequest.asPutDataRequest();        Wearable.DataApi.putDataItem(gaApiClient, dataRequest).setResultCallback(new ResultCallback<DataApi.DataItemResult>() {            @Override            public void onResult(DataItemResult arg0) {                if (!arg0.getStatus().isSuccess()) {                } else {                    Log.i("info", "发送成功了");                    Toast.makeText(MainActivity.this, "Notification  Success", Toast.LENGTH_LONG).show();                }            }        });    } else {        Log.i("info", "没连上");    }

可以发送简单的数据,使用Datamap以键值对的形式发送数据,但是大小有限制在100kb左右。如果要发送大的数据,可以为DataItem附加一个Asset,这个Asset可以足够的大。一个通常的使用场景是,手机端的APP通过网络下载了一张大图,然后压缩到一个适合手表查看的大小,然后放置到一个Asset中发送给手表。

这里有两种使用方式:

1、 使用PutDataRequest

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),    R.drawable.image);    Asset asset = createAssetFromBitmap(bitmap);    PutDataRequest request = PutDataRequest.create("/image");    request.putAsset("profileImage", asset);    Wearable.DataApi.putDataItem(mGoogleApiClient, request);

2、 使用PutDataMapRequest

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);     Asset asset = createAssetFromBitmap(bitmap);    PutDataMapRequest dataMap = PutDataMapRequest.create("/image");    dataMap.getDataMap().putAsset("profileImage", asset)    PutDataRequest request = dataMap.asPutDataRequest();    PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi    .putDataItem(mGoogleApiClient, request);

使用Asset发送数据的本质是,使用Asset在手机和手表之间建立一个流的通道。关于流的一切操作也可以通过Asset来完成。

Google服务包提供的数据访问API共有三个 DataAPI、NodeApi和MessageApi 其中DataApi主要负责管理同步Dataitem相关的功能。NodeApi和MessageApi要结合起来使用,NodeApi得到所有连接的节点,MessageApi需要这个节点才能完成它自己的与消息有关的一些业务逻辑。 DataApI还有一个比较常用的方法,删除Dataitem

下边是一个 简单的例子:

final Uri dataItemUri = new Uri.Builder().scheme(WEAR_URI_SCHEME).path(Constants.BOTH_PATH).build();  if (Log.isLoggable(TAG, Log.DEBUG)) {   Log.d(TAG, "Deleting Uri: " + dataItemUri.toString());  }  Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri).setResultCallback(this);

这个适用于发送一些简单的请求,比如删除通知,挂断电话,取消提醒等。

发送消息相关

发送消息有两个重要的方法:

获取已经连接的设备的节点:

public Collection<String> getNodes() {  HashSet<String> results = new HashSet<String>();  NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();  for (Node node : nodes.getNodes()) {   results.add(node.getId());  }  return results; }

和发送消息:

  SendMessageResult result = Wearable.MessageApi.sendMessage(  mGoogleApiClient, node, pathString, null).await();if (!result.getStatus().isSuccess()) { handler.sendEmptyMessage(SEND_MESSAGE_FAIL);} else { Log.e("info", "发送成功");}

注意到这两个重要步骤里边都有一个await方法,这个方法是同步的,过程中会一直阻塞,所以需要各自放在线程中去执行。

以下是发送消息的一个典型例子:

public void openOnPhone(final String openactivity) {  if (mGoogleApiClient.isConnected()) {   new Thread() {public void run() { Collection<String> nodes = getNodes(); if (nodes.size() > 0)  for (String string : nodes) {   Log.e("info", "" + string);   Message msg = new Message();   Bundle data = new Bundle();   data.putString("node", string);   data.putString("path", "" + openactivity);   msg.setData(data);   handler.sendMessage(msg);  }};   }.start();  } else {   Toast.makeText(mContext, "无连接", Toast.LENGTH_LONG).show();  } } Handler handler = new Handler() {  public void handleMessage(android.os.Message msg) {   if (msg.what == SEND_MESSAGE_FAIL) {Toast.makeText(mContext, "打开失败", Toast.LENGTH_LONG).show();   } elsesendMessage2Phone(msg);  } }; protected void sendMessage2Phone(Message msg) {  final String pathString = msg.getData().getString("path");  final String node = msg.getData().getString("node");  new Thread() {   public void run() {String threadNameString = Thread.currentThread().getName();Log.e("info", "当前的线程:" + threadNameString);SendMessageResult result = Wearable.MessageApi.sendMessage(  mGoogleApiClient, node, pathString, null).await();if (!result.getStatus().isSuccess()) { handler.sendEmptyMessage(SEND_MESSAGE_FAIL);} else { Log.e("info", "发送成功");}   };  }.start(); }

发送消息和发出Dataitem的相关请求,都需要在请求中附加一个来标识这个请求唯一性的path。path以反斜杠开头,例如"/openOneActivity"。

数据的接收

数据的接收即监听数据层数据的变化,当监听器中的事件触发的时候再进行后续的业务处理。

监听数据层的数据变化有两种实现,一种在service中使用,实现类WearableListenerService 并监听onDataChanged等事件。一种是在Activity中设置DataApi.DataListener, NodeApi.NodeListener, MessageListener 等。两种监听器的实现的区别也很明显,前一种无界,长期运行在后台,后一种Activity消失的话监听器也失效了。

使用这个service的时候要注意,这个service的生命周期是由系统来管理,所以注册的时候要加上一个特定的action,这个action是为了在手机和手表连接起来的时候由Google服务自动启动的。

以下是一个实例:

 <service android:name=".DataService" ><intent-filter><action android:name="com.google.android.gms.wearable.BIND_LISTENER" /></intent-filter><intent-filter><action android:name="com.soufun.app.android.wearable.synchronizednotifications.DISMISS" /></intent-filter></service>

例子中还有一个action是自定义的功能,这个service也是一个普通的service,只不过它的生命周期是由系统来控制的,但仍然可以当做一个普通的service来使用。注意不需要程序员手动去启动这个service。

在这个service的类中重写onDataChanged方法,来处理数据层事件,也是间接地实现了接收前边讲到的各种方法发送过来的数据的功能。有onMessageReceived,来接收另一端发送过来的各种消息。

消息的处理比较简单

下边这个例子要做的是接收其他设备发送过来的消息打开相应的activity:

@Overridepublic void onMessageReceived(final MessageEvent messageEvent) {    if (messageEvent.getPath().equals(Constants.OPEN_PATH)) {//通过path识别消息类型        Intent startIntent = new Intent(this, OpenActivity.class);        startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        startActivity(startIntent);    }}

下边是一个典型的onDataChanged方法的实现

@Override public void onDataChanged(DataEventBuffer dataEvents) {  super.onDataChanged(dataEvents);  Log.i("info", "数据发生变化喽");  for (DataEvent event : dataEvents) {   if (event.getType() == DataEvent.TYPE_CHANGED) {String typeString = event.getDataItem().getUri().getPath();DataItem item = event.getDataItem();DataMap map = DataMapItem.fromDataItem(item).getDataMap();if ("/image".equals(typeString)) { Log.i("info", "是图片"); Asset mAsset = map.getAsset("asset"); Bitmap bitmap = loadBitmapFromAsset(mAsset); sendPicNoti(bitmap);} else { sendNotification(); String getString = map.getString("key"); Log.i("info", "数据变化类型事件:" + getString);}   } else if (event.getType() == DataEvent.TYPE_DELETED) {Log.i("info", "数据删除");   }  } }/**     * 从asset中抽取出一个bitmap     *      * @param asset     * @return     */    public Bitmap loadBitmapFromAsset(Asset asset) {        if (asset == null) {            throw new IllegalArgumentException("Asset must be non-null");        }        ConnectionResult result = mGoogleApiClient.blockingConnect(20000,                TimeUnit.MILLISECONDS);        if (!result.isSuccess()) {            return null;        }        // convert asset into a file descriptor and block until it's ready        InputStream assetInputStream = Wearable.DataApi                .getFdForAsset(mGoogleApiClient, asset).await()                .getInputStream();        mGoogleApiClient.disconnect();        if (assetInputStream == null) {            Log.w("info", "Requested an unknown Asset.");            return null;        }        // decode the stream into a bitmap        return BitmapFactory.decodeStream(assetInputStream);    }

注意代码中的方法 loadBitmapFromAsset 是从接收到的Asset中提取出来包含的图片,

可以看到onDatachangged方法是可能同时接收好多个事件的,比如手机向数据层发送了很多事件,而手表上的APP中是注册了数据层事件监听器的,但是两者之间并没有连接所以这些‘积蓄’的事件在手表连上的时候便会陆续过来,这点之前也提到了。当然也可能手机连续的向手表发送了很多的事件遍历这些事件。

在onDataChanged方法中遍历这些事件,系统将事件分为了两类—TYPECHANGED和TYPEDELETED两种,这两种类型的事件分别通过系统不同的API来发送请求。 TYPE_CHANGED类,顾名思义,就是数据变化的事件类型,即DataItem必须发生了变化,这里的DataItem的变化是指在一个与数据层的连接从连接成功到连接断开这之间,如果有重复发送的完全相同的DataItem(指的DataItem的两个元素—储存数据的payload 和标识该DataItem唯一性的path,同时相同),则第二次发送出的DataItem并不会触发数据层监听器的onDataChanged方法。那么如果就想两次都发送相同的数据怎么做呢?要想连续发送相同的内容,则前后两次发送的DataItem不能相同,Dataitem的唯一标识path是不能改变的,那么只能改变内部的数据,在前边介绍的两种发送数据的方式中都是以键值对的形式加入到Dataitem中的,这里可以向里边添加一个无意义但是能保证不重复的键值对,只在发送请求的时候加入,在接收数据的时候却不对这个键值对做处理,这样既保证每次发送的Dataitem都不一样,也可以拿到自己所期望的相同的数据。

而发送消息的方法,sendMessage则不会有这种现象发生。

另一种数据层监听器的实现是使用在Activity中实现。

下边是一个典型的使用Activity来监听数据层变化事件的例子。

首先连接数据层

public class MainActivity extends Activity implements  ConnectionCallbacks, OnConnectionFailedListener,    DataApi.DataListener, NodeApi.NodeListener, MessageListener {@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    gaApiClient = new GoogleApiClient.Builder(this).addApi(Wearable.API).addConnectionCallbacks(this)            .addOnConnectionFailedListener(this).build();}@Overrideprotected void onResume() {    super.onResume();    gaApiClient.connect();}@Overridepublic void onConnected(Bundle arg0) {    Log.i("info", "手机连上了");    // 注册各种监听器,在activity上用    Wearable.DataApi.addListener(gaApiClient, this);    Wearable.MessageApi.addListener(gaApiClient, this);    Wearable.NodeApi.addListener(gaApiClient, this);    flag = 0;}@Overrideprotected void onStop() {    super.onStop();    if (gaApiClient.isConnected()) {        //移除各种监听器        Wearable.DataApi.removeListener(gaApiClient, this);        Wearable.MessageApi.removeListener(gaApiClient, this);        Wearable.NodeApi.removeListener(gaApiClient, this);        gaApiClient.disconnect();    }}@Overridepublic void onConnectionSuspended(int arg0) {}@Overridepublic void onConnectionFailed(ConnectionResult arg0) {}@Overridepublic void onPeerConnected(Node arg0) {    Log.i("info", "手机和手表连上了");}@Overridepublic void onPeerDisconnected(Node arg0) {    Log.i("info", "手机和手表断开了");}@Overridepublic void onDataChanged(DataEventBuffer dataEvents) {    Log.i("info", "手机上有数据变化");    for (DataEvent event : dataEvents) {        if (event.getType() == DataEvent.TYPE_CHANGED) {            String typeString = event.getDataItem().getUri().getPath();            DataItem item = event.getDataItem();            DataMap map = DataMapItem.fromDataItem(item).getDataMap();            if (pString.equals(typeString)) {                Intent intent = new Intent(this, PicActivity.class);                startActivity(intent);            }        } else if (event.getType() == DataEvent.TYPE_DELETED) {            Log.i("info", "数据删除");        }    }}@Overridepublic void onMessageReceived(MessageEvent arg0) {    Log.i("info", "手机上有消息过来");}}

而监听到数据变化或者消息的处理,都是取出这个请求中的唯一标识path,判断这个请求要执行什么样的业务,然后做相应的处理。

0 0
原创粉丝点击