Android Things-物联网开发

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,加打开下载好的对应刷机包。

  1. 使用USB线链接Edison,如果Edison没有显示,换USB口和线试试。


  2. Start to Flash(开始刷机)


4.使用Fastboot刷入系统镜像,此时需要几十秒,光System.img就有500多M。




5.刷入Google Service镜像。



6.刷入OEM镜像。

7.重启
fastboot reboot

8.验证系统状态。



如果出现以下Error,把Intel Flash Tool关掉,重新连接下USB。

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命令和服务启动参数

$ adb shell am startservice \
    -n com.google.wifisetup/.WifiSetupService \
    -a WifiSetupService.Connect \
    -e ssid SSID \
    -e passphrase password

用logcat查看网络状态

$ adb logcat -d | grep Wifi
...
V WifiWatcher: Network state changed to CONNECTED
V WifiWatcher: SSID changed: ...
I WifiConfigurator: Successfully connected to ...

Ping检测

$ 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

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

<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

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灯进行开关操作。

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.
 * 

 * Example of using Button driver for toggling a LED.
 * 


 * 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.
 * 


 * 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");
            // Initialize and register the InputDriver that will emit SPACE key events
            // on GPIO state changes.
            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) {
            // Turn on the LED
            setLedValue(true);
            return true;
        }

        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_SPACE) {
            // Turn off the LED
            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

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;
        // For the edison check the pin prefix
        // to always return Edison Breakout pin name when applicable.
        if (sBoardVariant.equals(DEVICE_EDISON)) {
            PeripheralManagerService pioService = new PeripheralManagerService();
            List 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微信群进行交流。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容