简单的手电筒程序(基于系统驱动节点)

来源:互联网 发布:java移植安卓 编辑:程序博客网 时间:2024/05/01 00:58

上一篇文章 简单的手电筒程序(基于Camera类实现) 介绍了通过Camera类进行Flash操作,但是通过camera接口可能会有下面两个问题: 1. 因为是间接使用camera接口,所以时间上会有延时 ;2. 进行Camera相关操作,耗电会比较大。下面我们介绍另外一种方法,通过操作驱动节点进行操作,控制闪关灯亮灭。

我使用的手机是三星G5309W,节点文件为/sys/class/camera/flash/rear_flash,操作如下表:

Flash On写入 “1”Flash Off写入 “0”

我估计并不是每个手机都适用,可以用adb命令 “echo "1" >/sys/class/camera/flash/rear_flash ”验证下你的手机是否支持,当然,前提是root的,因为是system节点,节点的权限如下


如果你发现你的手机支持上面的节点,哈哈,很高兴和可以和你继续share这个有趣的应用,如果没有这个节点,也不用桑心,下面涉及JNI操作,如果你对这个知识点有兴趣也是有所裨益。

大概的思路 : JAVA层通过JNI调用Native层函数进行节点操作

1. Java层的代码比较简单,主要是声明以及调用native函数

package com.saberhao.bulbjni;import android.os.Bundle;import android.app.Activity;import android.graphics.drawable.Drawable;import android.view.Menu;import android.widget.CompoundButton;import android.widget.ImageView;import android.widget.ToggleButton;import android.widget.CompoundButton.OnCheckedChangeListener;public class BulbSwitch extends Activity {ToggleButton tb;static {    // load the JNI so    System.loadLibrary("BulbJNI");      }//declare the JNI function.plz pay attention to the key word -- "native"public native void TurnOn();public native void TurnOff();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_bulb_switch);tb = (ToggleButton)findViewById(R.id.BulbButton);tb.setOnCheckedChangeListener        (           new OnCheckedChangeListener()           {@Overridepublic void onCheckedChanged(CompoundButton buttonView,boolean isChecked){setBulbState(isChecked);}           }          ); }protected void setBulbState(boolean isChecked) {// TODO Auto-generated method stub      ImageView iv=(ImageView)findViewById(R.id.ImageView);iv.setImageResource((isChecked)?R.drawable.bulb_on:R.drawable.bulb_off);if(isChecked)TurnOn();elseTurnOff();ToggleButton tb=(ToggleButton)findViewById(R.id.BulbButton);        tb.setChecked(isChecked);}  private void releaseImageViews() {ImageView iv=(ImageView)findViewById(R.id.ImageView);    releaseImageView(iv);  }  private void releaseImageView(ImageView imageView) {    Drawable d = imageView.getDrawable();    if (d != null)      d.setCallback(null);    imageView.setImageDrawable(null);    imageView.setBackgroundDrawable(null);  }}

2. 生成native h头文件

a .通过命令行进入工程目录(D:\workspace\BulbJNI)

b. 输入如下命令编译h头文件
javah -classpath bin /classes -d jni com.saberhao.bulbjni.BulbSwitch

-classpath ——类路径 bin/classes
-d ——保存目录:jni
com.android.jni.JniTest:包名+类名

通过上面步骤就可以在JNI文件夹下得到Native定义头文件 : com_saberhao_bulbjni_BulbSwitch.h
#include <jni.h>/* Header for class com_saberhao_bulbjni_BulbSwitch */#ifndef _Included_com_saberhao_bulbjni_BulbSwitch#define _Included_com_saberhao_bulbjni_BulbSwitch#ifdef __cplusplusextern "C" {#endifJNIEXPORT void JNICALL Java_com_saberhao_bulbjni_BulbSwitch_TurnOn  (JNIEnv *, jobject);JNIEXPORT void JNICALL Java_com_saberhao_bulbjni_BulbSwitch_TurnOff  (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif

当然,也可以通过NDK生成头文件,详情请参考文末的参考文献

3.编写Native处理函数

在JNI目录下新建一个Cpp文件,用于实现Native函数,这个也是本应用最重要的一步

#include<jni.h>#include<stdio.h>#include <fcntl.h>#include<sys/types.h>#include<sys/stat.h>#include"com_saberhao_bulbjni_BulbSwitch.h"#include <android/log.h>#include <unistd.h>#define LOG_TAG "BulbJNI"#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)#define TRUNON "1"#define TRUNOFF "0"FILE *fp;int fd;int fd0;JNIEXPORT void JNICALL Java_com_saberhao_bulbjni_BulbSwitch_TurnOn  (JNIEnv *, jobject)  {fd=open("/sys/class/camera/flash/rear_flash",O_WRONLY);if(fd < 0){LOGE("[turn on]open device error ");}else{LOGI("turn on the flash");write(fd,TRUNON,sizeof(TRUNON));}close(fd);if(fd0 < 0){LOGE("[turn on]open test0 device error ");}else{//rewind(fp);LOGI("turn on test0 the flash");write(fd0,TRUNON,sizeof(TRUNON));}close(fd0);  }JNIEXPORT void JNICALL Java_com_saberhao_bulbjni_BulbSwitch_TurnOff  (JNIEnv *, jobject)  {fd=open("/sys/class/camera/flash/rear_flash",O_WRONLY);if(fd < 0){LOGE("[turn off]open device error ");}else{LOGI("turn off the flash");write(fd,TRUNOFF,sizeof(TRUNOFF));}close(fd);  }
这部分代码主要实现对节点的操作,也是本应用的核心,有一点是需要注意的,上文提到sys节点的权限,对同个所有者或者同个group是可写的,其他不赋予权限,所以在定义句柄fd的时候必须使用O_WRONLY(只写),而不能使用到 O_RDWR(可读可写),要不然会出现fd初始化失败的情况,这个问题也纠结了我很久~

4.编写JNI mk文件

native函数提供实现方法,需要编译成so文件,才能被JAVA层调用,在JNI文件夹下创建Android.mk

LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := engLOCAL_C_INCLUDES := \$(JNI_H_INCLUDE)LOCAL_PRELINK_MODULE := falseLOCAL_LDLIBS := -llog -lGLESv2LOCAL_MODULE := libBulbJNILOCAL_SRC_FILES :=BulbJNI.cppinclude $(BUILD_SHARED_LIBRARY)


LOCAL_LDLIBS := -llog -lGLESv2         //在Native函数中打log必须添加的lib

LOCAL_MODULE_TAGS := eng           //在Eng模式下编译

LOCAL_SRC_FILES -编译的源文件

LOCAL_MODULE -编译的目标对象


5. 编译生成so文件

a. 配置NDK

打开Eclipse,点Window->Preferences->Android->NDK,设置NDK路径,例如D:\android-ndk-r9d-windows-x86\android-ndk-r9d

b. 点Properties->C/C++ Build的Building Settings中去掉Use default build command,然后输入${NDKROOT}/ndk-build.cmd

c. 在C/C++ Build中点击Environment,点Add...添加环境变量NDKROOT,值为NDK的根目录

全部编译之后,就可以再libs目录下看到对应的so文件


6. 添加app mk文件

由于节点 /sys/class/camera/flash/rear_flash 为系统节点,普通应用无法直接操作,需要在xml中添加

android:sharedUserId="android.uid.system"

同时需要在 app的android.mk中添加

LOCAL_CERTIFICATE := platform 

才能进行操作,但是eclipse中无法添加Android.mk,所以必须在源代码环境中编译生成apk,添加的mk如下

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := engLOCAL_DEX_PREOPT := falseLOCAL_PRIVILEGED_MODULE := trueLOCAL_PACKAGE_NAME := BulbJNILOCAL_CERTIFICATE := platformLOCAL_SRC_FILES := $(call all-subdir-java-files)include $(BUILD_PACKAGE)################################################### Use the folloing include to make our test apk.include $(call all-makefiles-under,$(LOCAL_PATH))
最后,在源码中使用mm进行部分编译即可生成apk

运行效果如下:

Flash OnFlash Off
源代码,请点击这里下载,稍后会传到github~


相关参考文档:

http://jingyan.baidu.com/article/5d6edee22d908799eadeec9f.html

http://blog.csdn.net/sdvch/article/details/17492861

http://blog.csdn.net/vincent_czz/article/details/7199567

http://jingyan.baidu.com/article/5d6edee22d908799eadeec9f.html


0 0