记录学习Android基础的心得07:硬件控制P1



好时节,愿得年年,常见中秋月。----徐有贞《中秋月·中秋月》


前言

一台手机搭载了许多的硬件模块,而不同的手机厂商对同一功能模块配置的硬件大多都不是一个型号,当然这些是Android底层开发人员担心的问题,对于普通开发者来说,Android系统已经帮我们实现了所有的硬件驱动,我们若要控制硬件,只需调用相应的API即可方便的使用了,而无需关心底层驱动。众所周知,一个普通的Android手机都会搭载以下的硬件:震动器、闪光灯、各类传感器、定位模块、红外发射器、扬声器、麦克风、摄像头、蓝牙、NFC等等。那么本小结的P1将由易到难总结前五种硬件的控制方法,剩余的五种将在P2中总结。

运行时动态权限管理:
现在有的APP真是离谱,一个看电影的软件恨不得把所有的权限都获取了,如果拒绝,好家伙,直接强行退出了,真离谱。
在这里插入图片描述

关于APP的权限管理,用户可以从系统设置里更改,对于开发者来说,可以在APP运行过程中动态检查所需要的权限。
这个功能需要用到ContextCompat的方法:
public static int checkSelfPermission(Context context,String permission):检查是否拥有指定的permission,要求此permission必须在配置文件中声明。

ActivityCompat的方法:
public static void requestPermissions(final Activity activity,final String[] permissions, final int requestCode):向系统请求获取permissions数组里的权限,在指定的activity页面展示请求对话框。

Activity的方法:
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults):系统权限申请结果的回调方法。

动态权限管理是Android6.0引入的,所有要申请的权限都应在配置文件中申明,通用步骤是首先检查当前所用的Android版本是否是6.0以上,再调用ContextCompat的checkSelfPermission方法检查是否拥有权限,若没有,则可调用ActivityCompat的requestPermissions方法请求系统弹出权限申请对话框,最后开发者重写Activity的onRequestPermissionsResult方法来处理用户的允许/拒绝选择。
动态申请权限的通用方法如下:

    // 同时检查多个权限。返回true表示已完全启用权限,返回false表示未完全启用权限
    public static boolean checkMultiPermission(Activity act, String[] permissions, int requestCode) {
        boolean result = true;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//版本要>6.0
            int check = PackageManager.PERMISSION_GRANTED;
            // 通过权限数组检查是否都开启了这些权限
            for (String permission : permissions) {
                check = ContextCompat.checkSelfPermission(act, permission);
                if (check != PackageManager.PERMISSION_GRANTED) {
                    break;
                }
            }
            if (check != PackageManager.PERMISSION_GRANTED) {
                // 未开启该权限,则请求系统弹窗,好让用户选择是否立即开启权限
                ActivityCompat.requestPermissions(act, permissions, requestCode);
                result = false;
            }
        }
        return result;
    }
        @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {//获取成功
            //TODO:
            } else {
                //TODO:

一、震动器

1.简介

在这里插入图片描述
俗称马达,手机上搭载的马达通常分为两类:低端机搭载转子马达,高端机搭载线性马达。转子马达通电转,断电停,转动速度受电流控制,价格便宜。线性马达启停快,速度可调且具有方向性,价格较贵。
对于开发者来说,Android系统屏蔽了硬件差异,Vibrator是一个管理震动器的类,它的实例对象需要从系统服务中获取,即调用Context的getSystemService方法,通常使用三个方法来控制震动器:
public void vibrate(VibrationEffect vibe)
public void vibrate(VibrationEffect vibe, AudioAttributes attributes) :按照设置的 VibrationEffect 效果来震动,并且执行AudioAttributes 设置的声音效果。
public abstract void cancel() :取消震动。

VibrationEffect用于指定震动效果,有三个方法来创建该实例:
public static VibrationEffect createOneShot(long milliseconds, int amplitude) :创建震动一次的震动效果,两参数分别表示震动持续时间(mS)和振动幅度(0-255)。
public static VibrationEffect createWaveform(long[] timings, int repeat) :创建矩形波的震动效果,参数timings表示启停时间节点,即到达节点时震动器状态翻转。
public static VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) :创建自定义的波形震动,可以在指定时间段timings里设置对应的振动幅度amplitudes。

2.使用方法

对于硬件控制,都需要在配置文件AndroidManifest.xml中声明相关的权限,比如震动器,需添加:

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

在代码中只需两句代码即可控制:

		Vibrator vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);// 获取系统的Vibrator服务
		vibrator.vibrate(VibrationEffect.createOneShot(500, 200));// 控制手机振动500ms,振动幅度为200

二、闪光灯

1.简介

在这里插入图片描述
手机一般配置了两颗LED,分别发白光和黄光。对于这种结构简单的硬件,在代码里使用也很简单。
目前Android没有专门提供控制闪光灯的管理器类,不过我们可以使用相机管理器CameraManager来控制。使用CameraManager来控制相机的用法在下一P中总结,这里给出它与闪光灯有关的方法:
String[] getCameraIdList():返回当前手机可用的相机设备列表。
void setTorchMode(String cameraId, boolean enabled):在不打开相机的情况下,设置指定ID相机设备的闪光灯组件的模式。
CameraCharacteristics getCameraCharacteristics(String cameraId):查询相机设备的功能。

相机特征CameraCharacteristics是一个描述相机功能的类,与闪光灯有关的属性有:
public static final Key< Boolean> FLASH_INFO_AVAILABLE:该相机设备是否具有闪光灯组件,true表示有。
相关的方法有:
< T> T get(Key key):获取当前相机设备的指定属性值。

2.使用方法

通用流程如下:
①在配置文件声明闪光灯权限FLASHLIGHT,通过系统服务CAMERA_SERVICE获取相机管理利器CameraManager的实例对象,调用它的getCameraIdList方法获取所有可用的相机设备列表。
②遍历列表,调用CameraManager的getCameraCharacteristics方法获得每一个相机设备的功能CameraCharacteristics,调用它的get方法查询内部属性FLASH_INFO_AVAILABLE是否支持闪光灯。
③如果某一相机支持,则调用CameraManager对象的setTorchMode方法打开该相机设备的闪光灯。
代码模板如下:

    //设置闪光灯状态
    public static void setFlashEnable(Context context,boolean enable)
    {
         String[] cameraIdList;//可用的相机列表
        // 从系统服务中获取相机管理器
        CameraManager cMgr = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
        try {
            //获取可用的相机列表
            cameraIdList=cMgr.getCameraIdList();
            for (String camera :cameraIdList) {
                //获取相机属性
                CameraCharacteristics chara = cMgr.getCameraCharacteristics(camera);
                if (chara.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)){//相机如果支持FLASH组件
                    cMgr.setTorchMode(camera,enable);
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

三、各类传感器

1.简介

在这里插入图片描述
传感器能感受到规定的被测量,并按照一定的规律转换成可用数据,通常由敏感元件和转换电路组成。Android支持许多类型的传感器,但具体到一个手机,搭载的传感器就没那么多了。
传感器管理类SensorManager提供了如下常用方法管理传感器:
Sensor getDefaultSensor(int type) :获取指定类型的传感器。
List< Sensor > getSensorList(int type) :获取特定类型的可用传感器列表。
boolean registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs):以给定的采样频率为传感器注册监听器。
void unregisterListener(SensorEventListener listener, Sensor sensor) :取消注册传感器的监听器。

传感器Sensor是一个传感器的抽象表示,其常用方法:
String getName ():获取该传感器的名称。
int getType ():获取该传感器的类型。
一般用到的传感器类型如下:
TYPE_ALL:描述所有传感器类型。TYPE_ACCELEROMETER:加速度传感器类型。
TYPE_AMBIENT_TEMPERATURE:环境温度传感器类型。TYPE_GRAVITY:重力传感器类型。
TYPE_GYROSCOPE:陀螺仪传感器类型。TYPE_HEART_RATE:心率监视器类型。
TYPE_LIGHT:光线传感器类型。TYPE_LINEAR_ACCELERATION:线性加速度传感器类型。
TYPE_PRESSURE:压力传感器类型。TYPE_RELATIVE_HUMIDITY:相对湿度传感器类型。
TYPE_STEP_COUNTER:计步器传感器。TYPE_STEP_DETECTOR:步行检测器传感器。

传感器监听器SensorEventListener:当有新的传感器数据时,用于接收来自SensorManager的通知,提供了如下回调方法:
abstract void onAccuracyChanged(Sensor sensor, int accuracy):当注册传感器的精度发生变化时回调。
abstract void onSensorChanged(SensorEvent event):当有新的传感器事件时调用。

传感器事件SensorEvent :保存了传感器类型,时间戳,准确性,传感器采集的数据。
其属性值可直接访问:
public int accuracy:事件的准确性。
public Sensor sensor:生成此事件的传感器。
public long timestamp:事件发生的时间(nS)。
public final float[] values:采集到的数据。

传感器采集的数据使用的坐标系是相对于默认方向的手机屏幕(坐标原点在屏幕左上角)定义的,如图:
在这里插入图片描述
不同的传感器采集到的数据values数组大小不一样,说明如下:
①加速度传感器:返回三个值,分别代表加速度在X,Y,Z轴的分量(m/S^2)。
②陀螺仪传感器:返回三个值,分别代表手机绕X,Y,Z轴的旋转角度(rad)。
③磁场传感器:返回三个值,分别代表磁感应强度在X,Y,Z轴的分量(uT)。
④重力加速度传感器:返回三个值,分别代表重力加速度在X,Y,Z轴的分量。
⑤线性加速度传感器:返回三个值,分别代表加速度在X,Y,Z轴的分量(不含重力加速度)。
⑥温度,湿度,光照,压力,步行检测,步行计数传感器:返回一个值,代表相应标准单位的物理量。
获取了这些原始物理量数据,还需要一定的算法来处理这些数据才能开发出有用的APP。

2.使用方法

查看自己手机所搭载的传感器,可调用SensorManager的getSensorList方法,遍历获得的Sensor队列,调用Sensor对象的getType获取传感器类型,调用getName获取传感器名称。
对开发者来说,所有的传感器都采用相同的代码模板来获取数据,而且使用传感器也无需声明任何权限:
①通过系统服务SENSOR_SERVICE获取传感器管理器SensorManager的实例对象。
②调用该对象的getDefaultSensor获取指定类型的传感器。
③在Activity的onResume里调用该对象的registerListener注册传感器监听器,在onSensorChanged回调方法里获取采集的原始数据。
④在Activity的onPause里调用该对象的unregisterListener注销监听器。
那么基于以上流程,熟悉一下API吧:
页面布局如下:
在这里插入图片描述

由于获取传感器管理类,为每一个传感器注册/注销监听器很简单,故不再演示了,其他主要代码如下:

//获取支持的所有传感器
  private void showSensorInfo() {
        // 获取当前设备支持的传感器列表
        List<Sensor> sensorList = mSensorMgr.getSensorList(Sensor.TYPE_ALL);
        String sensorContent = "";
        for (Sensor sensor : sensorList) {
            String content = String.format("%s\n", sensor.getName());
            sensorContent += content;
        }
        tv_sensor.setText(sensorContent);
    }
    //当传感器精度改变时回调该方法,一般不做处理
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy)
    {
    }
    //当传感器数据改变时回调该方法
    @Override
    public void onSensorChanged(SensorEvent event)
    {
        float[] rawDates = event.values;
        // 获取触发event的传感器类型
        int sensorType = event.sensor.getType();
        switch (sensorType)
        {
            // 陀螺仪传感器
            case Sensor.TYPE_GYROSCOPE:
                String gyrContent = String.format("绕X轴旋转的角度:%f, 绕Y轴旋转的角度:%f,绕Z轴旋转的角度:%f",
                        rawDates[0],rawDates[1],rawDates[2]);
                tv_Gyro.setText(gyrContent);
                break;
            // 磁场传感器
            case Sensor.TYPE_MAGNETIC_FIELD:
                String magContent = String.format("X轴方向上的磁感应强度:%f, Y轴方向上的磁感应强度:%f,Z轴方向上的磁感应强度:%f",
                        rawDates[0],rawDates[1],rawDates[2]);
                tv_Magnetic.setText(magContent);
                break;
            // 重力传感器
            case Sensor.TYPE_GRAVITY:
                String graContent = String.format("X轴方向上的重力:%f, Y轴方向上的重力:%f,Z轴方向上的重力:%f",
                        rawDates[0],rawDates[1],rawDates[2]);
                tv_Gravity.setText(graContent);
                break;
            // 线性加速度传感器
            case Sensor.TYPE_LINEAR_ACCELERATION:
                String linearAccContent = String.format("X轴方向上的线性加速度:%f, Y轴方向上的线性加速度:%f,Z轴方向上的线性加速度:%f",
                        rawDates[0],rawDates[1],rawDates[2]);
                tv_LinearAcc.setText(linearAccContent);
                break;
            // 光传感器
            case Sensor.TYPE_LIGHT:
                String ligContent = String.format("当前光的强度为:%f",rawDates[0]);
                tv_Light.setText(ligContent);
                break;
            // 压力传感器
            case Sensor.TYPE_PRESSURE:
                String preContent = String.format("当前压力为:%f",rawDates[0]);
                tv_Pressure.setText(preContent);
                break;
        }
    }

那么效果如下:
在这里插入图片描述

四、定位模块

1.简介

在这里插入图片描述
手机的定位方法有网络定位和卫星定位两种,其中卫星定位包括中国的北斗,美国的GPS和俄国的格洛纳斯,网络定位包括基站定位和Wifi定位。虽然定位方法不同,不过对于开发者来说只需掌握几个类便可方便的开发与定位有关的应用了。
定位管理器LocationManager:该类提供对系统位置服务的管理,通过Context的getSystemService方法获取实例对象。它的常用方法如下:
List< String > getAllProviders() :返回所有已知的位置提供者的名称列表。
String getBestProvider(Criteria criteria, boolean enabledOnly):根据定位条件,返回最符合提供者的名称。
boolean isProviderEnabled(String provider):返回给定提供者是否可用。
void requestLocationUpdates(String provider, long minTime, float minDistance, PendingIntent intent):指定provider周期性获取位置信息,并通过intent启动相关组件。
Location getLastKnownLocation(String provider):返回一个给定位置提供者提供的最新的位。
void removeUpdates(LocationListener listener):删除指定的定位监听器。

位置提供者LocationProvider:有三种:GPS位置提供者GPS_PROVIDER,网络位置提供者NETWORK_PROVIDER,无法定位PASSIVE_PROVIDER。有常用方法如下:
String getName():返回此提供者的名称。
int getPowerRequirement():返回此提供者的电源要求。
boolean requiresCell()
boolean requiresNetwork()
boolean requiresSatellite()
:此提供者是否需要基站信息,访问数据网络,访问基于卫星的定位系统,需要则返回true,否则返回false。
boolean supportsAltitude()
boolean supportsBearing()
boolean supportsSpeed()
:提供者是否能够提供高度信息,方位信息,速度信息,能则返回true,否则返回false。

定位条件Criteria:用于设置定位的前提条件(如精度,速度,海拔,方位)来过滤定位提供者。常用方法如下:
void setAccuracy(int accuracy):设置定位精度,可取值:ACCURACY_FINE(高精度),ACCURACY_COARSE (一般)。 更准确的位置会消耗更多的电力,需要更长的时间。
void setSpeedAccuracy(int accuracy):设置所需的速度准确度,可取值:ACCURACY_LOW, ACCURACY_HIGH ,或NO_REQUIREMENT。
void setAltitudeRequired(boolean altitudeRequired):是否必须提供高度信息。
void setBearingRequired(boolean bearingRequired):是否需要方向信息。
void setSpeedRequired(boolean speedRequired):是否提供速度信息。
void setPowerRequirement(int level):设置所需的电源级别,可取值:NO_REQUIREMENT,POWER_LOW,POWER_MEDIUM或POWER_HIGH。

定位监听器LocationListener:用于监听定位事件,其有四个回调方法:
abstract void onLocationChanged(Location location):当位置发生变化时调用,可以在此处获取最新的位置信息。
abstract void onProviderDisabled(String provider):当提供者被用户禁用时调用。
abstract void onProviderEnabled(String provider):当提供者被用户启用时调用。
abstract void onStatusChanged(String provider, int status, Bundle extras):当提供者状态改变时调用。

位置Location:代表一个位置信息,常用方法如下:
float getAccuracy()
double getAltitude()
float getBearing()
double getLatitude()
double getLongitude()
float getSpeed()
long getTime():见名知意,分别获取定位精度,位置的高度,方向,纬度,经度,速度,时间戳。

2.使用方法

定位功能需要声明ACCESS_COARSE_LOCATION和ACCESS_FINE_LOCATION权限:

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

获取定位信息的步骤如下:
①获取LocationManager实例对象。
②使用该对象通过定位条件Criteria获取合适的位置提供者LocationProvider。
③使用LocationManager为LocationProvider注册定位监听器LocationListener。
④在LocationListener的回调方法中获取位置信息Location。

那么接下来举个例子来熟悉一下流程吧:布局界面只有一个TextView,activity中与定位有关的代码如下:

    private TextView tv_location;
    private String mLocation = "";
    private LocationManager mLocationMgr; // 声明一个定位管理器对象
    private Criteria mCriteria = new Criteria(); // 声明一个定位条件
    // 初始化定位服务
    private void initLocation() {
            //动态获取定位权限
        checkMultiPermission(this,new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.ACCESS_FINE_LOCATION},0);
        // 从系统服务中获取定位管理器
        mLocationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        // 设置定位精确度。Criteria.ACCURACY_COARSE表示粗略,Criteria.ACCURACY_FIN表示高精度
        mCriteria.setAccuracy(Criteria.ACCURACY_FINE);
        // 设置是否需要高度信息
        mCriteria.setAltitudeRequired(true);
        // 设置是否需要方位信息
        mCriteria.setBearingRequired(true);
        // 设置是否允许运营商收费
        mCriteria.setCostAllowed(true);
        // 设置对电源的需求
        mCriteria.setPowerRequirement(Criteria.POWER_LOW);
        // 获取定位管理器的最佳位置提供者
        String bestProvider = mLocationMgr.getBestProvider(mCriteria, true);
        if (mLocationMgr.isProviderEnabled(bestProvider)) { // 位置提供者当前可用
            tv_location.setText("正在由" + bestProvider + "提供定位服务");
            mLocation = String.format("定位类型=%s", bestProvider);
            beginLocation(bestProvider);
        } else { // 位置提供者暂不可用
            tv_location.setText("\n" + bestProvider + "定位不可用");
        }
    }
    // 开始定位
    private void beginLocation(String method) {
        // 检查当前设备是否已经开启了定位功能
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "请授予定位权限并开启定位功能", Toast.LENGTH_SHORT).show();
            return;
        }
        // 设置定位管理器的位置变更监听器
        mLocationMgr.requestLocationUpdates(method, 300, 0, mLocationListener);
    }
    // 定义一个位置变更监听器
    private LocationListener mLocationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            if (location != null) {
                String desc = String.format("%s\n定位对象信息如下: " + "\n\t其中经度:%f,纬度:%f" +
                                "\n\t其中高度:%d米,精度:%d米", mLocation,
                        location.getLongitude(), location.getLatitude(),
                        Math.round(location.getAltitude()), Math.round(location.getAccuracy()));
                tv_location.setText(desc);
            } else {
                tv_location.setText(mLocation + "\n暂未获取到定位对象");
            }
        }

        @Override
        public void onProviderDisabled(String arg0) {}

        @Override
        public void onProviderEnabled(String arg0) {}

        @Override
        public void onStatusChanged(String arg0, int arg1, Bundle arg2) {}
    };
    @Override
    protected void onDestroy() {
        if (mLocationMgr != null) {
            // 移除定位管理器的位置变更监听器
            mLocationMgr.removeUpdates(mLocationListener);
        }
        super.onDestroy();
    }

效果如下:
在这里插入图片描述

五、红外发射器

1.简介

在这里插入图片描述

红外遥控是一种无线、非接触式的控制技术,由红外发射器和红外接收器组成(发射器是透明的,另一个是黑色的,两者形状近似LED),采用近红外光传送遥控指令。手机上通常只搭载红外发射器,我们可以用来发射自定义的信号,也可以发送遥控器厂家规范的数据信号来实现模拟遥控器。

在实际的通信领域,原始信号一般有较宽的频谱,而且都是在比较低的频率段分布大量的能量,所以称之为基带信号,这种信号是不适合直接在信道中传输。为便于传输、提高抗干扰能力和有效的利用带宽,通常需要将信号调制到适合信道和噪声特性的频率范围内进行传输,这就叫做信号调制。在通信系统的接收端要对接收到的信号进行解调,恢复出原来的基带信号。
红外遥控器里的红外通信,通常是使用38KHz左右的载波进行调制的。就是用待传送信号去控制某个高频信号的幅度、相位、频率等参量变化的过程,即用一个信号去装载另一个信号。比如我们的红外遥控信号要发送的时候,先经过38K载波调制,如图所示:
在这里插入图片描述
红外信号调制原始信号就是我们要发送的一个数据“0”位或者一位数据“1”位,而所谓38K载波就是频率为38KHz的方波信号,调制后信号就是最终我们发射出去的波形。我们使用原始信号来控制38K载波,当信号是数据“0”的时候,38K载波毫无保留的全部发送出去,当信号是数据“1”的时候,不发送任何载波信号。
遥控器的基带通信协议很多,大概有几十种,常用的就有ITT协议、NEC协议、Sharp协议、Philips RC-5协议、Sony SIRC协议等,用的最多的就是NEC协议了。
NEC协议的数据格式包括了引导码、用户码、用户码(或者用户码反码)、按键键码和键码反码,均是 8 位数据格式, 按照低位在前, 高位在后的顺序发送。
在这里插入图片描述
NEC协议有很多的子协议,它的标准协议规定了:引导码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,数据编码总共是4个字节32位,第一个字节是用户码,第二个字节可能也是用户码,或者是用户码的反码,具体由生产商决定,第三个字节就是当前按键的按键键码,而第四个字节是键码反码,可用于对数据的纠错。
NEC 码的位定义: 一个脉冲对应 560us 的连续载波, 一个逻辑 1 传输需要2.25ms(560us 脉冲+1680us 低电平), 一个逻辑 0 的传输需要 1.125ms(560us脉冲+560us 低电平) 。

红外遥控管理器ConsumerIrManager:是Android提供的管理红外发射器的类,通过系统服务获取实例对象,有3个方法:
CarrierFrequencyRange[] getCarrierFrequencies():查询红外发射器支持的载波频率(Hz)。
boolean hasIrEmitter():检查手机是否有红外发射器。
void transmit(int carrierFrequency, int[] pattern):发射红外数据信号,carrierFrequency指定载波频率(Hz),pattern表示电平翻转的时间节点(uS),0uS到第一个节点表示低电平。

2.使用方法

其本身的使用方法很简单:声明红外权限TRANSMIT_IR–>获取ConsumerIrManager的实例对象–>调用其hasIrEmitter判断手机是否支持红外–>如果支持,则调用其transmit方法发送数据即可。
问题在于发什么样的数据,有以下两类使用场景:
①自定义的数据格式:发送未经编码的原始数据,比如pattern取值{100,200,300},那么在红外接收端也就得到一个100uS高电平+200uS低电平+300uS高电平的波形。
②规范的编码格式。比如美的空调遥控器采取的NEC的R05d子协议,网上找到说明书,先看波形规范:

在这里插入图片描述
再看编码规范,比如关机的编码:L,A,A’,B,B’,C,C’, S, L,A,A’,B,B’,C,C’, S, L,A,A’,Q,Q’,Y,Y’
在这里插入图片描述
说明书介绍的很详细,比如说立刻关机,则相应的字母取值:
L =4.4mS低电平+4.4mS高电平
A=1011 0010
A’=0100 1101
B=0111 1011
B’=1000 0100
C=1110 0000
C’=0001 1111
S :0.54mS低电平+ 5.22mS高电平
Q=0000 0000(0小时)
Q’=1111 1111
Y=0000 0000(0分钟)
Y’=1111 1111

那么举个简单的例子如下:
布局界面含有开关机按钮,主要代码如下:

 private ConsumerIrManager cim; // 声明一个红外遥控管理器对象
    // 初始化红外遥控管理器
    private void initInfrared() {
        // 从系统服务中获取红外遥控管理器
        cim = (ConsumerIrManager) getSystemService(Context.CONSUMER_IR_SERVICE);
        if (!cim.hasIrEmitter()) { // 判断当前设备是否支持红外功能
            Toast.makeText(this, "当前手机不支持红外遥控", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "当前手机支持红外遥控", Toast.LENGTH_SHORT).show();
        }
    }
    int zeroLow = 540;
    int zeroHigh = 540;
    int oneLow = 540;
    int oneHigh = 1620;
    int[] powerOnPattern = {
            //数据格式:L,A,A',B,B',C,C', S, L,A,A',B,B',C,C',
            4400,4400,  //L
            //1011 0010 A
            oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,          zeroLow,zeroHigh,zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,           //A
            //0100 1101 A'
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,      oneLow,oneHigh,oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,         //A'
            //1011 1111 B
            oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,          oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,        //B
            //0100 0000 B'
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,       zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,            //B'
            //1011 1111 C
            zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,    oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,           //C
            //0100 0000 C'
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,      zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,//C'
            540,5220,   //S
            4400,4400,  //L
            //1011 0010 A
            oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,          zeroLow,zeroHigh,zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,           //A
            //0100 1101 A'
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,      oneLow,oneHigh,oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,         //A'
            //1011 1111 B
            oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,          oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,        //B
            //0100 0000 B'
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,       zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,            //B'
            //1011 1111 C
            zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,    oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,           //C
            //0100 0000 C'
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,      zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,//C'
            540     //end
    };
    int[] powerOffPattern = {
            //数据格式:L,A,A',B,B',C,C', S, L,A,A',B,B',C,C', S, L,A,A',Q,Q',Y,Y'
            4400,4400,  //L=4.4mS低电平+4.4mS高电平
            //A=1011 0010
            oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh, zeroLow,zeroHigh,zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,
            //A'=0100 1101
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh, oneLow,oneHigh,oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,
            //B=0111 1011
            zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh, oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,
            //B'=1000 0100
            oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh, zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,
            //C=1110 0000
            oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,zeroLow,zeroHigh, zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,
            //C'=0001 1111
            zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,oneLow,oneHigh, oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,
            540,5220,   //S=0.54mS低电平+ 5.22mS高电平
            4400,4400,  //L
            //A=1011 0010
            oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh, zeroLow,zeroHigh,zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,
            //A'=0100 1101
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh, oneLow,oneHigh,oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,
            //B=0111 1011
            zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh, oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh,
            //B'=1000 0100
            oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh, zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,
            //C=1110 0000
            oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,zeroLow,zeroHigh, zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,
            //C'=0001 1111
            zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,oneLow,oneHigh, oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,
            540,5220,   //S
            //A=1011 0010
            oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,oneLow,oneHigh, zeroLow,zeroHigh,zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,
            //A'=0100 1101
            zeroLow,zeroHigh,oneLow,oneHigh,zeroLow,zeroHigh,zeroLow,zeroHigh, oneLow,oneHigh,oneLow,oneHigh,zeroLow,zeroHigh,oneLow,oneHigh,
            //Q=0000 0000(0小时)
            zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh, zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,
            //Q'=1111 1111
            oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh, oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,
            //Y=0000 0000(0分钟)
            zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh, zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,zeroLow,zeroHigh,
            //Y'=1111 1111
            oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh, oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,oneLow,oneHigh,
        540     //end
    };


    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_powerOn) {
            cim.transmit(38000, powerOnPattern);
        } else if (v.getId() == R.id.btn_powerOff) {
            cim.transmit(38000, powerOffPattern);
        }
        Toast.makeText(this, "已发送红外信号,看空调是否有反应", Toast.LENGTH_SHORT).show();
    }

效果如下:
在这里插入图片描述

对这种空调试了一下,按的时候听到“滴”的声音,能实现关机,但是不能开机,可能是开机参数哪里搞错了,唉,我也懒得弄了。
在这里插入图片描述


总结

本文总结了在Android系统中如何控制5种简单的硬件:震动器、闪光灯、各类传感器、定位模块和红外发射器。最后,明天是八月十五,祝大家中秋节快乐,阖家团圆!
在这里插入图片描述

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖工人_0803号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值