从手表到手机发送字符串

时间:2016-11-18 10:32:50

标签: android wear-os

我尝试使用以下代码从可穿戴设备向移动设备发送字符串。 此实现基于https://github.com/twotoasters/Wear-MessageApiDemo/

连接到设备的时间延迟存在问题我已经增加了 CONNECTION_TIME_OUT_MS从100到2000(毫秒)。

我添加的移动清单:

<service
    android:name=".ListenerService" >
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
    </intent-filter>
</service>

而不是

<service
    android:name=".ListenerService" >
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
    </intent-filter>
</service>

不推荐使用com.google.android.gms.wearable.BIND_LISTENER

代码编译但电话未收到消息。

方法

private void showToast(String message) {

        Log.d(TAG, "received message : " + message);

    }

收到邮件时,应在listenerService内触发。

问题是从未收到过消息。我是否正确实现了消息api?

API版本:23

来源:

  

移动组件

启动listenerService:

----------------------------------- MainActivity.onCreate --------- ------

  @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new ListenerService();

}

定义侦听器服务以侦听消息

----------------------------------- ListenerService ----------- -------

import android.util.Log;
import android.widget.TextView;

import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;

public class ListenerService extends WearableListenerService {

    private static final String TAG = "ListenerService";

    TextView mTextView;

    @Override
    public void onMessageReceived(MessageEvent messageEvent) {
        MainActivity.mTextView.setText("got message");
        showToast(messageEvent.getPath());
    }

    private void showToast(String message) {

        Log.d(TAG, "received message : " + message);

    }
}

在清单中定义服务

----------------------------------- AndroidManifest.xml --------- -------

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

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.GPS_PROVIDER" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".ListenerService" >
            <intent-filter>
                <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
            </intent-filter>
        </service>

    </application>

</manifest>
  

磨损组件

MainActivity:

package common;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.wearable.activity.WearableActivity;
import android.support.wearable.view.BoxInsetLayout;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

public class MainActivity extends WearableActivity {

    private static final long CONNECTION_TIME_OUT_MS = 2000;
    private static final String MESSAGE = "Hello Wear!";

    private GoogleApiClient client;
    private String nodeId;

    private static final String TAG = "MainActivity";

    private BoxInsetLayout mContainerView;


    /**
     * Initializes the GoogleApiClient and gets the Node ID of the connected device.
     */
    private void initApi() {
        client = getGoogleApiClient(this);
        retrieveDeviceNode();
    }

    /**
     * Returns a GoogleApiClient that can access the Wear API.
     * @param context
     * @return A GoogleApiClient that can make calls to the Wear API
     */
    private GoogleApiClient getGoogleApiClient(Context context) {
        return new GoogleApiClient.Builder(context)
                .addApi(Wearable.API)
                .build();
    }

    /**
     * Connects to the GoogleApiClient and retrieves the connected device's Node ID. If there are
     * multiple connected devices, the first Node ID is returned.
     */
    private void retrieveDeviceNode() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
                NodeApi.GetConnectedNodesResult result =
                        Wearable.NodeApi.getConnectedNodes(client).await();
                List<Node> nodes = result.getNodes();
                if (nodes.size() > 0) {
                    Log.d(TAG, "nodeId "+nodeId);
                    nodeId = nodes.get(0).getId();
                }
                client.disconnect();
            }
        }).start();
    }

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

        initApi();
        sendToast();

    }


    /**
     * Sends a message to the connected mobile device, telling it to show a Toast.
     */
    private void sendToast() {
        if (nodeId != null) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
                    Wearable.MessageApi.sendMessage(client, nodeId, MESSAGE, null);
                    client.disconnect();
                }
            }).start();
        }
    }

}

更新:

这是添加到移动模块以监听收到的消息的类:

package com.receivers;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class MessageListener extends BroadcastReceiver {

    private static final String TAG = "MessageListener";

    @Override
    public void onReceive(Context context, Intent intent) {
        String str = intent.getAction();
        Log.i(TAG, "onReceive triggered : "+str);
    }
}

AndroidManifest.xml中MessageListener的配置:

    <receiver android:name="com.receivers.MessageListener">
        <intent-filter>
            <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
        </intent-filter>
    </receiver>

我已尝试在第String str = intent.getAction();行设置断点,但似乎没有调用onReceive方法。

在wear模块中,方法onNodeFound()似乎确实正在发送消息,因为正在调用此行Wearable.MessageApi.sendMessage(googleApiClient, nodeId, MESSAGE_PATH, "Hello Wear!".getBytes(Charset.forName("UTF-8")));。我是否正确设置了MessageListener

更新2:

ReceiverActivity:

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

public class ReceiverActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
                new IntentFilter("custom-event-name"));

    }

    private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Get extra data included in the Intent
            String message = intent.getStringExtra("EXTRA_MESSAGE");
            Log.d("receiver", "Got message: " + message);
        }
    };

    @Override
    protected void onDestroy() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
        super.onDestroy();
    }

}

ListenerService内,方法onMessageReceived正在被解雇,此处尝试广播该消息:

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    super.onMessageReceived(messageEvent);
    Log.d("tester", "received a message from wear: " + new String(messageEvent.getData()));

    final String message = new String(messageEvent.getData());
    final Intent messageIntent = new Intent();
    messageIntent.putExtra("EXTRA_MESSAGE", message); // define your extra
    LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);

}

在AndroidManifest.xml中启动活动:

<activity android:name=".ReceiverActivity">
</activity>

但是ReceiverActivity似乎没有收到消息,ReceiverActivity是否设置正确?

更新3:

根据评论开始我添加的活动:

    Intent intent = new Intent(this, ReceiverActivity.class);
    startActivity(intent);

MainActivity.onCreate

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        Intent intent = new Intent(this, ReceiverActivity.class);
        startActivity(intent);
        ......

1 个答案:

答案 0 :(得分:2)

new ListenerService();

这不是您启动任何服务的方式。这只会创建一个Servie实例,该实例将不执行任何操作,并将在onCreate()退出后收集。

当您收到消息时,系统将启动此服务。您只需要在清单中定义它。此外,您可能需要为收到的消息定义路径,例如

<service android:name=".ListenerService" >
    <intent-filter>
        <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
        <data android:scheme="wear" android:host="*" android:pathPrefix="/message"/>
    </intent-filter>
</service>

您只需设置服务即可。此外,请记住,为了接收消息,您的Wear应用程序和手持设备应用程序应具有相同的程序包名称(applicationId)。仔细检查你是不是有不匹配的applicationId for flavor或buildTypes。因此,如果您在build.gradle中有applicationId,请确保它们与wear和handhelp应用程序项目相匹配

defaultConfig {
    applicationId "com.runner"

关于更新用户界面:

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    MainActivity.mTextView.setText("got message");
}

这不是将服务与Activity进行交互的方法。 Service运行时,Activity可能会也可能不会运行。仅从活动更新Activtity UI。如果您需要向用户显示消息,请使用BroadcastReceiverObserver

请注意,onMessageReceived()线程不会运行Main UI,因此在显示Handler之前请先使用Toast

因此,如果您想将此服务中的消息传递给Activity,其中一种方法就像

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    final byte[] data = messsageEvent.getData();
    if (data != null) {
        final String message = new String(data, Charset.forName("UTF-8"));
        final Intent messageIntent = new Intent("custom-event-name");
        intent.putExtra(EXTRA_MESSAGE, message); // define your extra
        LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); 
        // Register BroadcastReiver from LocalBroadcastManager in your Activity to receive this broadcast
    }
}

或者如果您想要启动活动,如果它没有运行,您需要采用不同的方法:

<activity
    android:name=".ReceiverActivity"
    android:launchMode="singleTop"/>

服务:

@Override
public void onMessageReceived(MessageEvent messageEvent) {
    final byte[] data = messsageEvent.getData();
    if (data != null) {
        final String message = new String(data, Charset.forName("UTF-8"));
        final Intent activityIntent = new Intent(this, ReceiverActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra("EXTRA_MESSAGE", message);
        startActivity(intent);
    }
}

// In this case, in Activity, if it's explicitly started, you don't need a BroadcastReceiver
// Instead, you can get the extra from Activity Intent

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    handleIntent(getIntent());
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    handleIntent(intent);
}

private void handleIntent(Intent intent) {
    String message = intent.getStringExtra("EXTRA_MESSAGE");
}

磨损组件

initApi();
sendToast();

您使用可能同时运行的不同线程,因此当sendToast()运行时,您实际上可能还没有解析nodeId。

我建议将GoogleApiClient中的onCreate()与监听器连接起来。客户端连接后,开始获取节点。您不需要生成自己的线程,如果您使用setResultCallback()而不是await()

,则API是异步的

编辑14/02/2018 正如Rajesh在评论中提到的,Wearable.API已被弃用。下面的答案是指旧的API,这在撰写本文时是新的。我按原样留下旧答案,但我没有时间研究如何使用新API进行此操作。

private static final String MESSAGE_PATH = "/message";

private GoogleApiClient googleApiClient;

@Override
protected void onCerate(Bundle state) {
    super.onCreate(state);
    googleApiClient = getGoogleApiClient(this);
    googleApiClient.connect();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    googleApiClient.disconnect();
}

private GoogleApiClient getGoogleApiClient(Context context) {
    return new GoogleApiClient.Builder(context)
            .addApi(Wearable.API)
            .addConnectionCallbacks(mConnectionCallbacks)
            .build();
}

private void findNodes() {
     Wearable.NodeApi.getConnectedNodes(googleApiClient).setResultCallback(
            new ResultCallback<NodeApi.GetConnectedNodesResult>() {
                @Override
                public void onResult(
                        @NonNull final NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
                    List<Node> nodes = result.getNodes();
                    if (nodes != null && !nodes.isEmpty()) {
                        nodeId = nodes.get(0).getId();
                        Log.d(TAG, "nodeId "+ nodeId);
                        onNodeFound();
                    }
                }
            });
}

private void onNodeFound() {
    if (nodeId != null) {
        // Now you have your node, send a message, make sure the path starts like the path in manifest
        // What you thought is a message is actually a path, and the actual message is the byte array.
        // You may concat your message in path though, but keep in mind you will have to parse the string then
        Wearable.MessageApi.sendMessage(client, nodeId, MESSAGE_PATH, "Hello Wear!".getBytes(Charset.forName("UTF-8")));
    }
}

private final GoogleApiClient.ConnectionCallbacks mConnectionCallbacks
        = new GoogleApiClient.ConnectionCallbacks() {

    @Override
    public void onConnected(@Nullable final Bundle bundle) {
        findNodes();
    }

    @Override
    public void onConnectionSuspended(final int i) {
    }
};