GVKun编程网logo

Android位置服务开发(android定位开发)

22

关于Android位置服务开发和android定位开发的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于android4.0蓝牙服务开启流程分析、AndroidGeofence禁用/重新启动

关于Android位置服务开发android定位开发的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于android 4.0 蓝牙服务开启流程分析、Android Geofence禁用/重新启动位置服务时自动删除、Android – Android 1.6中开发的应用程序可以在Android 2.0中运行吗?、android – 位置服务GPS Force关闭等相关知识的信息别忘了在本站进行查找喔。

本文目录一览:

Android位置服务开发(android定位开发)

Android位置服务开发(android定位开发)

1. 使用LocationManager获取地理位置信息

代码如下:

private TextView positiontext;
    private String provider;
    private LocationManager manager;

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

        positiontext = (TextView) findViewById(R.id.tv_show1);

        // 使用LocationManager获取地理位置信息,先得到LocationManager对象
        // 调用Context的getSystemService()方法接受一个字符串用于确定获取系统的哪个服务
        // 传入Context.LOCATION_SERVICE这个服务
        manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        // 先用一个List集合获取所有可用的位置提供器
        List<String> providerlist = manager.getProviders(true);

        // 选择一个位置提供器来确定设备当前位置,有三种可供选择,但是必须在设置中提前打开相关权限
        // NETWORK_PROVIDER使用网络定位,精度差,耗电量少,用的较多
        // GPS_PROVIDER使用GPS定位,精度高,耗电量高,用得较多
        // PASSIVE_PROVIDER用的较少
        if (providerlist.contains(LocationManager.GPS_PROVIDER)) {
            provider = LocationManager.GPS_PROVIDER;
        } else if (providerlist.contains(LocationManager.NETWORK_PROVIDER)) {
            provider = LocationManager.NETWORK_PROVIDER;
        } else {
            Toast.makeText(this, "没有可用的位置提供器", Toast.LENGTH_LONG).show();
        }

        // 将选择好的位置提供器传入到getLastKNownLocation()得到一个Location对象;
        // 这个Location对象中包含了经度、纬度、海拔等一系列信息,然后提取需要的信息
        Location location = manager.getLastKNownLocation(provider);

        if (location != null) {

            // 显示当前的位置信息
            positiontext.setText("latitude is " + location.getLatitude() + "\n"
                    + "Longitude is " + location.getLongitude());

        }

        // requestLocationUpdates()方法实时更新用户的位置信息,接收四个参数
        // ①第一个参数是位置提供器的类型
        // ②第二个参数是监听位置变化间隔的毫秒数
        // ③第三个参数是监听位置变化间隔的距离,达到设定距离时,触发第四个参数的监听器的onLocationChanged()方法,
        // 并把新的位置信息作为参数传入
        // ④第四个参数是LocationListener()监听器接口,所以应该传入的是它的实现类的对象
        manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10,
                listener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 关闭程序时将监听器移除
        if (manager != null) {
            manager.removeUpdates(listener);
        }
    }

    // 定义LocationListener()监听器接口的实现类
    LocationListener listener = new LocationListener() {

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderdisabled(String provider) {

        }

        @Override
        public void onLocationChanged(Location location) {
            // 更新当前位置信息
            positiontext.setText("latitude is " + location.getLatitude() + "\n"
                    + "Longitude is " + location.getLongitude());

        }
    };

注意事项:Manifest文件中添加权限

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

2. 使用百度地图显示

①登录网址

http://lbsyun.baidu.com/apiconsole/key

注册成为一名百度开发者,并申请API Key,注册页面如下

注册成功,点击邮箱链接激活

接下来正式申请API Key,注意事项看下面两图

特别注意,每开发一个程序都应该重新申请Key,因为每次的包名不一样

点击提交之后就会看到自己申请的Key了

接下来进入这个网址

http://lbsyun.baidu.com/index.PHP?title=androidsdk/sdkandev-download

下载Android版的SDK,接下来由于新旧版本以及指导书籍的不同,会有不同的做法,对我这个来说,我用的是Eclipse+新版本的baiduAPI,找了好久在网上找到一篇可以使用的教程链接是

http://blog.csdn.net/chenyufeng1991/article/details/46980733

以下详细介绍配置过程

①如下图所示,箭头所指的地方都打勾,点击开发包进行下载

②下载完成后,会有多个jar包和一个armabi文件夹,把jar包和armabi文件夹复制到Eclipse工程目录下的libs文件夹下。如图:

③在百度服务器端的配置已经全部完成,下面开始写代码,在AndroidMenifest.xml中添加:

这里特别注意,添加的这一段代码必须放在原来的application里面位置,如下面代码所示

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <Meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="BtM2ugHA16R8fvdyAc5cyxR9Q6GGMyoF" />

        <activity
            android:name=".SecondActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
        </activity>
</application>

④添加权限:参考百度开发文档链接见⑦

 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- 这是使用LocationManager获取地理位置信息的时候要用的权限 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- //获取设备网络状态,禁用后无法获取网络状态 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- //网络权限,当禁用后,无法进行检索等相关业务 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- //读取设备硬件信息,统计数据 -->
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <!-- //读取系统信息,包含系统版本等信息,用作统计 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- //获取设备的网络状态,鉴权所需网络代理 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- //允许sd卡写权限,需写入地图数据,禁用后无法显示地图 -->
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <!-- //获取统计数据 -->
    <uses-permission android:name="android.permission.GET_TASKS" />
    <!-- //鉴权所需该权限获取进程列表 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <!-- //使用步行AR导航,配置Camera权限 -->

⑤在布局文件中添加地图控件:

特别注意,这里的代码提示不会体现出来,所以需要自己敲出这些代码

<com.baidu.mapapi.map.MapView  
       android:id="@+id/bmapView"  
       android:layout_width="fill_parent"  
       android:layout_height="fill_parent"  
       android:clickable="true" /> 

⑥在MainActivity.java中添加代码:

MapView mMapView = null;// 用于显示地图的控件;

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

        // 初始化地图SDK,注意一定要写在
        // setContentView(R.layout.activity_main)代码前
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_second);

        // 绑定控件;
        mMapView = (MapView) findViewById(R.id.bmapView);

    }

    @Override
    protected void onResume() {
        super.onResume();
        // 管理地图的生命周期:显示
        mMapView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 管理地图的生命周期:暂停
        mMapView.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 管理地图的生命周期:销毁
        mMapView.onDestroy();
    }

⑦具体更多内容请参考百度地图开发文档:

http://developer.baidu.com/map/index.PHP?title=androidsdk/guide/hellobaidumap

3. 使用百度地图定位到自己的位置

地图显示出来之后默认的是北京的地图,要定位到自己的位置需要加入下面代码

MapView mMapView = null;// 用于显示地图的控件;
    private BaiduMap map; // 百度地图提供的总控制器类
    private boolean showmapflag = true;

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

        // 初始化地图SDK,注意一定要写在
        // setContentView(R.layout.activity_main);代码前;

        Intent intent = getIntent();
        String data = intent.getStringExtra("extra_data");
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_second);

        // 绑定控件;
        mMapView = (MapView) findViewById(R.id.bmapView);
        // 获取BaiduMap对象
        map = mMapView.getMap();

        // 开始将经纬度坐标显示到地图的具体位置上
        if (showmapflag == true) {

            String[] location_data = data.split("=");
            // 借助LatLng类让地图定位到某一个经纬度,传入的参数是double类型的经纬度坐标值
            double my_latitude = Double.parseDouble(location_data[0]);
            double my_longitude = Double.parseDouble(location_data[1]);
            LatLng lng = new LatLng(my_latitude, my_longitude);

            // animateMapStatus()将地图定位到指定的经纬度上
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(lng);
            map.animateMapStatus(update);

            // 这里用来设置地图的缩放级别
            // MapStatusUpdateFactory的zoomTo()方法传入3-19之间的float数据
            update = MapStatusUpdateFactory.zoomTo(12f);
            map.animateMapStatus(update);

            // 这个变量的作用是防止多次调用animateMapStatus()方法,
            // 因为我们不动,只需要在程序第一次定位的时候显示一次即可
            showmapflag = false;

        }

    }

4. 使用百度地图定位到自己的位置并标记出来

①开启功能

// 设置设备显示在地图上的功能开启,否则设备的位置无法显示在地图上
        map.setMyLocationEnabled(true);

②设备位置显示

// 这是用来在地图上标记自己位置的代码,这段代码必须在if(showmapflag == true)语句
        // 的外面,因为地图只需要加载一次,但是自己的位置应该是实时更新的
        // MyLocationData.Builder类用来封装设备当前所在位置的
        MyLocationData.Builder builder = new MyLocationData.Builder();

        // 将经纬度传入MyLocationData.Builder类对应的方法中
        builder.latitude(my_latitude);
        builder.longitude(my_longitude);

        // build()方法生成一个MyLocationData对象
        MyLocationData locationData = builder.build();

        // 将MyLocationData对象传入到BaiduMap的setMyLocationData()方法中
        // 就可以在地图上将自己的位置显示出来
        map.setMyLocationData(locationData);

③功能关闭

@Override
    protected void onDestroy() {
        super.onDestroy();
        // 程序退出时设置设备显示在地图上的功能关闭
        map.setMyLocationEnabled(false);
        // 管理地图的生命周期:销毁
        mMapView.onDestroy();
    }

了解更多技术文章,欢迎关注我的个人公众号

android 4.0 蓝牙服务开启流程分析

android 4.0 蓝牙服务开启流程分析

第 1 章 Android 蓝牙系统

1.1 蓝牙技术简介

蓝牙 (Bleuetooth) 原是十世纪统一了丹麦的一个国王的名字,现取其 “统一” 的含义,用来意在统一无线局域网通讯的标准的蓝牙技术。蓝牙技术是爱立信,IBM,Intel 等世界 5 家著名大公司在 1998 年联合推出的一项无线通讯规范。随后成立的蓝牙技术特殊兴趣组织(SIG)来负责该技术的开发和技术协议的制定,如今全世界已有 1800 多家公司加盟该组织,最近微软公司也正式加盟并成为 SIG 组织的领导成员之一。它以低成本的近距离无线连接为基础,为移动通信设备建立一个短程无线连接。其实质内容是建立通用的无线电空中接口,使计算机和通信设备进一步结合,让不同的厂家生产便携式设备在没有电缆或电线相互连接的情况下,能在近距离范围内具有相互通信的一种技术。

1.2 蓝牙技术的特点

  • 全球范围适用

蓝牙技术使用无需经过许可的工业、科研和医疗 (ISM) 波段(2.4 至 2.485 GHz),使用展频、调频、全双工信号,标称速率为 1600 跳 / 秒。在大多数国家,无需经过许可便可使用 2.4 GHz ISM 波段。

  • 抗干扰

蓝牙技术的适配跳频 (AFH) 能力的设计目的是为了减少共用 2.4 GHz 频谱的无线技术之间出现的干扰。该功能会在频谱中寻找并无被占用的频带以供蓝牙技术使用。AFH 的工作原理是识别该频谱中的其他设备并避开这些设备所用的频带。跳频功能以 1 MHz 的频率在 79 个频段中进行切换,从而获得了较高的抗干扰能力,同时使该频谱中能够实现更加高效的传输。有了跳频功能,尽管其他技术与蓝牙技术同时使用,但蓝牙技术的用户仍能享有优质的性能表现。

  • 射程

射程根据不同的具体应用而定,尽管核心规格规定了最低射程,但这并非限制,制造商仍可根据其具体用例调整射程应用。

根据具体应用中使用的射频种类,射程将有所不同:

第三类射频 – 射程最高 1 米或 3 英尺

第二类射频 – 最常见于移动设备,射程为 10 米或 33 英尺

第一类射频 – 主要用于工业用例,射程为 100 米或 300 英尺

  • 低功耗

最常用的射频为第二类,其能耗为 2.5 mW。蓝牙技术的设计能耗非常之低。此外,规格允许射频处于非活跃状态时可以断电则进一步降低了能耗。3.0 版 HS 中的通用替代 MAC/PHY 能够发现高速设备的 AMP,并仅在需要进行数据传输时开启射频,实现了节能优势,同时增强了射频的安全性。对于无需高速数据传输率但需要最大限度延长电池寿命的设备而言,蓝牙低耗能技术为其实现了优化效果,其耗电量仅为传统蓝牙技术的 1/2 至 1/100。

 

1.3 蓝牙技术在 android 中的应用

1.3.1 蓝牙服务的启动

在前面章节 android 启动过程中介绍到 android 服务的启动,init 进程中,启动 Zygote 后,然后由 SystemServer 启动一系列服务,蓝牙服务就是在这个时候启动的。详细见代码:

/framework/base/services/java/com/android/server/SystemServer.java

if (SystemProperties.get("ro.kernel.qemu").equals("1")) {

} else {

……

bluetooth = new BluetoothService(context);

……

bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);

……

if (airplaneModeOn == 0 && bluetoothOn != 0) {

                    bluetooth.enable();

 }  

Bluetooth 服务的代码首先通过 SystemProperties 的 get 方法来判断系统是不是使用模拟器内核,如果是使用模拟器内核来启动 android 的系统,那么就会跳过蓝牙服务的启动,也就是说 Android 4.0 模拟器是不支持蓝牙系统的。否则就是一个实在的设备产品 (ro.kernel.qemu=0)

就是构造一个 bluetooth 的服务 (BluetoothService) 和一个蓝牙耳机服务 (BluetoothA2dpService)。

代码段最后一部分是判断开机是否要启用蓝牙,通过函数我们可以看到如果设备的飞行模式是关闭的并且 bluetooth 的那个开关是在 on。就是调用 bluetoothService 的 enable 方法使得我们设备开机的时候就将蓝牙开启。飞行模式就是那些使用无线频谱的模块都必须关掉,譬如:wifi,Bluetooth,GPS 等。接下来就是 BluetoothService 的 enable 方法了。

/framework/base/core/java/android/server/BluetoothService.java

public synchronized boolean enable(boolean saveSetting) {

mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,"Need BLUETOOTH_ADMIN permission");

if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {

            return false;

 }   

mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);

        return true;

 }   

蓝牙服务的 enable 的方法会先判断进程有没有操作权限,需要蓝牙管理的权限才能去 enable 蓝牙模块,然后还会再次判断系统的飞行模式有没有打开,如果此时飞行模式是 on 的话,那么会返回,还是不能打开服务。在确保拥有权限并且不是出于飞行模式的情况下,就会往蓝牙状态机发送一个 USER_TURN_ON 的命令。下面介绍一下 android 中的蓝牙状态机。

  • Poweroff

这就是蓝牙模块没有初始化的状态,这时候硬件模块是出于没有上电的状态。

  • Warmup

这个状态就是给设备上电,使设备能够从没电到待机状态。

  • Hotoff

Hotoff 我个人理解就是在模块上电了,出于一种待命的状态,如果收到了 turn_on_coninue 的命令时候就会去将蓝牙模块切换到工作状态。如果接收到了 turn_cold 的命令时候,设备就会断电进入 poweroff 状态。

  • Switching

