GVKun编程网logo

Android开发中方向传感器定义与用法详解【附指南针实现方法】(android 方向传感器)

14

对于Android开发中方向传感器定义与用法详解【附指南针实现方法】感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解android方向传感器,并且为您提供关于AndroidNDK开发中SO包

对于Android开发中方向传感器定义与用法详解【附指南针实现方法】感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解android 方向传感器,并且为您提供关于Android NDK 开发中 SO 包大小压缩方法详解、android – 使用传感器事件检测手机方向、android – 使用方向传感器指向特定位置、android – 指南针方向因手机方向而异的宝贵知识。

本文目录一览:

Android开发中方向传感器定义与用法详解【附指南针实现方法】(android 方向传感器)

Android开发中方向传感器定义与用法详解【附指南针实现方法】(android 方向传感器)

本文实例讲述了Android开发中方向传感器定义与用法。分享给大家供大家参考,具体如下:

Android中的方向传感器在生活中是一个很好的应用,典型的例子是指南针的使用,我们先来简单介绍一下传感器中三个参数x,y,z的含义,以一幅图来说明。

补充说明:图中的坐标轴x,z和传感器中的X,Y,Z没有任何联系!

如上图所示,绿色部分表示一个手机,带有小圈那一头是手机头部

传感器中的X:如上图所示,规定X正半轴为北,手机头部指向OF方向,此时X的值为0,如果手机头部指向OG方向,此时X值为90,指向OH方向,X值为180,指向OE,X值为270

传感器中的Y:现在我们将手机沿着BC轴慢慢向上抬起,即手机头部不动,尾部慢慢向上翘起来,直到AD跑到BC右边并落在XOY平面上,Y的值将从0~180之间变动,如果手机沿着AD轴慢慢向上抬起,即手机尾部不懂,直到BC跑到AD左边并且落在XOY平面上,Y的值将从0~-180之间变动,这就是方向传感器中Y的含义。

传感器中的Z:现在我们将手机沿着AB轴慢慢向上抬起,即手机左边框不动,右边框慢慢向上翘起来,直到CD跑到AB右边并落在XOY平面上,Z的值将从0~180之间变动,如果手机沿着CD轴慢慢向上抬起,即手机右边框不动,直到AB跑到CD左边并且落在XOY平面上,Z的值将从0~-180之间变动,这就是方向传感器中发Z的含义。

了解了方向传感器中X,Y,Z的含义之后下面我们就开始学习如何使用

首先我们创建一个传感器管理器和一个传感器监听器,管理器用来管理传感器以及创建各种各样的传感器,监听器用来监视传感器的变化并且进行相应的操作

private SensorManager sensorManager;
private MySensorEventListener mySensorEventListener;
mySensorEventListener= new MySensorEventListener();//这个监听器当然是我们自己定义的,在方向感应器感应到手机方向有变化的时候,我们可以采取相应的操作,这里紧紧是将x,z的值打印出来
private final class MySensorEventListener implements SensorEventListener{
@Override
//可以得到传感器实时测量出来的变化值
public void onSensorChanged(SensorEvent event) {
//方向传感器
if(event.sensor.getType()==Sensor.TYPE_ORIENTATION){
//x表示手机指向的方位,0表示北,90表示东,180表示南,270表示西
float x = event.values[SensorManager.DATA_X];
float y = event.values[SensorManager.DATA_Y];
float z = event.values[SensorManager.DATA_Z];
//tv_orientation是界面上的一个TextView标签,不再赘述
tv_orientation.setText("Orientation:"+x+","+y+","+z);
}
}

我们在onResume方法中创建一个方向传感器,并向系统注册监听器

protected void onResume() {
  Sensor sensor_orientation=sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
  sensorManager.registerListener(mySensorEventListener,sensor_orientation,SensorManager.SENSOR_DELAY_UI);
super.onResume();
}

最后我们在onPause()中注销所有传感器的监听,释放方向感应器资源!

protected void onPause() {
//注销所有传感器的监听
sensorManager.unregisterListener(mySensorEventListener);
super.onPause();
}

到此,有关方向传感器的介绍完毕!

完整实例代码点击此处本站下载

附:Android基于方向传感器实现指南针功能

step1:新建一个项目Compass,并将一张指南针图片导入到res/drawable-hdpi目录中

step2:设计应用的UI界面,main.xml

@H_301_58@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/compass" android:id="@+id/imageView" /> </LinearLayout>

step3:MainActivity.java

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
public class MainActivity extends Activity {
  private ImageView imageView;
  /** 传感器管理器 */
  private SensorManager manager;
  private SensorListener listener = new SensorListener();
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    imageView = (ImageView) this.findViewById(R.id.imageView);
    imageView.setKeepScreenOn(true);//屏幕高亮
    //获取系统服务(SENSOR_SERVICE)返回一个SensorManager 对象
    manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
  }
  @Override
  protected void onResume() {
    /**
     * 获取方向传感器
     * 通过SensorManager对象获取相应的Sensor类型的对象
     */
    Sensor sensor = manager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
    //应用在前台时候注册监听器
    manager.registerListener(listener,sensor,SensorManager.SENSOR_DELAY_GAME);
    super.onResume();
  }
  @Override
  protected void onPause() {
    //应用不在前台时候销毁掉监听器
    manager.unregisterListener(listener);
    super.onPause();
  }
  private final class SensorListener implements SensorEventListener {
    private float predegree = 0;
    @Override
    public void onSensorChanged(SensorEvent event) {
      /**
       * values[0]: x-axis 方向加速度
         values[1]: y-axis 方向加速度
         values[2]: z-axis 方向加速度
       */
      float degree = event.values[0];// 存放了方向值
      /**动画效果*/
      RotateAnimation animation = new RotateAnimation(predegree,degree,Animation.RELATIVE_TO_SELF,0.5f,0.5f);
      animation.setDuration(200);
      imageView.startAnimation(animation);
      predegree=-degree;
      /**
      float x=event.values[SensorManager.DATA_X];
      float y=event.values[SensorManager.DATA_Y];
      float z=event.values[SensorManager.DATA_Z];
      Log.i("XYZ","x="+(int)x+",y="+(int)y+",z="+(int)z);
      */
    }
    @Override
    public void onAccuracyChanged(Sensor sensor,int accuracy) {
    }
  }
}

step4:AndroidManifest.xml

@H_301_58@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.roco.sensor" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name="MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android视图View技巧总结》、《Android编程之activity操作技巧总结》、《Android操作SQLite数据库技巧总结》、《Android操作json格式数据技巧总结》、《Android资源操作技巧汇总》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

Android NDK 开发中 SO 包大小压缩方法详解

Android NDK 开发中 SO 包大小压缩方法详解

背景

这周在做Yoga包的压缩工作。Yoga本身是用BUCK脚本编译的,而最终编译出几个包大小大总共约为7M,不能满足项目中对于APK大小的限制,因此需要对它进行压缩。

这里先将Yoga编译脚本用CMAKE重新改写,以便可以在android studio中直接使用并输出一个AAR的包。后面又对它进行了压缩,最终将Yoga包的大小压缩到200多KB。

下面整理了一些可以用于减少NDK开发中Android SO包大小的方法:

1.STL的使用方式

对于C++的library,引用方式有2种:

  • 静态方式(static)
  • 动态方式(shared)

其中,静态方式在编译时会将用到的相关代码直接复制到目的文件中;而动态方式则会将相关的代码打成so文件,以便多次引用。由于编译器在编译时并不能知道所有被引用的地方,所以同时会打入了很多不相关的代码。

所以,如果项目中引用library的函数较多时,用动态方式可以避免多次拷贝,节省空间。相反,则直接使用静态方式会更节省空间。

NDK开发中,可以通过gradle的设置来配置:

defaultConfig{
  externalNativeBuild{
    cmake{
      // gnustl_shared 动态
      arguments "-DANDROID_STL=gnustl_static" 
    }
  }
}

在Yoga中,项目里的stl使用较少时,安卓运行时使用static的方式,而不是shared,所以这里采用static的方式。在采取了这种方式后,包的大小从2.7M缩减到了2M。

2.不使用Exception和RTTI

C++的exception和RTTI功能在NDK中默认是关闭的,但是可以通过配置打开的。

Android.mk:

APP_CPPFLAGS += -fexceptions -frtti

CMake:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -frtti")

Exception和RTTI会显著的增加包的体积,所以非必须的时候,没有必要使用。

RTTI

通过RTTI,能够通过基类的指针或引用来检索其所指对象的实际类型,即运行时获取对象的实际类型。C++通过下面两个操作符提供RTTI。

(1)typeid:返回指针或引用所指对象的实际类型。

(2)dynamic_cast:将基类类型的指针或引用安全的转换为派生类型的指针或引用。

在yoga中,RTTI的选项是默认打开的,而代码中其实并没有用到相关的功能,这里可以直接关闭。

Exception

使用C++的exception会增加包的大小,而目前JNI对C++的exception的支持是有bug的,比如下面这段代码就会引起程序的crash(对于低版本的android NDK)。

因此要在程序中引入exception要自己实现相关逻辑,yoga就是这么做的,这个又增加了一些包体大小。对于开发者来说,exception可以帮助快速定位问题,而对于使用者并不是那么重要,这里可以去掉。

try {
    ...
} catch (std::exception& e) {
    env->ThrowNew(env->FindClass("java/lang/Exception"), "Error occured");
}

在yoga中,在关闭RTTI和Exception功能并把exception相关的代码都去掉后,包的大小从2M缩减到的1.8M。

3.使用 gc-sections去除没有用到的函数

去除未使用的代码显然可以减少包体的大小,而在NDK的开发中,并不需要手动的来做这一点。可以开启编译器的gc-sections选项,让编译器自动的帮你做到这一点。

编译器可以配置自动去除未使用的函数和变量,以下是配置方式:

CMake:

# 去除未使用函数与变量
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
# 设置去除未使用代码的链接flag
SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections")

Android.mk:

LOCAL_CPPFLAGS += -ffunction-sections -fdata-sections
LOCAL_CFLAGS += -ffunction-sections -fdata-sections 
LOCAL_LDFLAGS += -Wl,--gc-sections

4.去除冗余代码

在NDK中,链接器还有一个选项 “-icf = safe”,可以用于去除代码中的冗余代码。但是要注意的是,这个选项也有可能去除定义好的inline函数,这里必须要做好权衡。

下面是配置方式:

CMake:

SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections,--icf=safe")

Android.mk:

LOCAL_LDFLAGS += -Wl,--gc-sections,--icf=safe

5.设置编译器的优化flag

编译器有个优化flag可以设置,分别是-Os(体积最小),-O3(性能最优)等。这里将编译器的优化flag设置为-Os,以便减少体积。

CMake:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")

Android.mk

LOCAL_CPPFLAGS += -Os
LOCAL_CFLAGS += -Os

在采用了3,4,5这几种方式后,Yoga包的大小从1.8M减少到了1.7M。这里减少的比较少是因为Yoga在这方面已经做的挺好了,其他的库可能会更有效。

6.设置编译器的 Visibility Feature

还有个减少包体大小的方法,就是设置编译器的visibility feature。

Visibility Feature就是用来控制在哪些函数可以在符号表中被输入,由于C++并不是完全面向对象的,非类的方法并没有public这种修饰符,因此,要用Visibility Feature来控制哪些函数可以被外部调用。

而JNI提供了一个宏-JNIEXPORT来控制这点。所以只要对函数加上这个宏,像这样:

// JNIEXPORT就是控制可见的宏
// JNICALL在NDK这里没有什么意义,只是个标识宏
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString)

然后在编译器的FLAGS选项开启 -fvisibility = hidden 就可以。这样,不仅可以控制函数的可见性,并且可以减少包体的大小。

CMake:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")

7.设置编译器的Strip选项

我在把Yoga库编译成AAR包的过程中发现,它的体积明显会大于最后打包进APK的大小,这点非常不合理,但是无法找到原因。

最终搜索到这是谷歌NDK的一个bug,在打AAR包的过程中,无论是debug版本还是release版本,NDK toolchain不会自动的把方便调试的C++ 符号表(Symbol Table)中数据删除,而只会在打APK包的时候进行这一操作。这就导致了打成的AAR包中的SO体积明显偏大。

找到原因后这个问题就很好解决了,可以手动的在链接选项中加入 strip参数,配置如下所示:

SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections,--icf=safe,-s")

强制进行strip操作后,将Yoga包的体积从1.7M成功减少到了282KB。

8.去除C++代码中的iostream相关代码

使用STL中的iostream相关库会明显的增加包的体积,而NDK本身是有预编译库(android/log.h)可以代替这一功能的,在Yoga这里,用log的函数代替了iostream中的所有函数,如:

//代替所有的iostream库里函数
//cout << obj->toString() << endl;
__android_log_print(ANDROID_LOG_VERBOSE,"Yoga","Node is: %s",obj->toString().c_str());

在做完代替之后,yoga包的体积从282KB减少到了218KB。

总结

在做完这一系列工作后,最终成功的压缩了Yoga包的体积,从几M到最后输出一个218KB的AAR包提供使用。以上几种方法并不局限于Yoga包的缩减。在NDK开发中,要缩减SO包的体积都可以按照这几种方式尝试一下。

以上就是Android NDK 开发中 SO 包大小压缩方法详解的详细内容,更多关于Android NDK开发SO包压缩的资料请关注其它相关文章!

您可能感兴趣的文章:
  • Android数据缓存框架内置ORM功能使用教程
  • Android 性能优化实现全量编译提速的黑科技
  • Android neon 优化实践示例
  • Android开发OkHttp执行流程源码分析
  • Android 动态加载 so实现示例详解
  • Android中FileProvider的各种场景应用详解

android – 使用传感器事件检测手机方向

android – 使用传感器事件检测手机方向

我有一个Activity,需要锁定它的方向
setRequestedOrientation(screenorientation);

但我希望获得方向更新,以便我可以对UI进行调整(想象一下HTC相机应用程序,只有按钮的图标改变方向).
所以我找到了this class.它提供了0到360之间的方向值.我可以过滤这个值,即完美区间[a,b],如果< x< b那么方向是横向还是纵向?计算意味着什么?任何提示?

解决方法

听起来你需要代码才能在设备的方向改变为4个法线方向之一而不是每个角度时做出反应.这会将方向过滤为仅限0度,90度,180度和270度的值:
OrientationEventListener myOrientationEventListener;
    int iOrientation;

    myOrientationEventListener = new OrientationEventListener(this,SensorManager.SENSOR_DELAY_norMAL)
    {

        @Override
        public void onorientationChanged(int iAngle)
        {                         // 0  15 30 45 60 75,90 105,120,135,150,165,180,195,210,225,240,255,270,285,300,315,330,345
            final int iLookup[] = {0,90,0}; // 15-degree increments 
            if (iAngle != ORIENTATION_UNKNowN)
            {
                int iNewOrientation = iLookup[iAngle / 15];
                if (iOrientation != iNewOrientation)
                {
                    iOrientation = iNewOrientation;
                    // take action on new orientation here
                }
            }
        }
    };

    // To display if orientation detection will work and enable it
    if (myOrientationEventListener.canDetectOrientation())
    {
    Toast.makeText(this,"Can DetectOrientation",Toast.LENGTH_LONG).show();
        myOrientationEventListener.enable();
    }
    else
    {
    Toast.makeText(this,"Can't DetectOrientation",Toast.LENGTH_LONG).show();
    }

android – 使用方向传感器指向特定位置

android – 使用方向传感器指向特定位置

我试图实现一个箭头,使用方向传感器来指向特定的位置. Google Places会在ListView中为其找到的每个地方实现此箭头.

我设法得到方位角,但是给出一个位置,我不知道如何继续计算我需要的角度.此外,我需要从北方和北极的转换.有人有这样的实现的例子吗?

提前致谢.

解决方法

我解决了
float azimuth = // get azimuth from the orientation sensor (it's quite simple)
Location currentLoc = // get location from GPS or network
// convert radians to degrees
azimuth = Math.todegrees(azimuth);
GeomagneticField geoField = new GeomagneticField(
             (float) currentLoc.getLatitude(),(float) currentLoc.getLongitude(),(float) currentLoc.getAltitude(),System.currentTimeMillis());
azimuth += geoField.getDeclination(); // converts magnetic north into true north
float bearing = currentLoc.bearingTo(target); // (it's already in degrees)
float direction = azimuth - bearing;

如果要绘制箭头或其他东西来指向方向,请使用canvas.rotate(–irection).我们传递一个负面的参数,因为画布旋转是逆时针的.

android – 指南针方向因手机方向而异

android – 指南针方向因手机方向而异

我的增强现实应用程序需要摄像机视图的罗盘方位,并且有很多从传感器管理器获取方向的示例.

然而,我发现结果值取决于手机方向 – 旋转到右侧的景观与旋转到左侧的景观大约相差10度(ROTATION_0和ROTATION_180之间的差异较小,但仍然不同).这种差异足以破坏任何AR效应.

它与校准有关吗? (我不相信我正在做正确的8件事 – 我尝试过在youtube上找到的各种方法).

任何想法为何有区别?我搞砸了旋转矩阵的东西吗?我可以选择将应用程序限制为单一方向,但仍然担心罗盘读数仍然不是很准确(即使过滤后它相当稳定)

public void onSensorChanged(SensorEvent event) {
        if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
            return;
        }

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)  mGravity = event.values;
        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) mGeomagnetic = event.values;

        if (mGravity != null && mGeomagnetic != null) {

            float[] rotationMatrixA = mRotationMatrixA;
            if (SensorManager.getRotationMatrix(rotationMatrixA,null,mGravity,mGeomagnetic)) {

                float[] rotationMatrixB = mRotationMatrixB;

                display display = getwindowManager().getDefaultdisplay(); 
                int deviceRot = display.getRotation();

                switch (deviceRot)
                {
                // portrait - normal
                case Surface.ROTATION_0: SensorManager.remapCoordinateSystem(rotationMatrixA,SensorManager.AXIS_X,SensorManager.AXIS_Z,rotationMatrixB);
                break;
                // rotated left (landscape - keys to bottom)
                case Surface.ROTATION_90: SensorManager.remapCoordinateSystem(rotationMatrixA,SensorManager.AXIS_MINUS_X,rotationMatrixB); 
                break;
                // upside down
                case Surface.ROTATION_180: SensorManager.remapCoordinateSystem(rotationMatrixA,rotationMatrixB); 
                break;
                // rotated right
                case Surface.ROTATION_270: SensorManager.remapCoordinateSystem(rotationMatrixA,SensorManager.AXIS_MINUS_Z,rotationMatrixB); 
                break;

                default:  break;
                }

                float[] dv = new float[3]; 
                SensorManager.getorientation(rotationMatrixB,dv);
                // add to smoothing filter
                fd.AddLatest((double)dv[0]); 
            }
            mDraw.invalidate();     
        }
    }

解决方法

试试这个
public void onSensorChanged(SensorEvent event) {
    if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
        return;
    }

    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)  mGravity = event.values.clone ();
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) mGeomagnetic =  event.values.clone ();

    if (mGravity != null && mGeomagnetic != null) {

        float[] rotationMatrixA = mRotationMatrixA;
        if (SensorManager.getRotationMatrix(rotationMatrixA,mGeomagnetic)) {

            float[] rotationMatrixB = mRotationMatrixB;
            SensorManager.remapCoordinateSystem(rotationMatrixA,rotationMatrixB);
            float[] dv = new float[3]; 
            SensorManager.getorientation(rotationMatrixB,dv);
            // add to smoothing filter
            fd.AddLatest((double)dv[0]); 
        }
        mDraw.invalidate();     
    }
}

你不需要switch语句,似乎有很多关于getRotationMatrix,remapCoordinateSystem和来自stackoverflow问题的getorientation的混淆.我可能会在不久的将来写下这些的详细解释.

关于Android开发中方向传感器定义与用法详解【附指南针实现方法】android 方向传感器的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于Android NDK 开发中 SO 包大小压缩方法详解、android – 使用传感器事件检测手机方向、android – 使用方向传感器指向特定位置、android – 指南针方向因手机方向而异的相关信息,请在本站寻找。

本文标签: