基于百度定位SDK的定位服务的实现

标签: 百度 sdk 服务 | 发表时间:2013-08-22 15:44 | 作者:android_ls
出处:http://blog.csdn.net

 

转载请标明出处: http://blog.csdn.net/android_ls/article/details/10179013

一、定位模块的需求:我们想知道使用我们应用的用户的大概位置,每隔五分钟上传一次用户当前所在地的经纬度值到服务器端。

二、需求分析

     A、UML流程图如下:

    B、定位服务,功能具体分析:

      启动方式:手动、开机自启动。
      关闭方式:用户在设置里强制停止应用、关闭手机。(用户使用其他软件杀死掉我们的服务,用户重新启动应用服务才会开启。)
      1、开机自启动服务,等1分钟后开始检测网络状态和GPS是否开启,并通过通知栏提醒用户。(未开启时,提醒三次,5分钟提醒一次)
      2、直接启动应用,立即开始检测网络状态和GPS是否开启,并通过弹Dialog提示用户。若用户不愿意开启网络,即网络不可用时,直接退出应用。
     3、用户在设置-->应用程序-->正在运行的服务里面手动停止掉服务后,服务自动重启。
     4、网络检测可用,开始检测GPS。用户不开启GPS时,使用基站定位(WLAN、3G/2G)。
     5、网络检测可用,启动百度地图定位服务,每隔五分钟确认一次当前我所在的位置,并将经纬度值上传服务器端。
     6、网络检测可用,但是在发送定位数据时,网络断开了,以Toast形式提醒用户。
     7、网络检测可用,但是在定位过程中,网络断开了,并且目前打开的不是我们的应用(也就是说服务在后台运行),以通知的形式提醒用户。
     8、服务运行过程中,意外停止了。当用户重启应用后,服务重新启动。
     9、添加了开机自启动后,检测网络和通过通知栏提醒用户当前的网络、GPS状态。
    10、服务运行过程中,网络检测返回的标识的处理。

三、编码实现:

      完整源码下载地址: http://download.csdn.net/detail/android_ls/5993623 核心类的源码如下,

      A、服务类的源码如下:

package com.android.mobile.locator;

import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;

import com.android.mobile.locator.net.HttpRequester;
import com.android.mobile.locator.utils.Constant;
import com.android.mobile.locator.utils.FileUtil;
import com.android.mobile.locator.utils.GPSUtil;
import com.android.mobile.locator.utils.LogUtil;
import com.android.mobile.locator.utils.NetWorkUtil;
import com.android.mobile.locator.utils.NotificationUtil;
import com.android.mobile.locator.utils.ServiceUtil;
import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.easi.mobile.locator.R;

/**
 * 类名:MobileLocatorService
 * 功能描述:定位服务类。
 * @author android_ls
 * 创建日期:2013-2-18
 */
public class MobileLocatorService extends Service {

    /**
     * Service action.
     */
    public static final String ACTION_MOBILE_LOCATOR_SERVICE = "com.easi.mobile.locator.MobileLocatorService";

    /**
     * 间隔时间5分钟
     */
     private static final int DELAY_TIME = 5*60*1000;

    /**
     * 开机一分钟后开始检测网络
     */
    private static final int CHECK_NETWORK_DELAY_TIME = 1 * 60 * 1000;

    private Context mContext;

    /**
     * 定位SDK的核心类
     */
    private LocationClient mLocationClient;

    /**
     * 定位结果处理器 # class MyLocationListener implements BDLocationListener{}
     */
    private MyLocationListener mLocationListener;

    /**
     * 通知工具类
     */
    private NotificationUtil mNotificationUtil;

    /**
     * 服务的启动方式,开机自启动/手动启动
     */
    private int startingMode = -1;

    /**
     * 当前网络是否可用的标志
     */
    private boolean isOpenNetwork = false;

    /**
     * 检测网络的次数(5分钟一次,检测三次)
     */
    private int checkNetworkNumber = 0;

    /**
     * 定时器
     */
    private Timer mTimer;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        LogUtil.e("--------------MobileLocatorService onCreate()----------------");

        mNotificationUtil = new NotificationUtil(this);

        mContext = MobileLocatorService.this;

        // 设置为前台进程,尽量避免被系统干掉。
        // MobileLocatorService.this.setForeground(true);

        // 初始化定位服务,配置相应参数
        initLocationService();

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.e("--------------MobileLocatorService onStartCommand()----------------");

        if (intent != null) {
            startingMode = intent.getIntExtra("startingMode", -1);
            LogUtil.i("startingMode = " + startingMode);
            if (startingMode == Constant.HANDLER_START_SERVICE) {

                LogUtil.e("-------------手动启动---------------");
                
                // 判断服务是否已开启
                boolean isRun = ServiceUtil.isServiceRun(getApplicationContext(), "com.baidu.location.f");
                LogUtil.i("isRun = " + isRun);
                if (isRun == false) {
                    LogUtil.e("MobileLocatorService start Location Service");

                    // 没启动,开启定位服务
                    mLocationClient.start();
                }
            } else {
                // 关闭手机,再次开启手机。这种情况下,startingMode的值获取不到。
                // 关机重启,这种情况下,startingMode的值可以拿到。
                // if (startingMode == Constant.BOOT_START_SERVICE) {

                LogUtil.e("-------------开机自启动---------------");

                checkNetworkNumber++;

                // 第一次,1分钟后检测网络
                mHandler.postDelayed(new Runnable() {

                    @Override
                    public void run() {

                        LogUtil.e("--------------第一次检测网络---------------");

                        checkNetwork();

                        Message msg = new Message();
                        msg.arg1 = Constant.CHECK_NETWORK_CONNECT_FLAG;
                        mHandler.sendMessage(msg);

                    }
                }, CHECK_NETWORK_DELAY_TIME);

            }

        }

        return Service.START_REDELIVER_INTENT;
    }

    /**
     * 检测网络是否可用
     */
    private void checkNetwork() {
        // 如果网络不可用,开启GPS就没有意义
        if (NetWorkUtil.isNetworkAvailable(mContext)) {
            isOpenNetwork = true;

            if (GPSUtil.isOPen(mContext) == false) {
                // 通知用户GPS未开启
                mNotificationUtil.sendGPSNotification();
            }

            LogUtil.i("MobileLocatorService start Location Service");

            // 开启定位服务
            mLocationClient.start();

        } else {
            // 通知用户网络不可用
            mNotificationUtil.sendNetworkNotification();
        }
    }

    /**
     * 初始化定位服务,配置相应参数
     */
    private void initLocationService() {
        mLocationClient = new LocationClient(this.getApplicationContext());
        mLocationListener = new MyLocationListener();
        mLocationClient.registerLocationListener(mLocationListener);

        LocationClientOption locationOption = new LocationClientOption();
        locationOption.setOpenGps(true);
        locationOption.setCoorType("bd09ll");
        locationOption.disableCache(true);
        locationOption.setPriority(LocationClientOption.GpsFirst);
        locationOption.setScanSpan(DELAY_TIME);
        locationOption.setProdName(this.getString(R.string.loaction_prod_name));

        mLocationClient.setLocOption(locationOption);
    }

    Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            int result = msg.arg1;

            switch (result) {
            case Constant.CHECK_NETWORK_CONNECT_FLAG:

                // 第一检测网络,直接过了。(已打开)
                boolean isRun = ServiceUtil.isServiceRun(getApplicationContext(), "com.baidu.location.f");
                LogUtil.i("isRun = " + isRun);
                if (isOpenNetwork && isRun) {
                    LogUtil.i("--------------第一次检测网络,直接过了。(已打开)----------------");
                    return;
                }

                mTimer = new Timer();
                mTimer.schedule(new TimerTask() {

                    @Override
                    public void run() {
                        checkNetworkNumber++;
                        LogUtil.i("Timer checkNetworkNumber = " + checkNetworkNumber);

                        checkNetwork();

                        boolean isRun = ServiceUtil.isServiceRun(getApplicationContext(), "com.baidu.location.f");
                        if (isOpenNetwork && isRun) {
                            mNotificationUtil.cancelNotification(Constant.NOTIFICATIO_NETWORK_NOT_OPEN);
                            mTimer.cancel();
                            return;
                        } else {
                            if (checkNetworkNumber == 3) {

                                LogUtil.e("--------------第三次检测网络,还未开启,直接退出应用---------");

                                // 检查网络,提醒了用户三次依然未开,退出应用。
                                mNotificationUtil.cancelNotification(Constant.NOTIFICATIO_NETWORK_NOT_OPEN);
                                mNotificationUtil.cancelNotification(Constant.NOTIFICATIO_GPS_NOT_OPEN);
                                mTimer.cancel();

                                // System.gc();
                                System.exit(0);
                            }
                        }
                    }
                }, 0, DELAY_TIME);

                break;

            case Constant.UPLOAD_LOACTION_SUCCESS:
                LogUtil.i("您当前的位置上传服务器成功!");
                // Toast.makeText(getApplicationContext(), "您当前的位置上传服务器成功!", Toast.LENGTH_LONG).show();        
                break;

            case Constant.LOCATION_NETWORK_EXCEPTION:
                LogUtil.e("网络异常!请检查您的网络连接。");
                //  网络异常,没有成功向服务器发起请求。
                // Toast.makeText(getApplicationContext(), "网络异常!请检查您的网络连接。", Toast.LENGTH_LONG).show();

                // 通知用户网络不可用
                mNotificationUtil.sendNetworkNotification();
                break;

            case Constant.LOCATION_NETWORK_CONNECT_FAIL:
                LogUtil.e("网络连接失败,请将网络关闭再重新打开试试!");

                // 通知用户网络不可用
                mNotificationUtil.sendNetworkNotification();
                break;

            case Constant.UPLOAD_LOACTION_FAIL:
                // Toast.makeText(getApplicationContext(), "您当前的位置上传服务器失败!请查看下你的网络状态。", Toast.LENGTH_LONG).show();
                LogUtil.e("您当前的位置上传服务器失败!");
                break;

            default:
                break;
            }
        }
    };

    
    class MyLocationListener implements BDLocationListener {
        double longitude;

        double latitude;

        @Override
        public void onReceiveLocation(BDLocation location) {
            if (location == null) {
                return;
            }

            LogUtil.i("BDLocationListener onReceiveLocation()");

            /* location.getLocType()的返回值含义:
               61 : GPS定位结果
               62 : 扫描整合定位依据失败。此时定位结果无效。
               63 : 网络异常,没有成功向服务器发起请求。此时定位结果无效。
               65 : 定位缓存的结果。
               66 : 离线定位结果。通过requestOfflineLocaiton调用时对应的返回结果
               67 : 离线定位失败。通过requestOfflineLocaiton调用时对应的返回结果
               68 : 网络连接失败时,查找本地离线定位时对应的返回结果
               161: 表示网络定位结果
               162~167: 服务端定位失败。*/
            int locType = location.getLocType();

            longitude = location.getLongitude();
            latitude = location.getLatitude();

            // TODO 调试使用
            StringBuffer sb = new StringBuffer(256);
            sb.append(" time : ");
            sb.append(location.getTime());
            sb.append("\n error code : ");
            sb.append(locType);
            sb.append("\n latitude : ");
            sb.append(longitude);
            sb.append("\n longitude : ");
            sb.append(latitude);
            LogUtil.i("BDLocationListene " + sb.toString());

            if (locType == Constant.LOCATION_GPS || locType == Constant.LOCATION_NETWORK) {

                 //  GPS定位结果、网络定位结果
                 mHandler.post(new Runnable() {

                     @Override
                     public void run() {
                         String userId = "bgao";
                         int result = send(userId, longitude, latitude);
                         
                         Message msg = new Message();
                         msg.arg1 = result;
                         mHandler.sendMessage(msg);
                     }
                 });

            } else if (locType == Constant.LOCATION_NETWORK_EXCEPTION || locType == Constant.LOCATION_NETWORK_CONNECT_FAIL) {
                //  网络异常,没有成功向服务器发起请求。
                Message msg = new Message();
                msg.arg1 = locType;
                mHandler.sendMessage(msg);
            }
        }

        @Override
        public void onReceivePoi(BDLocation arg0) {

        }
    }

    /**
     * 向服务器端当前位置的经纬度
     * @param usetId 用户ID
     * @param longitude 经度值
     * @param latitude 纬度值
     */
    private int send(String usetId, double longitude, double latitude) {
        StringBuffer params = new StringBuffer();
        params.append("event=save");
        params.append("¤tPointX=");
        params.append(longitude);
        params.append("¤tPointY=");
        params.append(latitude);
        params.append("&userId=");
        params.append(usetId);

        try {
            InputStream inputStream = HttpRequester.post(Constant.UPLOAD_GPS_URL, params);
            if (inputStream != null) {
                String result = new String(FileUtil.read(inputStream));
                String time = (new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")).format(System.currentTimeMillis());
                LogUtil.e("网络请求返回的结果:result = " + result + "\t 时间:" + time);

                if ("Y".equals(result)) {
                    return 1;
                } else if ("N".equals(result)) {
                    return 0;
                } else {
                    LogUtil.e("服务器端返回的值与预先商定的不否! ");
                }
            } else {
                LogUtil.e("网络请求成功,但是返回的数据流为NULL");
            }
        } catch (IOException e) {
            LogUtil.e("IOException 服务器访问失败!");
            e.printStackTrace();
            return 0;
        }

        return 0;
    }

    @Override
    public void onDestroy() {
        LogUtil.e("---------------MobileLocatorService onDestroy()----------------");

        if (mLocationClient != null && mLocationClient.isStarted()) {
            mLocationClient.stop();
            if (mLocationListener != null) {
                mLocationClient.unRegisterLocationListener(mLocationListener);
            }
        }

        SharedPreferences sp = mContext.getSharedPreferences("MobileLocator", Context.MODE_PRIVATE);
        String result = sp.getString("instruct", null);
        LogUtil.i("MobileLocatorService onDestroy() result = " + result);
        if ("exit".equals(result)) {
            sp.edit().putString("instruct", "true").commit();
            LogUtil.e("---------------MobileLocatorService onDestroy()-----------1-----");
            System.exit(0);
            return;
        }

        LogUtil.e("---------------MobileLocatorService onDestroy()---------2-------");

        // 销毁时重新启动Service
        Intent intent = new Intent(ACTION_MOBILE_LOCATOR_SERVICE);
        intent.putExtra("startingMode", startingMode);
        this.startService(intent);
    }

}

       B、启动时系统发出的广播的接收器类源码:

package com.android.mobile.locator;

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

import com.android.mobile.locator.utils.Constant;
import com.android.mobile.locator.utils.LogUtil;

/**
 * 类名:BootBroadcastReceiver 
 * 功能描述:启动时系统发出的广播的接收器
 * #<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 * @author android_ls
 */
public class BootBroadcastReceiver extends BroadcastReceiver {

	private static final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED";

	@Override
	public void onReceive(Context context, Intent intent) {
	    LogUtil.i("Boot this system , BootBroadcastReceiver onReceive()");
		
		if (intent.getAction().equals(ACTION_BOOT)) {
		    LogUtil.i("BootBroadcastReceiver onReceive(), MobileLocatorService Start");
		    
		    Intent mIntent = new Intent(MobileLocatorService.ACTION_MOBILE_LOCATOR_SERVICE);
		    mIntent.putExtra("startingMode", Constant.BOOT_START_SERVICE);
			context.startService(mIntent);
		}

	}

}

      C、关机时系统发出的广播的接收器类源码:

package com.android.mobile.locator;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;

import com.android.mobile.locator.utils.LogUtil;

/**
 * 类名:ShutdownBroadcastReceiver
 * 功能描述:关机时系统发出的广播的接收器
 * @author android_ls
 */
public class ShutdownBroadcastReceiver extends BroadcastReceiver {

	private static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
	
	@Override
	public void onReceive(Context context, Intent intent) {
	    LogUtil.e("Shut down this system, ShutdownBroadcastReceiver onReceive()");
	    
		if (intent.getAction().equals(ACTION_SHUTDOWN)) {
		    LogUtil.i("ShutdownBroadcastReceiver onReceive(), MobileLocatorService Stop");
		    
		    SharedPreferences sp = context.getSharedPreferences("MobileLocator", Context.MODE_PRIVATE);
		    sp.edit().putString("instruct", "exit").commit();
		    
			context.stopService(new Intent(MobileLocatorService.ACTION_MOBILE_LOCATOR_SERVICE));
		}

	}

}

       D、在AndroidManifest.xml文件中的配置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.easi.mobile.locator"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <!--
    android:sharedUserId="android.uid.system"
    android:killAfterRestore="true"
    android:process=":remote"  
    android:enabled="true"
    -->

    <application
        android:allowClearUserData="false"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name="com.android.mobile.locator.MobileLocatorActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

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

        <service
            android:enabled="true"
            android:name="com.android.mobile.locator.MobileLocatorService"
            android:process=":remote" >
            <intent-filter >
                <action android:name="com.easi.mobile.locator.MobileLocatorService" />
            </intent-filter>
        </service>
        <service
            android:enabled="true"
            android:name="com.baidu.location.f"
            android:process=":remote" />

        <receiver android:name="com.android.mobile.locator.BootBroadcastReceiver" >
            <intent-filter >
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.HOME" /> 
            </intent-filter>
        </receiver>
        <receiver android:name="com.android.mobile.locator.ShutdownBroadcastReceiver" >
            <intent-filter >
                <action android:name="android.intent.action.ACTION_SHUTDOWN" />
                <category android:name="android.intent.category.HOME" />
            </intent-filter>
        </receiver>
    </application>

    <!-- 授予应用程序访问系统开机事件的权限 -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

</manifest>



      前段时间由于忙于公司的项目,所以好久没更新博客了,后面我会继续更新的,谢谢大家的支持!
 


 

 

作者:android_ls 发表于2013-8-22 15:44:32 原文链接
阅读:4 评论:0 查看评论

相关 [百度 sdk 服务] 推荐:

基于百度定位SDK的定位服务的实现

- - CSDN博客推荐文章
一、定位模块的需求:我们想知道使用我们应用的用户的大概位置,每隔五分钟上传一次用户当前所在地的经纬度值到服务器端.      A、UML流程图如下:.     B、定位服务,功能具体分析:.       启动方式:手动、开机自启动.       关闭方式:用户在设置里强制停止应用、关闭手机. (用户使用其他软件杀死掉我们的服务,用户重新启动应用服务才会开启.

phonegap利用百度地图sdk定位

- - snoopyxdy的博客
近期的一个phonegap项目把我做的焦头烂额,最让人蛋疼的就是安卓4.1.x对html5的定位获取经纬度有个无比巨大的坑,一般我们利用如下代码进行html5的定位. 另外一个巨坑也直接影响了我对这个问题的判断,如果我重启了安卓4.1的机器,那么上述代码将能够正常跑大约几小时,甚至半天,可是到了第二天又会出现同样的问题,无尽的timeout.

百度地图SDK for Android【Demo Place详情页】

- - CSDN博客移动开发推荐文章
        Place详情展示页面可以帮助开发者便捷的展示Poi详情信息,此外通过详情展示页面您还可以实现电话的拨打等功能. 今天将向大家介绍Place详情页的具体使用方法.         第一步,创建工程,并放置基础地图mapview控件(详细介绍请参考: 百度地图SDK for Android【Demo地图展示】 ).

Goolge发布Chromecast SDK

- - Chrome迷
自从有了Chromecast,用户把各种自己喜爱的在线内容投放到电视屏幕上变得非常方便,投放过程简单得只需要用户按下手机、平板和笔记本上的投放按钮. 今日,为了让内容更容易地被呈现到电视上,Google发布了Chromecast软件开发套件(SDK),开发者们可以在自己的应用和网站上提供Chromecast接入.

微软发布Kinect SDK For Windows

- skyan - Solidot
微软遵守承诺发布了Kinect SDK For Windows,允许教育研究人员或爱好者为这种体感控制器开发新的应用. SDK是基于XBOX 360上使用的软件,但微软将其移植到了.NET平台,支持C#、VB.NET或C++.NET等开发语言. Kinect SDK For Windows的运行平台是Windows 7,最低硬件需求是4GB RAM、双核处理器和DirectX 9.0c显卡,开发工具是Visual Studio 2010 Express(免费版),.NET Framework 4.0.

Kinect for Windows SDK出炉了

- 杯子 - 增强视觉 | 计算机视觉 增强现实
官方首页:http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/default.aspx. 微软从PrimeSense买来Kinect硬件加上来自Andrew Blake 带领的MSR剑桥视觉组的算法,让微软在本已经热卖的XBox360上又大赚了一大笔.

Kinect for Windows SDK 本周发布

- 王辉 - LiveSino - LiveSide 中文版
微软在 MIX 11 大会上宣布了 Kinect for Windows SDK,也演示了相关的开发和应用,但始终未提供 Beta 版 SDK 的下载. 微软西班牙总裁 María Garaña 周三披露微软计划本周发布 Kinect for Windows SDK Beta. 根据微软研究院网站,Kinect for Windows SDK 包括:.

GAE SDK 1.5.5 版发布

- Ken - python.cn(jobs, news)
本想睡觉了,突然看到GAE SDK 1.5.5版发布了,于是就再坚持一下,写完本文吧. 这个版本最重要的更新就是支持Python 2.7了. 关于Python 2.7的新功能,可以查看《What's New in Python 2.7》这篇文档. 在app.yaml中设置threadsafe: true即可启用,必须使用WSGI接口(直接在app.yaml里设置WSGI application对象的路径,而非Python文件).

GAE SDK 1.5.5版发布

- f41c0n - keakon的涂鸦馆
本想睡觉了,突然看到GAE SDK 1.5.5版发布了,于是就再坚持一下,写完本文吧. 这个版本最重要的更新就是支持Python 2.7了. 关于Python 2.7的新功能,可以查看《What's New in Python 2.7》这篇文档. 在app.yaml中设置threadsafe: true即可启用,必须使用WSGI接口(直接在app.yaml里设置WSGI application对象的路径,而非Python文件).

Android 4.0 SDK 已可下载

- Elic - cnBeta.COM
Google今日在香港发布了Android 4.0系统,并面向程序员发布了开发工具包,现已可以在Android开发中心下载. 新的SDK支持移动数据控制、面部识别、高分辨率图像、增强共享等功能,详细信息请参看开发者中心页面:.