基于iTop4412的FM收音机系统设计(二)
说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计
现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:APP+Frameworks+JNI+HAL+Driver
整个系统设计,大致分为三篇文章介绍完毕,包括:
一、驱动设计篇
二、系统API接口篇
三、APP功能实现篇
---------------------------------------------------(二)系统接口篇-----------------------------------------------------------------
说明:关于系统接口,在FM系统中完全用不到这么复杂的流程,我这里是想把整个系统调用流程捋顺,所以使用了Frameworks(AIDL)->JNI->HAL
1.Frameworks
1.1.首先我们需设计好暴露给APP端的API接口,这里我们采用aidl的机制实现
进入到frameworks/base/core/java/android/os目录下,新建IFMService.aidl文件
- package android.os;
- interface IFMService {
- int getADC();
- int getFreq();
- void setFreq(int freq);
- void searchNextorPreFreq(int enable);
- void setNextorPreFreq(int enable);
- void enableMute(int enable);
- int getIsMute();
- void startAutoSearch();
- }
1.2.回到frameworks/base目录,编写编译规则,生成Stub接口文件
打开Android.mk文件,在LOCAL_SRC_FILES属性中添加
- LOCAL_SRC_FILES += \
- ...
- core/java/android/os/IFMService.aidl \
- ...
1.3.在frameworks/base目录下,使用mm命令编译IFMService.aidl生成IHelloService.Stub,在使用mm目录之前需要lunch系统的编译环境
Stub实质上就是binder通信,client<-->proxy<-->stub<-->service
1.4.进入到frameworks/base/services/java/com/android/server,新建FMService.java文件,继承IHelloService.Stub并实现接口函数
- package com.android.server;
- import android.content.Context;
- import android.os.IFMService;
- import android.util.Slog;
- public class FMService extends IFMService.Stub {
- private static final String TAG = "FMService";
- FMService() {
- Slog.d(TAG,"FM init...");
- init_native();
- }
- public int getADC(){
- return getADC_native();
- }
- public int getFreq(){
- return getFreq_native();
- }
- public void setFreq(int freq){
- setFreq_native(freq);
- return ;
- }
- public void searchNextorPreFreq(int enable){
- searchNextorPreFreq_native(enable);
- return ;
- }
- public void setNextorPreFreq(int enable){
- setNextorPreFreq_native(enable);
- return ;
- }
- public void enableMute(int enable){
- enableMute_native(enable);
- return ;
- }
- public int getIsMute(){
- return getIsMute_native();
- }
- public void startAutoSearch(){
- startAutoSearch_native();
- return ;
- }
- private static native boolean init_native();
- private static native int getADC_native();
- private static native int getFreq_native();
- private static native void setFreq_native(int freq);
- private static native void searchNextorPreFreq_native(int enable);
- private static native void setNextorPreFreq_native(int enable);
- private static native void enableMute_native(int enable);
- private static native int getIsMute_native();
- private static native void startAutoSearch_native();
- };
在这个文件中,我们把函数调用传递到了JNI
1.5.最后在Frameworks中我们需要启动我们的FMservice
进入frameworks/base/services/java/com/android/server目录,在SystemServer.java文件中添加
- class ServerThread extends Thread {
- @Override
- public void run() {
- if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- try {
- Slog.i(TAG, "DiskStats Service");
- ServiceManager.addService("diskstats", new DiskStatsService(context));
- } catch (Throwable e) {
- reportWtf("starting DiskStats Service", e);
- }
- try {
- Slog.i(TAG, "FM Service");
- ServiceManager.addService("fm5767", new FMService());
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting FM Service", e);
- }
- }
- }
- }
1.6.编译service(若修改了service的实现方法可模块编译service模块)
进入frameworks/base/services/java,使用命令mm进行编译,在out目录下的system/frameworks下生成services.jar,使用adb直接cp到板子上重启即可生效
2.JNI
2.1.实现JNi函数,函数传递到HAL层
进入frameworks/base/services/jni目录,新建com_android_server_FMService.cpp文件
- #define LOG_TAG "FMService_jni"
- #include "jni.h"
- #include "JNIHelp.h"
- #include "android_runtime/AndroidRuntime.h"
- #include <utils/misc.h>
- #include <utils/Log.h>
- #include <hardware/hardware.h>
- #include <hardware/hw_tea5767.h>
- #include <stdio.h>
- namespace android
- {
- /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/tea5767.h>*/
- struct tea5767_device_t* tea5767_device = NULL;
- //访问硬件的接口
- static jint tea5767_getADC(JNIEnv* env, jobject clazz){
- int adc = ;
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return adc;
- }
- adc = tea5767_device->getADC(tea5767_device);
- LOGI("get fm adc = %d",adc);
- return adc;
- }
- static jint tea5767_getFreq(JNIEnv* env, jobject clazz){
- int freq = ;
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return freq;
- }
- freq = tea5767_device->getFreq(tea5767_device);
- LOGI("get fm freq = %d",freq);
- return freq;
- }
- static void tea5767_setFreq(JNIEnv* env, jobject clazz, jint freq){
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return ;
- }
- tea5767_device->setFreq(tea5767_device,freq);
- LOGI("set fm freq = %d",freq);
- return ;
- }
- static void tea5767_searchNextorPreFreq(JNIEnv* env, jobject clazz, jint enable){
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return ;
- }
- tea5767_device->searchNextorPreFreq(tea5767_device,enable);
- LOGI("searchNextorPreFreq state = %d",enable);
- return ;
- }
- static void tea5767_setNextorPreFreq(JNIEnv* env, jobject clazz, jint enable){
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return ;
- }
- tea5767_device->setNextorPreFreq(tea5767_device,enable);
- LOGI("setNextorPreFreq state = %d",enable);
- return ;
- }
- static void tea5767_enableMute(JNIEnv* env, jobject clazz, jint enable){
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return ;
- }
- tea5767_device->enableMute(tea5767_device,enable);
- LOGI("enableMute state = %d",enable);
- return ;
- }
- static jint tea5767_getIsMute(JNIEnv* env, jobject clazz){
- int enable = ;
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return enable;
- }
- enable = tea5767_device->getIsMute(tea5767_device);
- LOGI("getIsMute state = %d",enable);
- return enable;
- }
- static void tea5767_autoSearch(JNIEnv* env, jobject clazz){
- if(!tea5767_device){
- LOGE("FM jni is not open..");
- return ;
- }
- tea5767_device->autoSearch(tea5767_device);
- LOGI("fm start autoSearch");
- return ;
- }
- /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
- static inline int tea5767_device_open(const hw_module_t* module, struct tea5767_device_t** device) {
- return module->methods->open(module, tea5767_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
- }
- /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
- static jboolean tea5767_init(JNIEnv* env, jclass clazz) {
- tea5767_module_t* module;
- LOGI("tea5767 JNI: initializing......");
- if(hw_get_module(tea5767_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == ) {
- LOGI("tea5767 JNI: tea5767 Stub found.");
- if(tea5767_device_open(&(module->common), &tea5767_device) == ) {
- LOGI("tea5767 JNI: tea5767 device is open.");
- return ;
- }
- LOGE("tea5767 JNI: failed to open tea5767 device.");
- return -;
- }
- LOGE("tea5767 JNI: failed to get tea5767 stub module.");
- return -;
- }
- /*JNI方法表*/
- static const JNINativeMethod method_table[] = {
- {"init_native", "()Z", (void*)tea5767_init},
- {"getADC_native", "()I", (void*)tea5767_getADC},
- {"getFreq_native", "()I", (void*)tea5767_getFreq},
- {"setFreq_native", "(I)V", (void*)tea5767_setFreq},
- {"searchNextorPreFreq_native", "(I)V", (void*)tea5767_searchNextorPreFreq},
- {"setNextorPreFreq_native", "(I)V", (void*)tea5767_setNextorPreFreq},
- {"enableMute_native", "(I)V", (void*)tea5767_enableMute},
- {"getIsMute_native", "()I", (void*)tea5767_getIsMute},
- {"startAutoSearch_native", "()V", (void*)tea5767_autoSearch},
- };
- /*注册JNI方法*/
- int register_android_server_FMService(JNIEnv *env) {
- return jniRegisterNativeMethods(env, "com/android/server/FMService", method_table, NELEM(method_table));
- }
- };
注意:1.在tea5767_init函数中,通过HAL层提供的hw_get_module方法来加载模块ID,即tea5767_HARDWARE_MODULE_ID的硬件抽象层模块,而这个ID是在<hardware/hw_tea5767.h>中定义的。HAL层会根据该ID的值在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。
2.在jniRegisterNativeMethods函数中,第二个参数的值必须对应FMService所在的包的路径,即com.android.server.FMService。
2.2.自动加载FM模块的jni方法
进入frameworks/base/services/jni目录中,编辑onload.cpp文件,添加属性
- namespace android {
- ...
- int register_android_server_FMService(JNIEnv *env);
- };
- extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
- {
- ...
- register_android_server_FMService(env);
- return JNI_VERSION_1_4;
- }
2.3 添加编译选项,编译Fm的JNI模块
进入frameworks/base/services/jni目录中,编辑Android.mk文件
- LOCAL_SRC_FILES:= \
- ...
- com_android_server_FMService.cpp \
- onload.cpp
2.4.编译
进入frameworks/base/services/jni目录,执行命令mm,编译,最后会在system/lib下生成libandroid_servers.so文件
3.HAL层
3.1.定义HAL层的接口
进入hardware/libhardware/include/hardware目录下,新建hw_tea5767.h文件
- #ifndef ANDROID_TEA5767_INTERFACE_H
- #define ANDROID_TEA5767_INTERFACE_H
- #include <hardware/hardware.h>
- __BEGIN_DECLS
- /*定义模块ID*/
- #define tea5767_HARDWARE_MODULE_ID "tea5767"
- /*硬件模块结构体*/
- struct tea5767_module_t {
- struct hw_module_t common;
- };
- /*硬件接口结构体*/
- struct tea5767_device_t {
- struct hw_device_t common;
- int fd;
- int (*getADC)(struct tea5767_device_t* dev);
- int (*getFreq)(struct tea5767_device_t* dev);
- void (*setFreq)(struct tea5767_device_t* dev, int freq);
- void (*searchNextorPreFreq)(struct tea5767_device_t* dev, int enable);
- void (*setNextorPreFreq)(struct tea5767_device_t* dev, int enable);
- void (*enableMute)(struct tea5767_device_t* dev, int enable);
- int (*getIsMute)(struct tea5767_device_t* dev);
- void (*autoSearch)(struct tea5767_device_t* dev);
- };
- __END_DECLS
- #endif
3.2.实现HAL层的方法
进入hardware/libhardware/modules目录下,新建hwfm/hwtea5767.c文件
- #define LOG_TAG "FM5767_Stub"
- #include <hardware/hardware.h>
- #include <hardware/hw_tea5767.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <cutils/log.h>
- #include <cutils/atomic.h>
- #define DEVICE_NAME "/dev/tea5767"
- #define MODULE_NAME "tea5767"
- #define MODULE_AUTHOR "pngcui"
- #define Search 3
- #define AutoSearch 4
- #define SETFREQ 5
- #define OPENMUTE 8
- #define CLOSEMUTE 9
- #define SHUTDOWN 10
- #define ADC 100
- #define FREQ 101
- /*设备打开和关闭接口*/
- static int tea5767_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
- static int tea5767_device_close(struct hw_device_t* device);
- /*设备访问接口*/
- static int fm_getADC(struct tea5767_device_t* dev);
- static int fm_getFreq(struct tea5767_device_t* dev);
- static void fm_setFreq(struct tea5767_device_t* dev, int freq);
- static void fm_searchNextorPreFreq(struct tea5767_device_t* dev, int enable);
- static void fm_setNextorPreFreq(struct tea5767_device_t* dev, int enable);
- static void fm_enableMute(struct tea5767_device_t* dev, int enable);
- static int fm_getIsMute(struct tea5767_device_t* dev);
- static void fm_autoSearch(struct tea5767_device_t* dev);
- /*模块方法表*/
- static struct hw_module_methods_t tea5767_module_methods = {
- open: tea5767_device_open
- };
- /*
- *
- *实例变量名必须为HAL_MODULE_INFO_SYM,tag也必须为HARDWARE_MODULE_TAG,这是Android硬件抽象层规范规定的。
- */
- /*模块实例变量*/
- struct tea5767_module_t HAL_MODULE_INFO_SYM = {
- common: {
- tag: HARDWARE_MODULE_TAG,
- version_major: ,
- version_minor: ,
- id: tea5767_HARDWARE_MODULE_ID,
- name: MODULE_NAME,
- author: MODULE_AUTHOR,
- methods: &tea5767_module_methods,
- }
- };
- static int tea5767_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
- struct tea5767_device_t* dev;
- dev = (struct tea5767_device_t*)malloc(sizeof(struct tea5767_device_t));
- if(!dev){
- LOGE("tea5767 Stub: failed to malloc space");
- return -EFAULT;
- }
- memset(dev, , sizeof(struct tea5767_device_t));
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = ;
- dev->common.module = (hw_module_t*)module;
- dev->common.close = tea5767_device_close;
- dev->getADC = fm_getADC;
- dev->getFreq = fm_getFreq;
- dev->setFreq = fm_setFreq;
- dev->searchNextorPreFreq = fm_searchNextorPreFreq;
- dev->setNextorPreFreq = fm_setNextorPreFreq;
- dev->enableMute = fm_enableMute;
- dev->autoSearch = fm_autoSearch;
- dev->getIsMute = fm_getIsMute;
- if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -) {
- LOGE("tea5767 Stub: failed to open /dev/tea5767 -- %s.", strerror(errno));
- free(dev);
- return -EFAULT;
- }
- *device = &(dev->common);
- LOGI("tea5767 Stub: open /dev/tea5767 successfully.");
- return ;
- }
- static int tea5767_device_close(struct hw_device_t* device) {
- struct tea5767_device_t* tea5767_device = (struct tea5767_device_t*)device;
- if(tea5767_device) {
- close(tea5767_device->fd);
- free(tea5767_device);
- }
- return ;
- }
- static int fm_getADC(struct tea5767_device_t* dev){
- LOGI("fm get ADC....");
- int ret = ioctl(dev->fd,ADC, );
- LOGI("ret = %d",ret);
- return ret;
- }
- static int fm_getFreq(struct tea5767_device_t* dev){
- LOGI("fm get fm_getFreq....");
- int ret = ioctl(dev->fd,, );
- LOGI("ret = %d",ret);
- return ret;
- }
- static void fm_setFreq(struct tea5767_device_t* dev, int freq){
- LOGI("fm get fm_setFreq....");
- int ret = ioctl(dev->fd,FREQ, );
- LOGI("ret = %d",ret);
- return ret;
- }
- static void fm_searchNextorPreFreq(struct tea5767_device_t* dev, int enable){
- LOGI("fm get fm_searchNextorPreFreq....");
- int ret = ioctl(dev->fd,Search, enable);
- LOGI("ret = %d",ret);
- return ret;
- }
- static void fm_setNextorPreFreq(struct tea5767_device_t* dev, int enable){
- LOGI("fm get fm_setNextorPreFreq....");
- int ret = ioctl(dev->fd,Search, enable);
- LOGI("ret = %d",ret);
- return ret;
- }
- static void fm_enableMute(struct tea5767_device_t* dev, int enable){
- LOGI("fm get fm_enableMute....");
- int ret;
- if(enable){
- ret = ioctl(dev->fd,OPENMUTE, );
- }else{
- ret = ioctl(dev->fd,CLOSEMUTE, );
- }
- LOGI("ret = %d",ret);
- return ret;
- }
- static int fm_getIsMute(struct tea5767_device_t* dev){
- LOGI("fm get fm_getIsMute....");
- int ret = ioctl(dev->fd,CLOSEMUTE, );
- LOGI("ret = %d",ret);
- return ret;
- }
- static void fm_autoSearch(struct tea5767_device_t* dev){
- LOGI("fm get fm_autoSearch....");
- int ret = ioctl(dev->fd,AutoSearch, );
- LOGI("ret = %d",ret);
- return ret;
- }
通过这个函数,实际上是操作了FM的文件设备,即/dev/tea5767,使用ioctl调用驱动程序中的函数,最终驱动起芯片工作。
3.3.添加编译选项
3.3.1.进入hardware/libhardware/modules/hwfm下,新建Android.mk文件
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
- LOCAL_SHARED_LIBRARIES := liblog
- LOCAL_SRC_FILES := hwtea5767.c
- LOCAL_MODULE := tea5767.smdk4x12
- LOCAL_MODULE_TAGS := optional
- include $(BUILD_SHARED_LIBRARY)
这里的LOCAL_MODULE的定义规则,我们可以根据hardware/libhardware/hardware.c文件中得出
- #define HAL_LIBRARY_PATH1 "/system/lib/hw"
- #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
- static const char *variant_keys[] = {
- "ro.hardware", /* This goes first so that it can pick up a different
- file on the emulator. */
- "ro.product.board",
- "ro.board.platform",
- "ro.arch"
- };
- int hw_get_module_by_class(const char *class_id, const char *inst,
- const struct hw_module_t **module)
- {
- int status;
- int i;
- const struct hw_module_t *hmi = NULL;
- char prop[PATH_MAX];
- char path[PATH_MAX];
- char name[PATH_MAX];
- if (inst)
- snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
- else
- strlcpy(name, class_id, PATH_MAX);
- /*
- * Here we rely on the fact that calling dlopen multiple times on
- * the same .so will simply increment a refcount (and not load
- * a new copy of the library).
- * We also assume that dlopen() is thread-safe.
- */
- /* Loop through the configuration variants looking for a module */
- for (i= ; i<HAL_VARIANT_KEYS_COUNT+ ; i++) {
- if (i < HAL_VARIANT_KEYS_COUNT) {
- if (property_get(variant_keys[i], prop, NULL) == ) {
- continue;
- }
- snprintf(path, sizeof(path), "%s/%s.%s.so",
- HAL_LIBRARY_PATH2, name, prop);
- if (access(path, R_OK) == ) break;
- snprintf(path, sizeof(path), "%s/%s.%s.so",
- HAL_LIBRARY_PATH1, name, prop);
- if (access(path, R_OK) == ) break;
- } else {
- snprintf(path, sizeof(path), "%s/%s.default.so",
- HAL_LIBRARY_PATH1, name);
- if (access(path, R_OK) == ) break;
- }
- }
- status = -ENOENT;
- if (i < HAL_VARIANT_KEYS_COUNT+) {
- /* load the module, if this fails, we're doomed, and we should not try
- * to load a different variant. */
- status = load(class_id, path, module);
- }
- return status;
- }
- int hw_get_module(const char *id, const struct hw_module_t **module)
- {
- return hw_get_module_by_class(id, NULL, module);
- }
所以LOCAL_MODULE的命名应该为name.prop,而prop的值为getprop ro.product.board的属性,itop4412平台上是smdk4x12,故LOCAL_MODULE为tea5767.smdk4x12
3.3.2.把hwfm文件夹添加到编译系统中
进入到hardware/libhardware/modules目录下,编辑Android.mk文件
- hardware_modules := gralloc hwcomposer audio nfc hwfm
- ifeq ($(BOARD_HAVE_MTK_MT6620),true)
- hardware_modules += gps
- endif
- include $(call all-named-subdir-makefiles,$(hardware_modules))
3.4.编译
进入hardware/libhardware/modules/hwfm下,执行命令mm,最后在/system/lib/hw下生成tea5767.smdk4x12.so文件
4.更改/dev/tea5767设备文件权限
由于设备文件是在内核驱动里面通过device_create创建的,而device_create创建的设备文件默认只有root用户可读写,而我们的APP一般不具有root权限,这时候就导致打开设备文件失败而报错:failed to open /dev/tea5767 -- Permission denied.
进入device/samsung/smdk4x12/conf目录,编辑init.smdk4x12.rc文件,添加
- chmod 0777 /dev/tea5767
最后可以完整的编译一次Android测试
至此,从Frameworks暴露给APP的接口到驱动的调用完成,后续会进行优化,欢迎大家指出错误与不足指出,非常感谢~~
完整工程代码下载:
https://github.com/pngcui/FM-radio
基于iTop4412的FM收音机系统设计(二)的更多相关文章
- 基于iTop4412的FM收音机系统设计(一)
说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...
- 基于iTop4412的FM收音机系统设计(三)
说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...
- FM收音机 RDS的强大功能
FM收音机 RDS的强大功能 分类: MTK2011-04-26 16:06 14889人阅读 评论(6) 收藏 举报 交通公告体育音乐娱乐教育 前言 随着发展,会有越来越多的电台具有RDS广播功能, ...
- 文献综述三:基于JSP的商品信息管理系统设计与开发
一.基本信息 标题:基于JSP的商品信息管理系统设计与开发 时间:2015 出版源:Computer Knowledge and Technology 文件分类:jsp技术的系统开发 二.研究背景 通 ...
- TEA5676 + AT24C08 FM收音机 搜台 存台 mmap 实现读写
硬件说明TEA5767 + AT24c08 要使用耳机收听,不加功放芯片,声音非常小. 这2个芯片都支持 3.3 或 5.0 电源支持连线比较简单,sda scl 接到 2440 对应的 排针上,找出 ...
- 基于web的图书管理系统设计与实现
原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述 图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...
- 基于web的图书管理系统设计与实现(附演示地址)
欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...
- Android RecyclerView单击、长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类
Android RecyclerView单击.长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类 我写的附录文章2,介绍了 ...
- 与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器
原文:与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器 [索引页][源码下载] 与众不同 windows phon ...
随机推荐
- 如何规范移动应用交互设计?UI/UX设计师须知的11个小技巧
以下内容由Mockplus团队翻译整理,仅供学习交流,Mockplus是更快更简单的原型设计工具. 十年前,手机的使用只是为了沟通. 而近几年,情况发生了很大变化,我们很难找到不使用手机的人.手机在极 ...
- Java设计模式(4)——单例模式
转载:http://wiki.jikexueyuan.com/project/java-design-pattern/singleton-pattern.html 单例模式根据实例化对象时机的不同分为 ...
- chorme 浏览器记住密码后input黄色背景处理方法(两种)
使用chrome浏览器选择记住密码的账号,输入框会自动加上黄色的背景,有些设计输入框是透明背景的,需要去除掉这个黄色的背景: 方法1:阴影覆盖 input:-webkit-autofill { - ...
- 关于int转char类型引发的一些思考
signed char unsigned char
- Linux gcc支持的语法 __attribute__ 属性设置
__attribute__实际上是gcc专有的一种语法,是用来设置函数属性.变量属性.类属性的 语法:之前在C中的结构体对齐中提到过,当时是用来告诉编译器这个结构体的对齐方式 ,其实他还有很多种用法, ...
- WPF 绑定备忘单
Part I – Common Examples Basic Binding {Binding} Bind to current DataContext. {Binding Name} Bi ...
- SpringMvc与Struts2的对比
目前企业中使用SpringMvc的比例已经远远超过Struts2,那么两者到底有什么区别,是很多初学者比较关注的问题,下面我们就来对SpringMvc和Struts2进行各方面的比较: 1.核心控制器 ...
- Low-level Thinking in High-level Shading Languages
因为要反汇编shader代码,所以google了数学函数_sat的知识,发现了一些高级着色语言的优化相关的问题.Low-level Thinking in High-level Shading Lan ...
- Javascript Object.defineProperty()
转载声明: 本文标题:Javascript Object.defineProperty() 本文链接:http://www.zuojj.com/archives/994.html,转载请注明转自Ben ...
- Solr相似度算法一:DefaultSimilarity(基于TF-IDF的默认相似度算法)
默认的similarity是基于TF/IDF 模块. 该 similarity有以下配置选项: discount_overlaps –确定是否重叠的标识(标记位置增量为0)都将被忽略在正常计算的时候. ...