最近有个需求就是程序在后台监听手机的屏幕的旋转方向,废话不多说,先看看效果:


摸摸头~,既然是监听屏幕的旋转方向,那就需要要弄明白Android的屏幕是由谁控制的?,方向又是怎么控制的?

带着问题我们来一探究竟:

手机的屏幕管理类是WindowManager,那是不是可先去WindowManager类看看,从这个类里边并没法看到跟屏幕方向有关系方法~,在看该类的时候又发现了另一个类:

public class IWindowManagerImpl implements IWindowManager {...
}

通过对源码的一番撕扯,终于找到了一点蛛丝马迹:

public int watchRotation(IRotationWatcher arg0, int arg1) throws RemoteException

通过Android Studio看上边这个类会发现它实现的是IWindowManager,报红了,这个文件经过查询Android源代码发现是一个aidl文件,看看这个文件,最终发现屏幕的旋转方向监听是在IWindowManager.aidl文件中处理的,所以我们先看看这个文件:(sdk版本:7.1.1_r28)

package android.view;/**
* System private interface to the window manager.
*
* {@hide}
*/
interface IWindowManager
{....sdk<=25/*** Retrieve the current screen orientation, constants as per* {@link android.view.Surface}.*/int getRotation();sdk<=25/*** Watch the rotation of the screen.  Returns the current rotation,* calls back when it changes.*/int watchRotation(IRotationWatcher watcher);sdk>25/*** Watch the rotation of the specified screen.  Returns the current rotation,* calls back when it changes.*/int watchRotation(IRotationWatcher watcher, int displayId);/*** Remove a rotation watcher set using watchRotation.* @hide*/void removeRotationWatcher(IRotationWatcher watcher);/*** Lock the device orientation to the specified rotation, or to the* current rotation if -1.  Sensor input will be ignored until* thawRotation() is called.* @hide*/void freezeRotation(int rotation);....
}

这里主要看几个跟屏幕方向有关的方法,由于不同的sdk版本里边的函数也有所不同。所以我的想法就是通过这个IWindowManager接口来实现屏幕方向的监听,那么首先我们就需要获取到IWindowManager的实例。

再看看IWindowManager.aidl的实现类的部分代码,这里我们只需要关注如何获取IWindowManager的实例:


```java
public interface IWindowManager extends IInterface {void getBaseDisplaySize(int paramInt, Point paramPoint) throws RemoteException;void getInitialDisplaySize(int paramInt, Point paramPoint) throws RemoteException;void getRealDisplaySize(Point paramPoint) throws RemoteException;int getRotation() throws RemoteException;void removeRotationWatcher(IRotationWatcher paramIRotationWatcher) throws RemoteException;public static abstract class Stub extends Binder implements IWindowManager {private static final String DESCRIPTOR = "android.view.IWindowManager";static final int TRANSACTION_getBaseDisplaySize = 3;static final int TRANSACTION_getInitialDisplaySize = 1;static final int TRANSACTION_getRealDisplaySize = 2;static final int TRANSACTION_getRotation = 4;static final int TRANSACTION_removeRotationWatcher = 5;public Stub() {attachInterface(this, "android.view.IWindowManager");}public static IWindowManager asInterface(IBinder param1IBinder) {if (param1IBinder == null)return null;IInterface iInterface = param1IBinder.queryLocalInterface("android.view.IWindowManager");return (iInterface != null && iInterface instanceof IWindowManager) ? (IWindowManager) iInterface : new Proxy(param1IBinder);}public IBinder asBinder() {return (IBinder) this;}public boolean onTransact(int param1Int1, Parcel param1Parcel1, Parcel param1Parcel2, int param1Int2) throws RemoteException {Point point = new Point();if (param1Int1 != 1) {if (param1Int1 != 2) {if (param1Int1 != 3) {if (param1Int1 != 4) {if (param1Int1 != 5) {if (param1Int1 != 1598968902)return super.onTransact(param1Int1, param1Parcel1, param1Parcel2, param1Int2);param1Parcel2.writeString("android.view.IWindowManager");return true;}param1Parcel1.enforceInterface("android.view.IWindowManager");removeRotationWatcher(IRotationWatcher.Stub.asInterface(param1Parcel1.readStrongBinder()));param1Parcel2.writeNoException();return true;}param1Parcel1.enforceInterface("android.view.IWindowManager");param1Int1 = getRotation();param1Parcel2.writeNoException();param1Parcel2.writeInt(param1Int1);return true;}param1Parcel1.enforceInterface("android.view.IWindowManager");param1Int1 = param1Parcel1.readInt();point = new Point();getBaseDisplaySize(param1Int1, point);param1Parcel2.writeNoException();param1Parcel2.writeInt(1);point.writeToParcel(param1Parcel2, 1);return true;}point.enforceInterface("android.view.IWindowManager");point = new Point();getRealDisplaySize(point);param1Parcel2.writeNoException();param1Parcel2.writeInt(1);point.writeToParcel(param1Parcel2, 1);return true;}point.enforceInterface("android.view.IWindowManager");param1Int1 = point.readInt();Point point = new Point();getInitialDisplaySize(param1Int1, point);param1Parcel2.writeNoException();param1Parcel2.writeInt(1);point.writeToParcel(param1Parcel2, 1);return true;}private static class Proxy implements IWindowManager {private IBinder mRemote;Proxy(IBinder param2IBinder) {this.mRemote = param2IBinder;}public IBinder asBinder() {return this.mRemote;}public void getBaseDisplaySize(int param2Int, Point param2Point) throws RemoteException {Parcel parcel1 = Parcel.obtain();Parcel parcel2 = Parcel.obtain();try {parcel1.writeInterfaceToken("android.view.IWindowManager");parcel1.writeInt(param2Int);this.mRemote.transact(3, parcel1, parcel2, 0);parcel2.readException();if (parcel2.readInt() != 0)param2Point.readFromParcel(parcel2);return;} finally {parcel2.recycle();parcel1.recycle();}}public void getInitialDisplaySize(int param2Int, Point param2Point) throws RemoteException {Parcel parcel1 = Parcel.obtain();Parcel parcel2 = Parcel.obtain();try {parcel1.writeInterfaceToken("android.view.IWindowManager");parcel1.writeInt(param2Int);this.mRemote.transact(1, parcel1, parcel2, 0);parcel2.readException();if (parcel2.readInt() != 0)param2Point.readFromParcel(parcel2);return;} finally {parcel2.recycle();parcel1.recycle();}}public String getInterfaceDescriptor() {return "android.view.IWindowManager";}public void getRealDisplaySize(Point param2Point) throws RemoteException {Parcel parcel1 = Parcel.obtain();Parcel parcel2 = Parcel.obtain();try {parcel1.writeInterfaceToken("android.view.IWindowManager");this.mRemote.transact(2, parcel1, parcel2, 0);parcel2.readException();if (parcel2.readInt() != 0)param2Point.readFromParcel(parcel2);return;} finally {parcel2.recycle();parcel1.recycle();}}public int getRotation() throws RemoteException {Parcel parcel1 = Parcel.obtain();Parcel parcel2 = Parcel.obtain();try {parcel1.writeInterfaceToken("android.view.IWindowManager");this.mRemote.transact(4, parcel1, parcel2, 0);parcel2.readException();return parcel2.readInt();} finally {parcel2.recycle();parcel1.recycle();}}public void removeRotationWatcher(IRotationWatcher param2IRotationWatcher) throws RemoteException {Parcel parcel1 = Parcel.obtain();Parcel parcel2 = Parcel.obtain();try {parcel1.writeInterfaceToken("android.view.IWindowManager");if (param2IRotationWatcher != null) {IBinder iBinder = param2IRotationWatcher.asBinder();} else {param2IRotationWatcher = null;}parcel1.writeStrongBinder((IBinder) param2IRotationWatcher);this.mRemote.transact(5, parcel1, parcel2, 0);parcel2.readException();return;} finally {parcel2.recycle();parcel1.recycle();}}}}
}

代码可以看到,我们可以通过IWindowManager中的Stub的asInterface()方法返回IWindowManager的代理类Proxy,然后通过Proxy来调用相应的方法:

//加载得到ServiceManager类,然后得到方法getService。
Method getServiceMethod = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", new Class[]{String.class});
//通过getServiceMethod得到ServiceManager的实例(隐藏类,所以使用Object)
Object ServiceManager = getServiceMethod.invoke(null, new Object[]{"window"});
//通过反射的到Stub
Class<?> cStub = Class.forName("android.view.IWindowManager$Stub");
//得到Stub类的asInterface 方法
Method asInterface = cStub.getMethod("asInterface", IBinder.class);
//然后通过类似serviceManager.getIWindowManager的方法获取IWindowManager的实例
Object IWindowManager=asInterface.invoke(null, ServiceManager);

这里就拿到了IWindowManager的代理类,然后我们通过反射获取到watchRotation()函数:

Method watchRotation = IWindowManager.getClass().getDeclaredMethod("watchRotation",参数类型)

这里获取watchRotation函数需要知道它的参数类型以及参数的个数:

public static Class<?>[] getMethodParamTypes(Class<?> clazz, String methodName) {Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {Log.e("Service", "method = " + method.toGenericString());if (methodName.equals(method.getName())) {return method.getParameterTypes();}}return null;
}

修改watchRotation函数的获取方法:

Class<?> paramTypes = getMethodParamTypes(IWindowManager.getClass(),"watchRotation")
Method watchRotation = IWindowManager.getClass().getDeclaredMethod("watchRotation",paramTypes)

这里可以打印参数类型看看:

sdk>25
public int android.view.IWindowManager$Stub$Proxy.watchRotation(android.view.IRotationWatcher,int) throws android.os.RemoteExceptionsdk<=25
public int android.view.IWindowManager$Stub$Proxy.watchRotation(android.view.IRotationWatcher) throws android.os.RemoteException

需要一个IRotationWatcher和int类型的参数,IRotationWatcher接口后边讲。这里需要注意我们最开始说的版本兼容问题,因为我这里是在android 9手机上测试的,所以这里的watchRotation方法多了个int类型的参数,因此我们执行watchRotation方法的时候需要做版本适配:

public static int watchRotation(IRotationWatcher watcher) {int rotation = -1;try {Object iWindowManager = getIWindowManager();Class<?>[] paramTypes = ClassUtils.getMethodParamTypes(iWindowManager.getClass(), "watchRotation");Method watchRotation = ClassUtils.getMethod("watchRotation", iWindowManager.getClass(), paramTypes);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//这里做个适配rotation = (int) watchRotation.invoke(iWindowManager, watcher, 0);} else {rotation = (int) watchRotation.invoke(iWindowManager, watcher);}Log.e("Service", "rotation = " + rotation);} catch (Exception e) {e.printStackTrace();}return rotation;
}

接下来看看IRotationWatcher这个接口,查询资料发现也是一个Binder对象。思考一下,手机屏幕的旋转显然是在系统进程中监听的,所以这里就需要跨进程通讯获取到屏幕方向,先看看这个文件:

//RotationWatcher.aidlpackage android.view;/**
* {@hide}
*/
interface IRotationWatcher {void onRotationChanged(int rotation);
}

这里的IRotationWatcher是一个aidl接口,我们通过这个接口实现屏幕方向旋转结果的回调监听,所以这里我们只需要将这个文件拷贝到我们项目的aidl包中,包名必须是android.view才行:

编译后生成IRotationWatcher.java文件,然后我们创建一个Service,在Service中监听屏幕的旋转:

public class AccessService extends Service {public AccessService() {}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.e("Service", "onStartCommand rotation = " + WindowUtils.watchRotation(new RotationWatcher()));return super.onStartCommand(intent, flags, startId);}@Overridepublic IBinder onBind(Intent intent) {throw new UnsupportedOperationException("Not yet implemented");}public static class RotationWatcher extends IRotationWatcher.Stub {@Overridepublic void onRotationChanged(int rotation) throws RemoteException {Log.e("Service", "onRotationChanged = " + rotation);}@Overridepublic IBinder asBinder() {return new RotationWatcher();}}
}

这里我们实现IRotationWatcher的回调方法onRotationChanged,然后创建IRotationWatcher的实例并传给watchRotation方法:

WindowUtils.watchRotation(new RotationWatcher())

ok,到此我们可以运行程序并切到后台后,然后切换手机的横竖屏,结果:

E/Service: onStartCommand rotation = 0E/Service: onRotationChanged = 1
E/Service: onRotationChanged = 0
E/Service: onRotationChanged = 3

可以看到,我们成功的监听到了屏幕方向的旋转!

Android后台监听全局屏幕旋转相关推荐

  1. iOS 监听手机屏幕旋转

    监听屏幕旋转 首先所要监听的NSNotificationName是UIApplicationDidChangeStatusBarOrientationNotification而非UIDeviceOri ...

  2. js监听手机屏幕旋转

    //事例一 window.addEventListener("orientationchange", function() {if(window.orientation === 9 ...

  3. H5 + vue 监听手机屏幕旋转及判断横竖屏

    mounted () {// 监听 resize 方法window.addEventListener("resize", this.renderResize, false) }, ...

  4. Android怎样监听蓝牙耳机的按键事件

    Android怎样监听蓝牙耳机的按键事件 写在前面: 直接想要代码非常easy,你直接把滚动栏拉到最底端就能够看到.假设想要十分地了解为什么,那就依照我规划的一步一步来理解.下面測试环境以手头上有的「 ...

  5. Android如何监听蓝牙耳机的按键事件(转)

    源: Android如何监听蓝牙耳机的按键事件 写在前面: 直接想要代码很简单,你直接把滚动条拉到最底端就可以看到.如果想要十分地了解为什么,那就按照我规划的一步一步来理解.以下测试环境以手头上有的「 ...

  6. android 如何监听应用前后台切换

    今天,简单讲讲android如何判断应用切换到后台和应用切换到前台. 这个其实很简单,之前需要做一个功能,当app由后台进入前台时需要完成一些逻辑操作,所以在网上查找如何判断app由后台进入前台,最终 ...

  7. android listview ontouchlistener,Android ListView监听滑动事件的方法(详解)

    ListView的主要有两种滑动事件监听方法,OnTouchListener和OnScrollListener 1.OnTouchListener OnTouchListener方法来自View中的监 ...

  8. android 监听动画过程,Android应用开发之Android动画监听实现方法

    本文将带你了解Android应用开发Android动画监听实现方法,希望本文对大家学Android有所帮助. Android动画监听实现方法. package com.briup.anim; impo ...

  9. 通过PhoneStateListener实现Android电话监听

    电话监听是比较简单的安卓案例.但却非常经典,因为它涵盖了动态监听.服务绑定.文件保存三大技术操作.作为Android学习的不错案例,今天我就和大家一起来看看安卓通过PhoneStateListener ...

最新文章

  1. WORD 同一位置引用多篇文献
  2. C++运算符重载 实现有理数(分数)的加减法
  3. Android应用中,去掉Activity标题栏以及状态栏
  4. 如何在 Github 工作流文件里引用自定义实现的 action
  5. Linux 普通用户拿到root权限及使用szrz命令上传下载文件
  6. ios 一直是正在等待审核_iOS开发者账号被调查了,相关问题整理
  7. 小记安装python的MySQLdb模块
  8. 4. PHP递增/递减运算符
  9. [转]HttpWatch工具简介及使用技巧
  10. [error] eclipse编写spring等xml配置文件时只有部分提示,tx无提示
  11. 【转】Js 数组转JSON格式
  12. 去掉重复值php,php多维数组去掉重复值
  13. LeetCode-11-Container With Most Water
  14. Qt 多语言切换——Qt语言家
  15. jdk目录详解及其使用方法
  16. elpida颗粒_内存涨价三星背锅,晶圆颗粒远远不止这几家!
  17. 模型思考笔记2—分类和同群效应带来的思考
  18. android 实现ble蓝牙自动配对连接
  19. unity 3d孤岛求生案例代码解析
  20. QQ群 该页面暂时无法显示

热门文章

  1. 【进阶篇】全流程学习《20天掌握Pytorch实战》纪实 | Day08 | 低阶API示范
  2. 三星c9pro语言,三星C9Pro评测 为什么被认为专门为中国用户量身打造的一款产品...
  3. 服务器运维管理系统突发故障
  4. 如何量化样本偏差对信贷风控模型的影响?
  5. 又一匹创新黑马:Gwallet全球路演正式开启
  6. 血管造影 /x射线/FPGA医疗应用
  7. 秋招季,对于三方协议你了解多少?|智测优聘总结
  8. W3School-CSS 背景实例
  9. 公众号推送模板消息(JAVA版)
  10. 超级计算机预测图上蓝格子,怪事?超级计算机预测图上,四川突现蓝格子,分析:是危险信号!...