对于Android开发中方向传感器定义与用法详解【附指南针实现方法】感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解android方向传感器,并且为您提供关于AndroidNDK开发中SO包
对于Android开发中方向传感器定义与用法详解【附指南针实现方法】感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解android 方向传感器,并且为您提供关于Android NDK 开发中 SO 包大小压缩方法详解、android – 使用传感器事件检测手机方向、android – 使用方向传感器指向特定位置、android – 指南针方向因手机方向而异的宝贵知识。
本文目录一览:- Android开发中方向传感器定义与用法详解【附指南针实现方法】(android 方向传感器)
- Android NDK 开发中 SO 包大小压缩方法详解
- 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
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
更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android视图View技巧总结》、《Android编程之activity操作技巧总结》、《Android操作SQLite数据库技巧总结》、《Android操作json格式数据技巧总结》、《Android资源操作技巧汇总》及《Android控件用法总结》
希望本文所述对大家Android程序设计有所帮助。
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 – 使用传感器事件检测手机方向
setRequestedOrientation(screenorientation);
但我希望获得方向更新,以便我可以对UI进行调整(想象一下HTC相机应用程序,只有按钮的图标改变方向).
所以我找到了this class.它提供了0到360之间的方向值.我可以过滤这个值,即完美区间[a,b],如果< x< b那么方向是横向还是纵向?计算意味着什么?任何提示?
解决方法
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 – 使用方向传感器指向特定位置
我设法得到方位角,但是给出一个位置,我不知道如何继续计算我需要的角度.此外,我需要从北方和北极的转换.有人有这样的实现的例子吗?
提前致谢.
解决方法
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 – 指南针方向因手机方向而异
然而,我发现结果值取决于手机方向 – 旋转到右侧的景观与旋转到左侧的景观大约相差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 – 指南针方向因手机方向而异的相关信息,请在本站寻找。
本文标签: