Android Things正式接替Brillo亮相,名称的改变带来了什么新的内容,广大Android开发者如何进入这一新的领域,通过本文,你不仅会了解Android Things的来龙去脉,也会直接通过代码来体验开发带给你的魅力。
Android Everywhere
一张Google IO 2015上的旧图,清晰的展示了Android的历史和未来。
Android
处于中心的Android Mobile,已经占据了全球手机市场绝大多数份额,几十亿部Android手机,构成了Android生态系统最坚实的基础。
Android TV
随着着电视大屏发展的脚步,Android TV也成了所有电视盒子和智能电视的不二之选,主要归功于Android硬件系统的开放、庞大的开发者群体、完整的工具链。如果说手机端还有iOS将近20%的份额,在TV端,目测tvOS不到2%,Android TV也就代表了整个智能电视,国内更是100%的Android TV,Apple TV没有国行版本。
Android Wear
让你的应用跑在手表上,同样使用熟悉的开发工具,主要是面向海外的应用,由于国内Android Mobile严重分裂,而Android Wear需要依赖手机的支持。
Android Auto
针对汽车的使用场景进行优化,有了Android Auto就不用上车后就是找电源线,然后开导航,再把手机放到各种架子上,而是直接把Android Mobile放到原生底座上,直接使用语音和易操作的中控大屏。
Android Chromebook
从 Chrome 操作系统版本 M53 开始,可以直接使用Chromebook中的Google Play Store下载和使用Android应用,现在使用的Google Chrome版本是55,那么绝大多数已有Chromebook都已经支持Android应用,新的Chromebook自然都会支持。
针对 Chromebook 优化应用
支持Android应用的Chromebook列表
Android和Chrome两大系统的合并,除了带来了Android丰富的应用以外,对于用户最大的好处就是自动更新了,Chrome自动下载更新,下次重新打开/启动时自动应用更新,从Android Nougat开始,Android系统也将使用这种更新机制。
根据IDC报告,Chromebook在2016年第一季度的出货量已经在美国市场超越了Mac,特别是由于教育市场的大量需求。
这里,还要注意,Chromebook使用的Chrome OS和Google Chrome高度共享代码,既然Android应用可以在Chromebook上运行,当未来在Mac/Windows/Linux上可以时,你也不要感到惊讶。
Android Things
终于到了今天的主角登场,Android Things!先看外表。
再看内部核心硬件。
它的愿景就是将无数的的设备连接起来,Android Things作为物联网的大脑,使用公开协议Weave与广大的传感器/外部设备进行对话。
不像Android其它系统,Android Things大多数情况下只在后台以服务方式运行,没有显示屏,默默的与打印机、门锁、烤箱、灯泡、插座这些设备一起提供服务。
Android Things全解析
## Android Things架构
先看Brillo和Android Things的架构图进行对比。
这是Brillo,
这是Android Things,
可以很清楚的看出来:
- Brillo使用C/C++基于NDK进行开发,Android Things通过Java API面向广大的Android和Java开发者,就算是新手,Android的也是极易上手的。各位苦于嵌入式开发各种工具坑的福音到了,对于性能和底层要求高的部分仍然可以用NDK编写,在Android Studio里调试NDK代码也和Java代码一样的简单。
- Android Studio,Android SDK,Play service和Firebase,这些工具和Service形成了完整易用的工具链。
- Android Things出生最晚,更新条件也是最好的,直接使用Android Nougat的自动后台更新机制,最大限度的提高系统的安全性。
##广泛的硬件平台支持
现在支持以下3款硬件
Intel Edison
开发版中的贵族。
NXP Pico
中规中矩的中间阶层。
Raspberry Pi 3
少了草根精英树莓派怎么行。
Hello Android Things
买到的开发版都是没有装操作系统系统的,第一步先把Android Things刷到板子里。
Flash image(刷机)
官方刷机教程
以Intel Edison为例:
0.Android SDK Platform Tools 25.0.3以上,fastboot工具添加到PATH环境变量中,以便从任意目录运行。
1.下载后打Intel Flash Tool,加打开下载好的对应刷机包。
使用USB线链接Edison,如果Edison没有显示,换USB口和线试试。
Start to Flash(开始刷机)
4.使用Fastboot刷入系统镜像,此时需要几十秒,光System.img就有500多M。
5.刷入Google Service镜像。
6.刷入OEM镜像。
7.重启
8.验证系统状态。
如果出现以下Error,把Intel Flash Tool关掉,重新连接下USB。
1 2 3 4 5 6 7 8
| List of devices attached adb server version (35) doesn't match this client (36); killing... adb E 69469 2714428 usb_osx.cpp:327] Could not open interface: e00002c5 adb E 69469 2714428 usb_osx.cpp:289] Could not find device interface error: could not install *smartsocket* listener: Address already in use ADB server didn't ACK * failed to start daemon * error: cannot connect to daemon
|
Connecting WIFI(联网)
依然是熟悉的adb命令和服务启动参数
1 2 3 4 5
| $ adb shell am startservice \ -n com.google.wifisetup/.WifiSetupService \ -a WifiSetupService.Connect \ -e ssid SSID \ -e passphrase password
|
用logcat查看网络状态
1 2 3 4 5
| $ adb logcat -d | grep Wifi ... V WifiWatcher: Network state changed to CONNECTED V WifiWatcher: SSID changed: ... I WifiConfigurator: Successfully connected to ...
|
Ping检测
1 2 3 4 5 6
| $ adb shell ping 114.114.114.114 PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data. 64 bytes from 114.114.114.114: icmp_seq=1 ttl=57 time=6.67 ms 64 bytes from 114.114.114.114: icmp_seq=2 ttl=57 time=55.5 ms 64 bytes from 114.114.114.114: icmp_seq=3 ttl=57 time=23.0 ms 64 bytes from 114.114.114.114: icmp_seq=4 ttl=57 time=245 ms
|
Hello Android Things项目
Android Studio中新建项目
在build.gralde中添加依赖com.google.android.things:androidthings
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.geekdev.alpha.androidthings" minSdkVersion 24 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) provided 'com.google.android.things:androidthings:0.1-devpreview' compile 'com.google.android.things.contrib:driver-button:0.1' compile 'com.android.support:appcompat-v7:25.1.0' testCompile 'junit:junit:4.12' }
|
此处依赖方式是provided,让Android Things使用系统中的库。
添加activity
添加一个主activity并配置AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.geekdev.alpha.androidthings"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <uses-library android:name="com.google.android.things"/> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.IOT_LAUNCHER"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> </application> </manifest>
|
在Activity中输出Hello Android Things
1 2 3 4 5 6 7 8 9
| public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "Hello Android Things!"); } }
|
运行输出
直接Command+R,可以在logcat窗口中看到结果了。
Peripheral I/O
不满足于Hello Android Things,继续来使用Android Things对外设进行操作。
使用Button driver对LED灯进行开关操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
| package com.geekdev.alpha.androidthings; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.widget.Button; import com.google.android.things.contrib.driver.button.ButtonInputDriver; import com.google.android.things.pio.Gpio; import com.google.android.things.pio.PeripheralManagerService; import java.io.IOException; * Created by Alpha. * <p> * Example of using Button driver for toggling a LED. * <p> * This activity initialize an InputDriver to emit key events when the button GPIO pin state change * and flip the state of the LED GPIO pin. * <p> * You need to connect an LED and a push button switch to pins specified in {@link BoardDefaults} * according to the schematic provided above. */ public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); private Gpio mLedGpio; private ButtonInputDriver mButtonInputDriver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "Hello Android Things!"); Log.i(TAG, "Starting ButtonActivity"); PeripheralManagerService pioService = new PeripheralManagerService(); try { Log.i(TAG, "Configuring GPIO pins"); mLedGpio = pioService.openGpio(BoardDefaults.getGPIOForLED()); mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW); Log.i(TAG, "Registering button driver"); mButtonInputDriver = new ButtonInputDriver( BoardDefaults.getGPIOForButton(), Button.LogicState.PRESSED_WHEN_LOW, KeyEvent.KEYCODE_SPACE); mButtonInputDriver.register(); } catch (IOException e) { Log.e(TAG, "Error configuring GPIO pins", e); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SPACE) { setLedValue(true); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SPACE) { setLedValue(false); return true; } return super.onKeyUp(keyCode, event); } * Update the value of the LED output. */ private void setLedValue(boolean value) { try { mLedGpio.setValue(value); } catch (IOException e) { Log.e(TAG, "Error updating GPIO value", e); } } @Override protected void onDestroy() { super.onDestroy(); if (mButtonInputDriver != null) { mButtonInputDriver.unregister(); try { mButtonInputDriver.close(); } catch (IOException e) { Log.e(TAG, "Error closing Button driver", e); } finally { mButtonInputDriver = null; } } if (mLedGpio != null) { try { mLedGpio.close(); } catch (IOException e) { Log.e(TAG, "Error closing LED GPIO", e); } finally { mLedGpio = null; } mLedGpio = null; } } }
|
添加一个开发板的处理工具类BoardDefaults.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| package com.geekdev.alpha.androidthings; import android.os.Build; import com.google.android.things.pio.PeripheralManagerService; import java.util.List; * Created by Alpha. */ public class BoardDefaults { private static final String DEVICE_EDISON_ARDUINO = "edison_arduino"; private static final String DEVICE_EDISON = "edison"; private static final String DEVICE_RPI3 = "rpi3"; private static final String DEVICE_NXP = "imx6ul"; private static String sBoardVariant = ""; * Return the GPIO pin that the LED is connected on. * For example, on Intel Edison Arduino breakout, pin "IO13" is connected to an onboard LED * that turns on when the GPIO pin is HIGH, and off when low. */ public static String getGPIOForLED() { switch (getBoardVariant()) { case DEVICE_EDISON_ARDUINO: return "IO13"; case DEVICE_EDISON: return "GP45"; case DEVICE_RPI3: return "BCM6"; case DEVICE_NXP: return "GPIO4_IO21"; default: throw new IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE); } } * Return the GPIO pin that the Button is connected on. */ public static String getGPIOForButton() { switch (getBoardVariant()) { case DEVICE_EDISON_ARDUINO: return "IO12"; case DEVICE_EDISON: return "GP44"; case DEVICE_RPI3: return "BCM21"; case DEVICE_NXP: return "GPIO4_IO20"; default: throw new IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE); } } private static String getBoardVariant() { if (!sBoardVariant.isEmpty()) { return sBoardVariant; } sBoardVariant = Build.DEVICE; if (sBoardVariant.equals(DEVICE_EDISON)) { PeripheralManagerService pioService = new PeripheralManagerService(); List<String> gpioList = pioService.getGpioList(); if (gpioList.size() != 0) { String pin = gpioList.get(0); if (pin.startsWith("IO")) { sBoardVariant = DEVICE_EDISON_ARDUINO; } } } return sBoardVariant; } }
|
运行到如下的Raspberry Pi 3中,使用按钮来控制LED灯。
Code
所有示例项目代码可有Github中找到.
更多
到这里,你已经了解Android Things的历史,特点和开发。更多关于Google技术的内容,欢迎加入G tech online meetup微信群进行交流。