简单的手电筒程序(基于系统驱动节点)
上一篇文章?简单的手电筒程序(基于Camera类实现)?介绍了通过Camera类进行Flash操作,但是通过camera接口可能会有下面两个问题: 1. 因为是间接使用camera接口,所以时间上会有延时 ;2. 进行Camera相关操作,耗电会比较大。下面我们介绍另外一种方法,通过操作驱动节点进行操作,控制闪关灯亮灭。 我使用的手机是三星G5309W,节点文件为/sys/class/camera/flash/rear_flash,操作如下表:
我估计并不是每个手机都适用,可以用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(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bulb_switch); tb = (ToggleButton)findViewById(R.id.BulbButton); tb.setOnCheckedChangeListener ( new OnCheckedChangeListener() { @Override public 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(); else TurnOff(); 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:workspaceBulbJNI)
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 __cplusplus extern "C" { #endif JNIEXPORT 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,__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,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 := eng LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) LOCAL_PRELINK_MODULE := false LOCAL_LDLIBS := -llog -lGLESv2 LOCAL_MODULE := libBulbJNI LOCAL_SRC_FILES :=BulbJNI.cpp include $(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-x86android-ndk-r9d c.?在C/C++ Build中点击Environment,点Add...添加环境变量NDKROOT,值为NDK的根目录 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 := eng LOCAL_DEX_PREOPT := false LOCAL_PRIVILEGED_MODULE := true LOCAL_PACKAGE_NAME := BulbJNI LOCAL_CERTIFICATE := platform LOCAL_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 运行效果如下:
源代码,请点击 这里下载,稍后会传到github~ 相关参考文档: http://jingyan.baidu.com/article/5d6edee22d908799eadeec9f.html http://www.voidcn.com/article/p-hzmhesmq-tu.html http://www.voidcn.com/article/p-elkommhb-md.html http://jingyan.baidu.com/article/5d6edee22d908799eadeec9f.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |