Grinding my Gears - 成为最酷的服务器开发者





5.00/5 (17投票s)
介绍如何成为您身边最酷的开发者,编写可穿戴代码
引言
嗯,我们一直在听到很多关于“物联网”的事情,但直到最近,我们才开始看到它的商业化前景。可穿戴设备正迅速成为物联网的可见面,而三星凭借其一系列智能手表——先是Gear,然后是Gear 2——在竞争中抢占了先机。在本文中,我们将介绍在Android设备端需要编写的内容,以便开发Gear应用程序。
它折磨我,让我流血
好吧,事情开始变得有点……怎么说呢,有趣。可能最令人头疼的问题是,我们必须将Gear应用程序开发分成两部分。我的意思是,我们将使用一个版本的Eclipse来创建Android端,并使用另一个版本的Eclipse来创建Gear 2端。所以这意味着我们在创建应用程序时需要进行更多的规划。别担心,这会变得相当简单。
如果您还没有下载适用于Android开发的Eclipse版本,我建议您首先从这里下载并安装Android Developer Tools。别担心,我会等您。
开始编码
准备好了吗?很好。现在我们可以创建我们的应用程序了。与其一步一步地进行,不如创建一个新的Android应用程序——我称之为reputationwatcher——并将最低SDK要求设置为4.1(Jellybean)。将目标SDK和编译设置为4.4(KitKat)。由于Android端没有视觉效果或交互,我们可以删除主题并移除Activities。
好了,我们已经创建了项目,让我们开始添加一些代码。现在,可以肯定地说,由于我不是Android或Gear方面的专家,我很大程度上参考了在线示例,所以如果您看过那些示例,这一切应该都很熟悉。如果您没有看过,别担心,因为我们将在这里介绍代码,并深入探讨我通过试验代码所学到的东西。
我们需要在libs文件夹中添加几个jar文件。这些文件是accessory-v1.0.0.jar和sdk-v1.0.0.jar。要获取它们,我们需要从这里安装Samsung SDK。我使用了Eclipse来安装我的版本——您可能也想这样做。转到Help > Install New Software,然后按照说明安装Samsung Accessories。安装完成后,将这些文件从下载区域复制到libs文件夹。
好了,让我们创建一个新的Java类。它默认会放在src文件夹中,并为Gear->Android链的实际Android端提供基础。我们将编辑包名,在其末尾加上provider,这表明了此应用程序部分的意图——它将为手表提供更新。回想一下,我将我的类命名为ReputationProvider
——您可以随意命名,但如果您更改了它,请记住这些值,因为稍后创建配置时我们会再次用到它们。
到我们编写完Android应用程序的代码时,我们创建的结构将如下所示
为了节省时间,以下是我在此类中使用的导入(所以让我们继续将它们粘贴到我们的.java文件中)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.util.SparseArray;
import android.widget.Toast;
import com.samsung.android.sdk.SsdkUnsupportedException;
import com.samsung.android.sdk.accessory.SA;
import com.samsung.android.sdk.accessory.SAAgent;
import com.samsung.android.sdk.accessory.SAPeerAgent;
import com.samsung.android.sdk.accessory.SASocket;
我们的类扩展了SAAgent
的功能。SAAgent
允许我们的应用程序在工作线程上运行,所以它非常方便。
public class ReputationProvider extends SAAgent{
我们将继续在此类中实现几个重写的方法。
还记得我们之前谈到的提前规划应用程序吗?嗯,现在我们将受益于思考我们的应用程序。Gear与Android设备通信的方式是通过应用程序定义的通道。所以,我们将定义应用程序中使用的通道。请记住这个数字,因为这并不是我们唯一定义它的地方。
public final static int ChannelId = 2048;
是的,没错。我选择这个通道ID是为了纪念这款游戏。
现在,我们需要分神并创建一些辅助功能。我们将创建两个匿名的内部类(至少我的Java朋友们是这么称呼它们的:D),以提供对连接处理和绑定器服务的访问。
ReputationBinder
这是一个简单的类。它所做的只是返回ReputationProvider
的当前实例。这是完整的类
public class ReputationBinder extends Binder {
public ReputationProvider getBinderService() {
return ReputationProvider.this;
}
}
ReputationSocket
现在这个类更有趣了。我们将在此类中处理实际的套接字连接,所以我们希望从扩展SASocket开始。
public class ReputationSocket extends SASocket {
此类的构造函数只是将类名传递给它继承的类。
protected ReputationSocket() {
super(ReputationSocket.class.getName());
}
在实现这个类时,我们有几个重写的方法。我们不会全部介绍——例如,onError
只是记录错误,而onServiceConnectionLost
只是从SparseArray
中删除连接。所以请下载代码并仔细查看。
然而,onReceive
方法是我们套接字处理的实际“核心”。
@Override
public void onReceive(int channelId, byte[] data) {
final SASocket uiHandler =
connectionMap.get(connectionId);
String value = getDataFromUrl();
// Convert the output data
final byte[] outputData = value.getBytes();
new Thread(new Runnable(){
public void run() {
try {
uiHandler.send(ChannelId, outputData);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
在此方法开始时,我们从连接的SparseArray
中检索套接字详细信息(我知道我们还没有定义它——我们稍后会处理)。然后我们获取我们感兴趣的数据(同样,像connectionMap
一样,它实际上是在ReputationProvider
中定义的)并将其转换为字节数组。这是我们开始感觉到这个特定方法非常有趣的地方——字节数组与进入方法的数据类型相同——这给了我们线索,这个方法是我们想要处理来自手表的数据的点,所以当我们想要在收到请求时将响应发送回手表时,我们希望在此处处理。事实上,这正是我们的线程在这里所做的——它将数据排队以便发送回手表,而那些眼睛犀利(或者你选择的任何木偶角色)的人会注意到通道ID的再次出现。这几乎就像我们真的在考虑这些事情。
好了,我们的辅助类就完成了。让我们回到ReputationProvider
。
每当我们建立手表和设备之间的连接时,我们应该会收到一个服务连接响应。它被巧妙地称为onServiceConnectionResponse
方法。这个方法看起来有点像这样
@Override
protected void onServiceConnectionResponse(SASocket socket, int result) {
if (result != CONNECTION_SUCCESS) return;
if (socket == null){
Log.e("ReputationProvider", "The socket is null");
return;
}
if (connectionMap== null){
connectionMap = new SparseArray<ReputationSocket>();
}
ReputationSocket connection = (ReputationSocket)socket;
connection.connectionId = (int)(System.currentTimeMillis() & 255);
connectionMap.put(connection.connectionId, connection);
Toast.makeText(getBaseContext(), "Established connection",
Toast.LENGTH_LONG).show();
}
我们在这里做的第一件事是检查我们是否收到了有效的连接以及我们是否拥有一个套接字实例。如果这两个条件都没有成功,那么就没有继续的意义了。
我们将套接字转换为我们之前创建的套接字类,并在其上设置connectionId
,然后保存此连接信息以备将来使用。我们可以有很多方法来分配连接ID,但我选择遵循三星的示例。这完全是由于我对Java开发细微之处的不熟悉,所以我想更有经验的Java开发者会想出更好的方法。最后,我们发出一个Toast通知,告知Android端的用户我们已经建立了连接。
当手表尝试绑定时,我们将通过onBind
方法处理。它只是返回我们创建的ReputationBinder
的一个实例(线索在于它期望返回一个IBinder
,而我们为ReputationBinder
实现了IBinder
;巧妙吧)。
好了,我之前提到了getDataFromUrl
方法。嗯,恭喜你,你刚刚获得了CP声望提升。对于这个实现,我们只是简单地返回1000万,所以你已经超越了那些不看文章的懒惰者了:D。
好了,关于Android端就到这里了,感谢您的阅读,希望您喜欢这篇文章。
“等等,Pete。你不是说我们还会回来处理那个2048吗?”
哎呀,是的,这并没有结束Android端的工作——我们还有两件事要做。我们要添加的第一部分是描述我们服务配置文件的元数据。第二部分是更新应用程序清单。
好了,在res文件夹中,创建一个名为metadata的文件夹。在里面,添加一个空的xml文件。我们称之为repwatchermetadata.xml。现在,将以下内容复制进去
<!DOCTYPE resources [
<!ELEMENT resources (application)>
<!ELEMENT application (serviceProfile)+>
<!ATTLIST application name CDATA #REQUIRED>
<!ELEMENT serviceProfile (supportedTransports, serviceChannel+) >
<!ATTLIST application xmlns:android CDATA #IMPLIED>
<!ATTLIST serviceProfile xmlns:android CDATA #IMPLIED>
<!ATTLIST serviceProfile serviceImpl CDATA #REQUIRED>
<!ATTLIST serviceProfile role (PROVIDER | CONSUMER | provider | consumer) #REQUIRED>
<!ATTLIST serviceProfile name CDATA #REQUIRED>
<!ATTLIST serviceProfile id CDATA #REQUIRED>
<!ATTLIST serviceProfile version CDATA #REQUIRED>
<!ATTLIST serviceProfile serviceLimit (ANY | ONE_ACCESSORY | ONE_PEERAGENT | any | one_accessory | one_peeragent) #IMPLIED>
<!ATTLIST serviceProfile serviceTimeout CDATA #IMPLIED>
<!ELEMENT supportedTransports (transport)+>
<!ATTLIST supportedTransports xmlns:android CDATA #IMPLIED>
<!ELEMENT transport EMPTY>
<!ATTLIST transport xmlns:android CDATA #IMPLIED>
<!ATTLIST transport type (TRANSPORT_WIFI | TRANSPORT_BT | TRANSPORT_BLE | TRANSPORT_USB |
transport_wifi | transport_bt | transport_ble | transport_usb) #REQUIRED>
<!ELEMENT serviceChannel EMPTY>
<!ATTLIST serviceChannel xmlns:android CDATA #IMPLIED>
<!ATTLIST serviceChannel id CDATA #REQUIRED>
<!ATTLIST serviceChannel dataRate (LOW | HIGH | low | high) #REQUIRED>
<!ATTLIST serviceChannel priority (LOW | MEDIUM | HIGH | low | medium | high) #REQUIRED>
<!ATTLIST serviceChannel reliability (ENABLE | DISABLE | enable | disable ) #REQUIRED>
]>
<resources>
<application name="ReputationProvider">
<serviceProfile
name="sapimageswitcher"
id="/system/sapimageswitcher"
role="provider"
serviceImpl="com.codeproject.reputationwatch.provider.ReputationProvider"
version="1.0"
serviceLimit="ANY"
serviceTimeout="10">
<supportedTransports>
<transport type="TRANSPORT_WIFI" />
<transport type="TRANSPORT_BT" />
</supportedTransports>
<serviceChannel
id="2048"
dataRate="low"
priority="low"
reliability="enable" />
</serviceProfile>
</application>
</resources>
大部分都很直接,但正如你所见,我们现在开始将一些东西联系起来。首先,我们在serviceImpl
属性中告诉它我们功能实现的路径,以及我们允许的传输机制的详细信息。为了测试我们的应用程序,我们需要允许它通过WIFI进行通信,所以我们添加了该元素以及对蓝牙的正常支持。
是的。你又看到了,那个2048又出现了。这是我们暴露我们想使用的通道的地方。
好了,现在只剩下更新清单文件了,它看起来像这样
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codeproject.reputationwatch"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="com.samsung.accessory.permission.ACCESSORY_FRAMEWORK" />
<uses-permission android:name="com.samsung.wmanager.APP" />
<uses-permission android:name="com.samsung.wmanager.ENABLE_NOTIFICATION" />
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service android:name="com.codeproject.reputationwatch.provider.ReputationProvider" />
<receiver android:name="com.samsung.android.sdk.accessory.ServiceConnectionIndicationBroadcastReceiver" >
<intent-filter>
<action android:name="android.accessory.service.action.ACCESSORY_SERVICE_CONNECTION_IND" />
</intent-filter>
</receiver>
<receiver android:name="com.samsung.android.sdk.accessory.RegisterUponInstallReceiver" >
<intent-filter>
<action android:name="android.accessory.device.action.REGISTER_AFTER_INSTALL" />
</intent-filter>
</receiver>
<meta-data android:name="AccessoryServicesLocation"
android:value="/res/metadata/repwatchermetadata.xml"
/>
</application>
</manifest>
同样,大部分都很直接。请确保您添加了权限,并检查以确保已设置元数据文件的路径,以及ReputationProvider
源的路径。
好了,现在我们完成了。不要马上关闭Eclipse,因为您需要从手表源中复制一些东西过来。我在这里给您一个预览,当我们构建了应用程序的手表端后,我们将把它创建的widget文件(以.wgt结尾)复制到assets文件夹中。在下一篇文章中,我们将添加手表端的功能并讨论调试我们完成的应用程序。