这也是一个中间状态,需要继续接收命令。

  • Bluetoothon

这时蓝牙模块出于正常工作的状态。

根据 android 中蓝牙状态的源码中,具体的各个状态机相互转换图如下:

 

 

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class PowerOff extends State {

public void enter() {

   if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);

   } 

……

case USER_TURN_ON:

broadcastState(BluetoothAdapter.STATE_TURNING_ON);

 transitionTo(mWarmUp);

……

if (prepareBluetooth()) {

if ((Boolean) message.obj) {

   persistSwitchSetting(true);

}

deferMessage(obtainMessage(TURN_ON_CONTINUE));

蓝牙状态机初始化时 PowerOff 的,从上面的 BluetoothService 的 enable 函数中 USER_TURN_ON 命令。从上面代码中可以看出蓝牙状态机在接收到 USER_TURN_ON 后,首先就像蓝牙适配器广播蓝牙正处于 STATE_TRUNING_ON 的状态,蓝牙的适配器的蓝牙状态有四个:

分别是,state_off (10),state_turning_on (11),state_on (12),state_turning_off (14)。由于我们刚开机所以蓝牙适配器的状态必然是从 10->11。然后将蓝牙状态机的状态切换到 mWaremUp 状态。

接下来调用了 prepareBluetooth () 方法。接下来看看 prepareBluetooth 方法。代码如下:

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private boolean prepareBluetooth() {

  if (mBluetoothService.enableNative() != 0) {

          return false;

   }

……

int retryCount = 2;

boolean eventLoopStarted = false;

while ((retryCount-- > 0) && !eventLoopStarted) {

mEventLoop.start();

while ((pollCount-- > 0) && !eventLoopStarted) {

         if (mEventLoop.isEventLoopRunning()) {

                 eventLoopStarted = true;

                     break;

      }

在 preprareBluetooth 方法中,首先就是调用了 BluetoothService 的 enableNative () 的方法,只要一看到这种带 Native 的方法,JNI 的代码是少不了的。由于 enableNative 方法走的路有点多,所以先直接到 BluetoothService 的代码中寻找 enableNative () 看个究竟。

framework/base/core/java/android/server/BluetoothService.java

/*package*/ native int enableNative();

Framework/base/core/jni/ android_server_BluetoothService.cpp

 

static JNINativeMethod sMethods[] = {

……

{"enableNative", "()I", (void *)enableNative},

……

}

static jint enableNative(JNIEnv *env, jobject object) {

return bt_enable();

}

从上面的代码可以看出,BluetoothService 的 enableNative 就是直接调用了 JNI 的代码,JNI 是 java native interface 的 缩写,中文叫 java 本地接口。Android 上层跑的 java 代码,而底层代码都是 c 语言。以 android 的一贯作风是通过 JNI 代码调用 HAL 层,然后就可以直接调用驱动代码或者经由内核达到操作驱动代码。enableNative 的代码很简单,就是调用了 bt_enable。我们可以继续找到这个函数的实现。

System/bluetooth/bluedroid/bluetooth.c

int bt_enable() {

……

if (set_bluetooth_power(1) < 0) goto out

……

if (property_set("ctl.start", "hciattach") < 0)

……

for (attempt = 1000; attempt > 0;  attempt--) {

hci_sock = create_hci_sock();

……

ret = ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID);

……

}

Set_bluetooth_power () 函数会根据蓝牙的硬件开关,也就是 hci 设备注册的时候会同时在 linux 内核中注册一个 rfkill 类,比如我们在电脑键盘上面可能会看见一个按键来开关蓝牙或者 wifi 之类的。这里会去读这个键值,如果是 1 代表可以开启蓝牙的,否则是没法使用蓝牙的,在开发过程中如果没这样的按键,可将这行代码拿掉。Propery_set (“ctl.start”,hciattach)。这个函数会去启动 hciattach 服务,具体这个服务是以二进制文件存储在系统 system/bin 目录下面的。

我们可以从 andriod 启动脚本文件找到名字叫 hciattach 服务。当然这个是针对接串口的蓝牙来说需要启动服务,如果我们的设备是通过 USB 总线接入系统的话,其实这个服务也是可以不启动的。剩下的代码是一个 for 循环,先建立一个 bluetooth 的套接字,然后通过 ioctl 来和 bluez 的代码来打开蓝牙设备,可以重试 1000 次。接下来的代码就要跑到内核的 BlueZ 了。

Kernel/net/bluetooth/hci_sock.c

static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){

……

case HCIDEVUP:

                if (!capable(CAP_NET_ADMIN))

                        return -EACCES;

                return hci_dev_open(arg);

……

}

从上面的 ioctl 下来的代码可以看出,当函数第二个参数 cmd 为 HCIDEVUP 时,就会直接调用 hci_dev_open (arg) 方法。这个函数就好比我们在终端下面使用蓝牙调试工具 hciconfig, 执行了

#hciconfig hci0 up。

Kernel/net/bluetooth/hci_core.c

int hci_dev_open(__u16 dev)

{

……

hdev = hci_dev_get(dev);

……

if (hdev->open(hdev)) {

                ret = -EIO;

                goto done;

  }

}

在 kernel 的 bluez 调用 hci_dev_open,而在这个函数中又 hdev->open (hdev), 这个就是我们驱动注册时候的回调函数 open。由于我们平台使用的是 usb 的蓝牙接入方式,我就以 usb 的蓝牙驱动为例,看看驱动的 open 函数。

Kernel/driver/bluetooth/btusb.c

static int btusb_open(struct hci_dev *hdev){

……

err = btusb_submit_intr_urb(hdev, GFP_KERNEL);

……

err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);

……

}

USB hci 设备打开后,首先将设备的 interface 配置为 HCI_RUNNING 状态,然后为数据传输初始化设备的端点和管道,初始化和填充 urb。代码到这,蓝牙设备就算是真正打开了。

回到之前的蓝牙状态机的代码:

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private boolean prepareBluetooth() {

  if (mBluetoothService.enableNative() != 0) {

          return false;

   }

……

int retryCount = 2;

boolean eventLoopStarted = false;

while ((retryCount-- > 0) && !eventLoopStarted) {

mEventLoop.start();

while ((pollCount-- > 0) && !eventLoopStarted) {

         if (mEventLoop.isEventLoopRunning()) {

                 eventLoopStarted = true;

                     break;

      }

mBluetoothService 在 enableNative () 函数主要功能就是通过一系列代码来打开蓝牙设备。如果设备驱动代码没有问题的话,我们 enableNative () 返回的将会是 true。在实际调试蓝牙设备时候,我们可以通过在 linux 或者 android 的终端下面使用自带的工具命令 (hciconfig), 执行:

# hciconfig –a 如果驱动能够和设备绑定的话,我们就会看到蓝牙设备的一些比较重要信息,如:蓝牙的物理地址,总线类型,协议类型等。

上面的代码接下来会是一个 while 循环,执行 2 次。mEventLoop.start ()。也就是说调用了 EventLoop 的 start 方法。

/framework/base/core/java/android/server/BluetoothEventLoop.java

/* package */ void start() {

   if (!isEventLoopRunningNative()) {

            if (DBG) log("Starting Event Loop thread");

            startEventLoopNative();

        }   

}

第一次进入这个函数 isEventLoopRunningNative 肯定是返回 false 的,所以直接进入了 startEventLoopNative (),前面说过了一般带 native 的函数结尾的函数都是 JNI。看到这里又要进 JNI 了。

Framework/base/core/jni/android_server_BluetoothEventLoop.cpp

static JNINativeMethod sMethods[] = {

……

{"startEventLoopNative", "()V", (void *)startEventLoopNative},

……

}

static jboolean startEventLoopNative(JNIEnv *env, jobject object) {

……

nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *

            DEFAULT_INITIAL_POLLFD_COUNT);

……

nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *

            DEFAULT_INITIAL_POLLFD_COUNT);

……

if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {

        LOGE("Error getting BT control socket");

        goto done;

}

……

if (setUpEventLoop(nat) != JNI_TRUE) {

        LOGE("failure setting up Event Loop!");

        goto done;

 }

pthread_create(&(nat->thread), NULL, eventLoopMain, nat);

……

}

 

为 socket 文件描述符分配内存数据,同时为 DBusWatch 结构体分配内存,socketpair 创建了一对套接字 (AF_LOCAL 域中使用),这个描述符可以是单双工也可以是全双工的,这里是单双工的,也就是只能从这个描述符中读取数据,而不能写数据。如果 socketpair 的第四个参数是个数组,也可以实现一个描述符读,另外一个描述符写。从而实现全双工。然后就是 setUpEventLoop 函数,最后就是创建了 eventLoopMain 的线程。

Framework/base/core/jni/android_server_BluetoothEventLoop.cpp

static jboolean setUpEventLoop(native_data_t *nat) {

……

dbus_threads_init_default();

……

dbus_error_init(&err);

……

if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){

            return JNI_FALSE;

        }

……

dbus_bus_add_match(nat->conn,

                "type=''signal'',interface=''org.freedesktop.DBus''",   &err);

dbus_bus_add_match(nat->conn,

   "type=''signal'',interface=''"BLUEZ_DBUS_BASE_IFC".Adapter''",&err);

……

}

 

 

这里是初始化 dbus,是 bluez 能够挂接上 dbus。建立一个 dbus 连接之后,为这个 dbus 连接起名,为我们将要进行的消息循环添加匹配条件 (就是通过信号名和信号接口名来进行匹配控制的) -- dbus_bus_add_match ()。我们进入等待循环后,只需要对信号名,信号接口名进行判断就可以分别处理各种信号了。在各个处理分支上。我们可以分离出消息中的参数。对参数类型进行判断和其他的处理。具体对 dbus 感兴趣的话可以参照:http://dbus.freedesktop.org。

Framework/base/core/jni/android_server_BluetoothEventLoop.cpp

static void *eventLoopMain(void *ptr) {

……

while (1) {

……

if (nat->pollData[i].fd == nat->controlFdR) {

while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT) != -1) {

……

switch (data) {

case EVENT_LOOP_EXIT:

   dbus_connection_set_watch_functions(nat->conn,NULL, NULL, NULL, NULL, NULL);

   tearDownEventLoop(nat);

   nat->vm->DetachCurrentThread();

……

case EVENT_LOOP_ADD:

    {

     handleWatchAdd(nat);

         break;

     }

case EVENT_LOOP_REMOVE:

     {

      handleWatchRemove(nat);

          break;

      }

 

以轮训的方式从 socket 的描述符中不断的接收数据,如果有数据到来,就根据数据的类型做相应的处理。到这里 dbus 就和 bluez 建立连接。还是回到之前我们 prepareBluetooth 的函数。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class PowerOff extends State {

public void enter() {

   if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);

   } 

……

case USER_TURN_ON:

broadcastState(BluetoothAdapter.STATE_TURNING_ON);

 transitionTo(mWarmUp);

……

if (prepareBluetooth()) {

if ((Boolean) message.obj) {

   persistSwitchSetting(true);

}

deferMessage(obtainMessage(TURN_ON_CONTINUE));

前面的代码我们分析完了 prepareBluetooth (),如果没有问题就进入了 persistSwitchSetting ()。

然后就是讲蓝牙状态机切换到 mWarnUp 状态。并向蓝牙状态机发送了一个 TURN_ON_CONTINUE 的命令。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class WarmUp extends State {

……

public boolean processMessage(Message message) {

   log("WarmUp process message: " + message.what);

……

switch(message.what) {

case TURN_ON_CONTINUE:

 

这个命令在 WarmUp 状态里面什么也没做。直接通过 deferMessage () 到 HotOff 状态里面重新发送了 TURN_ON_CONTINUE 的命令。那我们 HotOff 状态机。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

private class HotOff extends State {

……

public boolean processMessage(Message message) {

    log("HotOff process message: " + message.what);

……

switch(message.what) {

case TURN_ON_CONTINUE:

        int retryCount = 5

……

mBluetoothService.switchConnectable(true);

transitionTo(mSwitching);

……

}

 

在 HotOff 状态机中,接收到 TURN_ON_CONTINUE 命令后,先调用了 BluetoothService 的 switchConnectable (true); 然后将蓝牙的状态机切换到 Switching 状态。

/framework/base/core/java/android/server/BluetoothService.java

/*package*/ synchronized void switchConnectable(boolean on) {

        setAdapterPropertyBooleanNative("Powered", on ? 1 : 0);

    }

又到了以 Native 结尾的函数,还是到 JNI 里面找到它的实现吧。

Framework/base/core/jni/android_server_BluetoothService.cpp

static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key,

                                         void *value, jint type) {

……

msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,get_adapter_path(env, object),

DBUS_ADAPTER_IFACE, "SetProperty");

……

dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);

dbus_message_iter_init_append(msg, &iter);

……

reply = dbus_connection_send_with_reply(nat->conn, msg, NULL, -1);

……

}

 

通过 Dbus 向 bluez 发送 SetPropery 的信息 (message),当成功的时候,我们在另外一端就会收到 powerChanged 的信号。具体处理如下的代码:

Framework/base/core/java/android/server/BluetoothEventLoop.java

/*package*/ void onPropertyChanged(String[] propValues) {

……

BluetoothAdapterProperties adapterProperties = mBluetoothService.getAdapterProperties();

……

else if (name.equals("Pairable") || name.equals("Discoverable")) {

 adapterProperties.setProperty(name, propValues[1]);

if (name.equals("Discoverable")) {

          mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SCAN_MODE_CHANGED);

            } 

……

else if (name.equals("Powered")) {

          mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,

                propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));

 

当有蓝牙 AdapterProperies 发生变化时,在 BluetoothEventLoop.java 中就会有个 onProperyCha

nged 方法来处理。首先通过 BluetoothService 的 getAdapterProperties 来获取蓝牙适配器的所有属性,都有哪些属性,在实际开发过程中我们通过调试可以看到按顺序依次是:power,

Pairable,class,device,UUID,Discoverable。通过将 power 的 value 设置为 true,就会向蓝牙状态发送一个 POWER_STATE_CHAGED,通过 Discoverable 的属性来向蓝牙状态机发送一个

SCAN_MODE_CHANGED 的命令。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

case POWER_STATE_CHANGED:

               removeMessages(POWER_DOWN_TIMEOUT);

                    if (!((Boolean) message.obj)) {

                        if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) {

                            transitionTo(mHotOff);

                            finishSwitchingOff();

                            if (!mContext.getResources().getBoolean

                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {

                                deferMessage(obtainMessage(TURN_COLD));

                            }  

                        }  

                    } else {

                        if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) {

                            if (mContext.getResources().getBoolean

                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {

                                recoverStateMachine(TURN_HOT, null);

                            } else {

                                recoverStateMachine(TURN_COLD, null);

                            }  

                        }  

                    }  

在上一个 HotOff 的时候,已经将蓝牙状态机切换到了 Switching 了。所以直接在 Switching 这个状态里面来处理命令。第一个 power_state_changed 的命令很简单。在蓝牙状态机里面有个叫 mPublicState 的全局变量来记录蓝牙适配器的状态。如果是 power 的值为 true,那么就将这个变量的值变为 STATE_TURNING_ON,否则就是 STATE_TURNING_OFF。在前面介绍过了蓝牙适配器总共有四个状态:State_off (10),state_turning_on (11),state_on (12),state_turning_off (

13)。那么继续来看第二个命令,scan_mode_changed。

/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java

case SCAN_MODE_CHANGED:

     if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) {

        mBluetoothService.setPairable();

        mBluetoothService.initBluetoothAfterTurningOn();

        transitionTo(mBluetoothOn);

        broadcastState(BluetoothAdapter.STATE_ON);

mBluetoothService.runBluetooth();

}

根据第一个命令,mPublicState 的值是 STATE_TURNING_ON,这里又要和 BluetoothService 来交互了,先调用了 setPairable 和 initBluetoothAfterTurningOn,runBluetooth 并将蓝牙状态机切换到 BluetoothOn 的状态。接下来到 bluetoothService 看这个 setPairable 方法。

/framework/base/core/java/android/server/BluetoothService.java

 

/*package*/ synchronized void setPairable() {

        String pairableString = getProperty("Pairable", false);

        if (pairableString == null) {

            Log.e(TAG, "null pairableString");

            return;

        }   

        if (pairableString.equals("false")) {

            setAdapterPropertyBooleanNative("Pairable", 1);

        }   

    }   

这个过程和上面的设置 POWER 的过程是类似的,先通过 getPropery 获取 Pairable 的状态,如果是 false 的话,就需要调用 JNI 的方法 setAdapterPropertyBooleanNative 来通过 dbus 来向 bluez 来设置蓝牙适配器的 Pairable 的值。如果设置成功的话,同样还会调用 BluetoothEventLoop 中的 onProperyChanged 方法。继续跟进代码 initBluetoothAfterTurningOn:

framework/base/core/java/android/server/BluetoothService.java

/*package*/ void initBluetoothAfterTurningOn() {

String discoverable = getProperty("Discoverable", false);

        String timeout = getProperty("DiscoverableTimeout", false);

        if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) {

            setAdapterPropertyBooleanNative("Discoverable", 0);

        }

mBondState.initBondState();

        initProfileState();

        getProfileProxy();

}

这个函数首先还是像设置 power,Parirable 的属性差不多,设置 Discoverable 的属性。当蓝牙模块打开和蓝牙适配器配对 (Pairable) 之后。剩下的 initProfileState 可以获取蓝牙的物理地址。

getProfileProxy 直接调用了 Adapter 的 getProfileProxy。得到俄 ProfileProxy 可以是 HEADSET,

A2DP,INPUT_DEVICE,PAN,HEALTH。

framework/base/core/java/android/server/BluetoothService.java

/*package*/ void runBluetooth() {

……

autoConnect();

}

private void autoConnect() {

String[] bonds = getKnownDevices();

        if (bonds == null) {

            return;

        }

……

for (String path : bonds) {

            String address = getAddressFromObjectPath(path);

            BluetoothDeviceProfileState state = mDeviceProfileState.get(address);

            if (state != null) {

                Message msg = new Message();

                msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;

                state.sendMessage(msg);

            }

        }

    }

 

在 autoConnect 中,就会扫描附近的设备,并获取设备的地址和名字。这是我们看到就是能看到了一系列扫描出来的附近的设备。此时蓝牙的状态出于正常运行。到这里蓝牙模块就在

Android 中工作起来了。

1.4 蓝牙开发在 android 中的调试

1.4.1 内核和驱动的支持

作为是 linux 内核的 Android 系统,必须在编译内核过程中将 bluez 编译的 config 选上。

CONFIG_BT =y

CONFIG_BT_RFCOMM =y

CONFIG_BT_BNEP = y

CONFIG_BT_CMTP =y

CONFIG_BT_L2CAP=y

CONFIG_BT_SCO=y

然后根据我们的驱动使用的接入方式,常见的有串口 (uart),USB,SDIO 总线等。如果我们的驱动能够正常工作工作的话,我们在 linux 的终端通过下面命令就可以看见 hci 设备了。

root@android :/ # hciconfig -a                                                 

hci0:   Type: BR/EDR  Bus: USB

        BD Address: 74:2F:68:CE:13:57  ACL MTU: 1022:8  SCO MTU: 183:5

        DOWN

        RX bytes:505 acl:0 sco:0 events:22 errors:0

        TX bytes:99 acl:0 sco:0 commands:22 errors:0

        Features: 0xff 0xfe 0x0d 0xfe 0xd8 0x7f 0x7b 0x87

        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3

        Link policy: RSWITCH HOLD SNIFF

        Link mode: SLAVE ACCEPT

如果能够像上面可以看见蓝牙的类型,总线类型,物理地址等信息。说明蓝牙设备已经在内核中注册成功了,但能不能使用还要继续使用下面命令,我们注意到蓝牙模块状态时 DOWN 的。

root@android :/ # hciconfig hci0 up

root@android :/ # hciconfig -a                                                 

hci0:   Type: BR/EDR  Bus: USB

        BD Address: 74:2F:68:CE:13:57  ACL MTU: 1022:8  SCO MTU: 183:5

        UP RUNNING

        RX bytes:994 acl:0 sco:0 events:42 errors:0

        TX bytes:185 acl:0 sco:0 commands:42 errors:0                          

        Features: 0xff 0xfe 0x0d 0xfe 0xd8 0x7f 0x7b 0x87                      

        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3                       

        Link policy: RSWITCH HOLD SNIFF                                        

        Link mode: SLAVE ACCEPT                                                

        Name: ''Bluetooth USB Host Controller''                                  

        Class: 0x000000                                                         

        Service Classes: Unspecified                                           

        Device Class: Miscellaneous,                                           

        HCI Version: 4.0 (0x6)  Revision: 0x102                                

        LMP Version: 4.0 (0x6)  Subversion: 0x1                                

        Manufacturer: Atheros Communications, Inc. (69)

通过 hciconfig hci0 up 命令让蓝牙模块的状态从 DOWN 变成 UP 状态。这个时候还不能就确定蓝牙驱动是能正常工作的。需要继续看看我们的蓝牙能不能扫描其他的蓝牙设备,如果能够扫描到其他的设备,就可以说明我们的蓝牙设备在内核态是可以正常工作的。

root@android :/ # hcitool scan                                                 

Scanning ...

        00:1C:26:D5:3E:D6       DAWEIYAN-MOBL

        00:1E:4C:F3:BC:FA       ZHILONGX-MOBL

        00:1F:3A:F1:94:CD       JUELIUX-MOBL

        00:27:13:D6:66:D9       QWANG29-MOBL2

        50:63:13:C7:83:6D       YANCHAOY-MOBL

        00:1C:26:FD:11:3A       ZWANG16X-MOBL

好了如果能够扫描出设备的物理地址和名字的话,那我们的设备在 linux 内核态就 ok 了。

1.4.2 Android Boradconfig 和服务的支持

在 BoardConfig.mk 中添加:BOARD_HAVE_BLUETOOTH := true。因为在 framework 中的代码很多函数是需要这个宏的,如果这个宏没有打开的话,很多代码是走不过的。在 android 的添加蓝牙工作必要的服务,Dbus-daemon,bluetoothd,

/init.rc

service dbus /system/bin/dbus-daemon --system --nofork

    class main                       

    socket dbus stream 660 bluetooth bluetooth         

    user bluetooth                              

group bluetooth net_bt_admin  

service bluetoothd /system/bin/bluetoothd -n

    class main                                   

    socket bluetooth stream 660 bluetooth bluetooth                  

socket dbus_bluetooth stream 660 bluetooth Bluetooth

service  hciattach /system/bin/hciattach  当然这个服务只针对你的蓝牙接入方式是串口的,向我们这里是 USB 的话,这个服务还是可以省掉的。

service hfag /system/bin/sdptool add --channel=10 HFAG

    user bluetooth

    group bluetooth net_bt_admin

    disabled

oneshot

service hsag /system/bin/sdptool add --channel=11 HSAG

    user bluetooth

    group bluetooth net_bt_admin

    disabled

oneshot

service opush /system/bin/sdptool add --channel=12 OPUSH

    user bluetooth

    group bluetooth net_bt_admin

    disabled

oneshot

service pbap /system/bin/sdptool add --channel=19 PBAP

    user bluetooth

    group bluetooth net_bt_admin

    disabled

    oneshot

好了,到这里蓝牙应该就能正常工作了。上面的代码都是基于 android 4.0。

Android Geofence禁用/重新启动位置服务时自动删除

Android Geofence禁用/重新启动位置服务时自动删除

我已经使用Flag NEVER_EXPIRE成功添加了地理围栏.一切似乎都运转正常.

但现在在测试时我发现如果我停止位置服务地理围栏停止工作为EXPECTED.此外,当我再次启动位置服务时,我之前添加的地理围栏应该再次开始工作,但不会生成任何通知,并且一旦禁用位置服务,地理围栏似乎会自动删除.我必须再次设置所有位置以恢复工作状态下的地理围栏.

任何建议或任何想法为什么它这样做?

编辑::

当设备关闭/重新启动等时也会出现类似的问题.因此,如果禁用位置服务/重新启动设备,基本上所有已注册的地理围栏都将过期.其中很少我尝试通过Session处理,但我正在寻找一种解决方案,通过该解决方案,我们可以在启用位置服务时将Geofences设置回来.

解决方法:

为了在后台观看,我遇到了同样的问题,并且能够通过将示例代码从IntentService更改为broadcastReceiver来解决它.所有细节都可以在我的帖子中找到:

Android Geofence eventually stop getting transition intents

这就是我所说的(如果有人懒得跟随链接):

因此,在稍微讨论一下后,看起来示例代码中定义的ReceiveTransitionsIntentService将在应用程序不在时停止获取通知.我认为这是示例代码的一个大问题……似乎这样会让像我一样的人绊倒.

所以我使用了广播接收器,到目前为止它似乎正在我的测试中工作.

将其添加到清单:

<receiver android:name="com.aol.android.geofence.GeofenceReceiver"
        android:exported="false">
        <intent-filter >
            <action android:name="com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE"/>
        </intent-filter>
    </receiver>

然后在GeofenceRequester类中,您需要更改createRequestPendingIntent方法,以便它转到broadcastReceiver而不是ReceiveTransitionsIntentService

private PendingIntent createRequestPendingIntent() {

        // If the PendingIntent already exists
        if (null != mGeofencePendingIntent) {

            // Return the existing intent
            return mGeofencePendingIntent;

        // If no PendingIntent exists
        } else {

            // Create an Intent pointing to the IntentService
            Intent intent = new Intent("com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE");
//            Intent intent = new Intent(context, ReceiveTransitionsIntentService.class);
            /*
             * Return a PendingIntent to start the IntentService.
             * Always create a PendingIntent sent to Location Services
             * with FLAG_UPDATE_CURRENT, so that sending the PendingIntent
             * again updates the original. Otherwise, Location Services
             * can't match the PendingIntent to requests made with it.
             */
            return PendingIntent.getbroadcast(
                    context,
                    0,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
        }
    }

然后我添加了看起来像这样的GeofenceReceiver类:

public class GeofenceReceiver extends broadcastReceiver {
    Context context;

    Intent broadcastIntent = new Intent();

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;

        broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);

        if (LocationClient.hasError(intent)) {
            handleError(intent);
        } else {
            handleEnterExit(intent);
        }
    }

    private void handleError(Intent intent){
        // Get the error code
        int errorCode = LocationClient.getErrorCode(intent);

        // Get the error message
        String errorMessage = LocationServiceErrorMessages.getErrorString(
                context, errorCode);

        // Log the error
        Log.e(GeofenceUtils.APPTAG,
                context.getString(R.string.geofence_transition_error_detail,
                        errorMessage));

        // Set the action and error message for the broadcast intent
        broadcastIntent
                .setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)
                .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage);

        // broadcast the error *locally* to other components in this app
        LocalbroadcastManager.getInstance(context).sendbroadcast(
                broadcastIntent);
    }


    private void handleEnterExit(Intent intent) {
        // Get the type of transition (entry or exit)
        int transition = LocationClient.getGeofenceTransition(intent);

        // Test that a valid transition was reported
        if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)
                || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {

            // Post a notification
            List<Geofence> geofences = LocationClient
                    .getTriggeringGeofences(intent);
            String[] geofenceIds = new String[geofences.size()];
            String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER,
                    geofenceIds);
            String transitionType = GeofenceUtils
                    .getTransitionString(transition);

            for (int index = 0; index < geofences.size(); index++) {
                Geofence geofence = geofences.get(index);
                // ...do something with the geofence entry or exit. I'm saving them to a local sqlite db

            }
            // Create an Intent to broadcast to the app
            broadcastIntent
                    .setAction(GeofenceUtils.ACTION_GEOFENCE_TRANSITION)
                    .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
                    .putExtra(GeofenceUtils.EXTRA_GEOFENCE_ID, geofenceIds)
                    .putExtra(GeofenceUtils.EXTRA_GEOFENCE_TRANSITION_TYPE,
                            transitionType);

            LocalbroadcastManager.getInstance(MyApplication.getContext())
                    .sendbroadcast(broadcastIntent);

            // Log the transition type and a message
            Log.d(GeofenceUtils.APPTAG, transitionType + ": " + ids);
            Log.d(GeofenceUtils.APPTAG,
                    context.getString(R.string.geofence_transition_notification_text));

            // In debug mode, log the result
            Log.d(GeofenceUtils.APPTAG, "transition");

            // An invalid transition was reported
        } else {
            // Always log as an error
            Log.e(GeofenceUtils.APPTAG,
                    context.getString(R.string.geofence_transition_invalid_type,
                            transition));
        }
    }

    /**
     * Posts a notification in the notification bar when a transition is
     * detected. If the user clicks the notification, control goes to the main
     * Activity.
     * 
     * @param transitionType
     *            The type of transition that occurred.
     * 
     */
    private void sendNotification(String transitionType, String locationName) {

        // Create an explicit content Intent that starts the main Activity
        Intent notificationIntent = new Intent(context, MainActivity.class);

        // Construct a task stack
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);

        // Adds the main Activity to the task stack as the parent
        stackBuilder.addParentStack(MainActivity.class);

        // Push the content Intent onto the stack
        stackBuilder.addNextIntent(notificationIntent);

        // Get a PendingIntent containing the entire back stack
        PendingIntent notificationPendingIntent = stackBuilder
                .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        // Get a notification builder that's compatible with platform versions
        // >= 4
        NotificationCompat.Builder builder = new NotificationCompat.Builder(
                context);

        // Set the notification contents
        builder.setSmallIcon(R.drawable.ic_notification)
                .setContentTitle(transitionType + ": " + locationName)
                .setContentText(
                        context.getString(R.string.geofence_transition_notification_text))
                .setContentIntent(notificationPendingIntent);

        // Get an instance of the Notification manager
        notificationmanager mnotificationmanager = (notificationmanager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);

        // Issue the notification
        mnotificationmanager.notify(0, builder.build());
    }
}

希望这有助于其他人.

Android – Android 1.6中开发的应用程序可以在Android 2.0中运行吗?

Android – Android 1.6中开发的应用程序可以在Android 2.0中运行吗?

Android是否向后兼容,换句话说,Android 1.6中开发的应用程序可以在Android 2.0中运行吗?

我刚刚发布了一个2.0应用程序,发现我使用我的G1(Android 1.6)手机无法在市场上找到它.但显然其他人正在下载它.

我应该将应用程序降级到1.6还是需要为每个Android版本创建单独的应用程序?

解决方法:

既然您已经使用1.6 SDK开发了应用程序,那么您应该(必须?)用1.6标记它.
这将增加能够查看和下载您的应用程序的人数.

如果它们是相同的,则不需要有2个应用程序(1.6和2.0).如果您愿意,可以尝试使用Android SDK 2.0的新功能来改进您的应用,然后:

>更新旧的应用程序,如果你只想瞄准Android 2.0(或如果你正在执行2个不同的代码关于手机上的Android版本)
>在市场上创建一个新的应用程序,以瞄准Android 2.0

android – 位置服务GPS Force关闭

android – 位置服务GPS Force关闭

嗨,我开发一个应用程序,以找到最佳位置,并将其发送到一个数字.它的网络位置工作得很好,但是当我想要定位GPS或Criteria类时,它就会被强制关闭!
它如此友好地帮助我tnx.

package ir.M410.toolkit;

import android.app.Service;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.telephony.SmsManager;
import android.util.Log;



public class LocationFinder extends Service implements LocationListener{

double lat ,lon ;
@Override
public IBinder onBind(Intent arg0) {
    return null;
       }

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // We want this service to continue running until it is explicitly
    // stopped, so return sticky.


    LocationManager mlocationManager = (LocationManager)        getSystemService(LOCATION_SERVICE);
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
    String locationprovider = mlocationManager.getBestProvider(criteria, true);
    Location mlocation = mlocationManager.getLastKNownLocation(locationprovider);


      lat = mlocation.getLatitude ();
      lon = mlocation.getLongitude ();

      Log.i("Geo_Location", "Latitude: " + lat + ", Longitude: " + lon);

      SmsManager sms = SmsManager.getDefault();
        sms.sendTextMessage("+11231233213", null,"https://maps.google.com/maps?q="+lat+","+lon, null, null);



        //  SmsManager sms = SmsManager.getDefault();
    //sms.sendTextMessage(MainActivity.senderNum, null,"  "+"lat:"+lat+"  "+"lon:"+lon, null, null);
 //stopSelf();
          return START_NOT_STICKY;
}

@Override
public void onLocationChanged(Location location) {
    // Todo Auto-generated method stub

     lat= location.getLatitude();
    lon =location.getLongitude();

    Log.i("Geo_Location", "Latitude: " + lat + ", Longitude: " + lon);
  //  stopSelf();
}

@Override
public void onProviderdisabled(String provider) {
    // Todo Auto-generated method stub

}

@Override
public void onProviderEnabled(String provider) {
    // Todo Auto-generated method stub

}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
    // Todo Auto-generated method stub

}


@Override
public void onDestroy() {
    super.onDestroy();

}
}

这是我的表现:

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

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

<uses-permission android:name="android.permission.RECEIVE_SMS" >
</uses-permission>
<uses-permission android:name="android.permission.READ_SMS" >
</uses-permission>
<uses-permission android:name="android.permission.SEND_SMS" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" >
</uses-permission>
<uses-permission android:name="android.permission.CALL_PHONE" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <receiver android:name="ir.M410.toolkit.broadcast_Receiver" >
        <intent-filter android:priority="2147483647" >
            <action android:name="android.intent.action.PHONE_STATE" />
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            <action android:name="ir.M410.toolkit.android.action.broadcast" />
        </intent-filter>
    </receiver>

    <service android:name="ir.M410.toolkit.LocationFinder" />

    <activity
        android:name="ir.M410.toolkit.PasswordCheck"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name="ir.M410.toolkit.MainActivity"
        android:label="@string/title_activity_main"
        android:screenorientation="portrait" >
    </activity>
    <activity
        android:name="ir.M410.toolkit.Teturial"
        android:label="@string/title_activity_teturial" >
    </activity>
    <activity
        android:name="ir.M410.toolkit.CalldivertActivity"
        android:label="@string/title_activity_call_divert" >
    </activity>
</application>

</manifest>

[编辑],这里是LogCat定义:

10-14 19:58:37.823: E/AndroidRuntime(2685): FATAL EXCEPTION: main
10-14 19:58:37.823: E/AndroidRuntime(2685): java.lang.RuntimeException: Unable to start service ir.M410.toolkit.LocationFinder@4482e4a8 with Intent { cmp=ir.M410.toolkit/.LocationFinder }: java.lang.NullPointerException
10-14 19:58:37.823: E/AndroidRuntime(2685):     at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3260)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at android.app.ActivityThread.access$3600(ActivityThread.java:135)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2205)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at android.os.Looper.loop(Looper.java:143)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at android.app.ActivityThread.main(ActivityThread.java:4914)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at java.lang.reflect.Method.invokeNative(Native Method)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at java.lang.reflect.Method.invoke(Method.java:521)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at dalvik.system.NativeStart.main(Native Method)
10-14 19:58:37.823: E/AndroidRuntime(2685): Caused by: java.lang.NullPointerException
10-14 19:58:37.823: E/AndroidRuntime(2685):     at ir.M410.toolkit.LocationFinder.onStartCommand(LocationFinder.java:38)
10-14 19:58:37.823: E/AndroidRuntime(2685):     at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3246)
10-14 19:58:37.823: E/AndroidRuntime(2685):     ... 10 more

[编辑]代码检查不为空:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // We want this service to continue running until it is explicitly
    // stopped, so return sticky.


    LocationManager mlocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
    String locationprovider = mlocationManager.getBestProvider(criteria, true);
    if(mlocationManager.getLastKNownLocation(locationprovider)!=null){
    Location mlocation = mlocationManager.getLastKNownLocation(locationprovider);


      lat = mlocation.getLatitude ();
      lon = mlocation.getLongitude ();

      Log.i("Geo_Location", "Latitude: " + lat + ", Longitude: " + lon);

      SmsManager sms = SmsManager.getDefault();
        sms.sendTextMessage("+11231233213", null,"https://maps.google.com/maps?q="+lat+","+lon, null, null);

    }

它的确定并没有得到强制关闭但没有收到数据它总是为空!

解决方法:

我从http://www.androidhive.info/2012/07/android-gps-location-manager-tutorial/开始实施自己的GPSTracker类

使用此功能,您可以打开GPS并在有效位置可用时立即接收回叫.这可能需要一段时间,具体取决于设备的位置,但会提供更精确和可靠的位置.

通过我的实现,您可以执行以下操作:

private GPSTracker gps;
private FirstFixListener firstFixListener;
private LocationUpdateListener locationUpdateListener;

private void sendGPStoSMS() {
    gps = GPSTracker.getInstance(context);
    firstFixListener = new MyFirstFixListener();
    locationUpdateListener = new MyLocationUpdateListener();
    gps.startUsingGPS(firstFixListener, locationUpdateListener);

}

private class MyFirstFixListener implements FirstFixListener {

    @Override
    public void onFirsFixChanged(boolean hasGPSfix) {
        if (hasGPSfix == true) {
            Location position = gps.getLocation();
            // send SMS with position

            // stop the gps and unregister callbacks
            gps.stopUsingGPS(firstFixListener, locationUpdateListener);
        }

    }

}

private class MyLocationUpdateListener implements LocationUpdateListener {

    @Override
    public void onLocationChanged(Location location) {
        // hand you each new location from the GPS
        // you do not need this as you only want to send a single position

    }

}

这是我对GPSTracker的实现:

public class GPSTracker extends Service implements LocationListener {

private static final String TAG = "GPSTracker";

/**
 * Register to receive callback on first fix status
 * 
 * @author Morten
 * 
 */
public interface FirstFixListener {

    /**
     * Is called whenever gps register a change in first-fix availability
     * This is valuable to prevent sending invalid locations to the server.
     * 
     * @param hasGPSfix
     */
    public void onFirsFixChanged(boolean hasGPSfix);
}

/**
 * Register to receive all location updates
 * 
 * @author Morten
 * 
 */
public interface LocationUpdateListener {
    /**
     * Is called every single time the GPS unit register a new location
     * The location param will never be null, however, it can be outdated if hasGPSfix is not true.
     *  
     * @param location
     */
    public void onLocationChanged(Location location);
}

private Context mContext;

// flag for GPS status
private List<FirstFixListener> firstFixListeners;
private List<LocationUpdateListener> locationUpdateListeners;
boolean isGPSFix = false;
boolean isGPSEnabled = false;
private GPSFixListener gpsListener;

// flag for GPS status
boolean canGetLocation = false;

Location location; // location
double latitude; // latitude
double longitude; // longitude
long mLastLocationMillis;

private boolean logLocationChanges;

// Declaring a Location Manager
protected LocationManager locationManager;

/** removed again as we need multiple instances with different callbacks **/
private static GPSTracker instance;

public static GPSTracker getInstance(Context context) {
    if (instance != null) {
        return instance;
    }
    return instance = new GPSTracker(context);
}

private GPSTracker(Context context) {
    this.mContext = context;
    gpsListener = new GPSFixListener();
    firstFixListeners = new ArrayList<GPSTracker.FirstFixListener>();
    locationUpdateListeners = new ArrayList<GPSTracker.LocationUpdateListener>();
}

public boolean hasGPSFirstFix() {
    return isGPSFix;
}

private void addFirstFixListener(FirstFixListener firstFixListener) {
    this.firstFixListeners.add(firstFixListener);
}

private void addLocationUpdateListener(
        LocationUpdateListener locationUpdateListener) {
    this.locationUpdateListeners.add(locationUpdateListener);
}

private void removeFirstFixListener(FirstFixListener firstFixListener) {
    this.firstFixListeners.remove(firstFixListener);
}

private void removeLocationUpdateListener(
        LocationUpdateListener locationUpdateListener) {
    this.locationUpdateListeners.remove(locationUpdateListener);
}

public void setLogLocationChanges(boolean logLocationChanges) {
    this.logLocationChanges = logLocationChanges;
}

public Location getLocation() {
    return location;
}

private Location startLocationListener() {
    canGetLocation = false;

    try {
        locationManager = (LocationManager) mContext
                .getSystemService(Service.LOCATION_SERVICE);

        // getting GPS status
        isGPSEnabled = locationManager
                .isProviderEnabled(LocationManager.GPS_PROVIDER);

        if (isGPSEnabled) {
            if (location == null) {
                locationManager.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER, 0, 0, this);
                locationManager.addGpsstatusListener(gpsListener);
                if (locationManager != null) {
                    location = locationManager
                            .getLastKNownLocation(LocationManager.GPS_PROVIDER);
                    if (location != null) {
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
                    }
                }
            }
        } else {
            showSettingsAlert();
        }

    } catch (Exception e) {
        e.printstacktrace();
    }

    return location;
}

public void stopUsingGPS(FirstFixListener firstFixListener,
        LocationUpdateListener locationUpdateListener) {
    if (firstFixListener != null)
        removeFirstFixListener(firstFixListener);
    if (locationUpdateListener != null)
        removeLocationUpdateListener(locationUpdateListener);

    stopUsingGPS();
}

/**
 * Stop using GPS listener Calling this function will stop using GPS in your
 * app
 * */
public void stopUsingGPS() {
    Log.d("DEBUG", "GPS stop");
    if (locationManager != null) {
        locationManager.removeUpdates(GPSTracker.this);
        location = null;

        if (gpsListener != null) {
            locationManager.removeGpsstatusListener(gpsListener);
        }

    }
    isGPSFix = false;
    location = null;
}

public void startUsingGPS(FirstFixListener firstFixListener,
        LocationUpdateListener locationUpdateListener) {
    Log.d("DEBUG", "GPS start");
    if (firstFixListener != null)
        addFirstFixListener(firstFixListener);
    if (locationUpdateListener != null)
        addLocationUpdateListener(locationUpdateListener);

    startLocationListener();
}

/**
 * Function to get latitude
 * */
public double getLatitude() {
    if (location != null) {
        latitude = location.getLatitude();
    } else {
        Log.e("GPSTracker", "getLatitude location is null");
    }

    // return latitude
    return latitude;
}

/**
 * Function to get longitude
 * */
public double getLongitude() {
    if (location != null) {
        longitude = location.getLongitude();
    } else {
        Log.e("GPSTracker", "getLongitude location is null");
    }

    // return longitude
    return longitude;
}

/**
 * Function to check GPS/wifi enabled
 * 
 * @return boolean
 * */
public boolean canGetLocation() {
    return this.canGetLocation;
}

/**
 * Function to show settings alert dialog On pressing Settings button will
 * lauch Settings Options
 * */
public void showSettingsAlert() {
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

    // Setting Dialog Title
    alertDialog.setTitle("GPS settings");

    // Setting Dialog Message
    alertDialog
            .setMessage("GPS is not enabled. Do you want to go to settings menu?");

    // On pressing Settings button
    alertDialog.setPositiveButton("Settings",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(
                            Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                    mContext.startActivity(intent);
                }
            });

    // on pressing cancel button
    alertDialog.setNegativeButton("Cancel",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });

    // Showing Alert Message
    alertDialog.show();
}

@Override
public void onLocationChanged(Location location) {
    if ( location == null)
        return;

    this.location = location;



    mLastLocationMillis = SystemClock.elapsedRealtime();
    canGetLocation = true;
    if (isGPSFix) {


        if (locationUpdateListeners != null) {
            for (LocationUpdateListener listener : locationUpdateListeners) {
                listener.onLocationChanged(location);
            }
        }
    }

}

@Override
public void onProviderdisabled(String provider) {
    canGetLocation = false;
}

@Override
public void onProviderEnabled(String provider) {

}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

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

private boolean wasGPSFix = false;

// https://stackoverflow.com/questions/2021176/how-can-i-check-the-current-status-of-the-gps-receiver
// answer from soundmaven
private class GPSFixListener implements Gpsstatus.Listener {
    public void onGpsstatusChanged(int event) {
        switch (event) {
        case Gpsstatus.GPS_EVENT_SATELLITE_STATUS:
            isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

            if (isGPSFix != wasGPSFix) { // only notify on changes
                wasGPSFix = isGPSFix;
                for (FirstFixListener listener : firstFixListeners) {
                    listener.onFirsFixChanged(isGPSFix);
                }
            }

            break;
        case Gpsstatus.GPS_EVENT_FirsT_FIX:
            // Do something.



            break;
        }
    }
}
}

关于Android位置服务开发android定位开发的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于android 4.0 蓝牙服务开启流程分析、Android Geofence禁用/重新启动位置服务时自动删除、Android – Android 1.6中开发的应用程序可以在Android 2.0中运行吗?、android – 位置服务GPS Force关闭的相关信息,请在本站寻找。

本文标签: