说明:第一版架构为: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收音机系统设计(二)的更多相关文章

  1. 基于iTop4412的FM收音机系统设计(一)

    说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...

  2. 基于iTop4412的FM收音机系统设计(三)

    说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...

  3. FM收音机 RDS的强大功能

    FM收音机 RDS的强大功能 分类: MTK2011-04-26 16:06 14889人阅读 评论(6) 收藏 举报 交通公告体育音乐娱乐教育 前言 随着发展,会有越来越多的电台具有RDS广播功能, ...

  4. 文献综述三:基于JSP的商品信息管理系统设计与开发

    一.基本信息 标题:基于JSP的商品信息管理系统设计与开发 时间:2015 出版源:Computer Knowledge and Technology 文件分类:jsp技术的系统开发 二.研究背景 通 ...

  5. TEA5676 + AT24C08 FM收音机 搜台 存台 mmap 实现读写

    硬件说明TEA5767 + AT24c08 要使用耳机收听,不加功放芯片,声音非常小. 这2个芯片都支持 3.3 或 5.0 电源支持连线比较简单,sda scl 接到 2440 对应的 排针上,找出 ...

  6. 基于web的图书管理系统设计与实现

    原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述     图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...

  7. 基于web的图书管理系统设计与实现(附演示地址)

    欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...

  8. Android RecyclerView单击、长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类

     Android RecyclerView单击.长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类 我写的附录文章2,介绍了 ...

  9. 与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器

    原文:与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器 [索引页][源码下载] 与众不同 windows phon ...

随机推荐

  1. DataStage 二、InfoSphere Information Server进程的启动和停止

    DataStage序列文章 DataStage 一.安装 1 关于进程需要了解的基础知识 名称 说明 ASB代理进程 通信代理程序,它的作用是协助层与层之间的通信,默认端口是31531,它以后台进程的 ...

  2. deploy: [mkdir] Created dir: C:\Program Files\Java\apache-cxf-2.4.2\samples\java_first_pojo\build [loadfile] Do not set property srcbuild.classpath as its length is 0.

    使用CXF的错误,错误是说我的路径有错误,因为路径错误所以无法运行程序 (1)原因,我将其放入了Program Files文件夹下,所以,其不好使 分析原因: 目录路径错误,目录中不能有空格,否则其解 ...

  3. Linux守护进程编写方法及原理

    什么守护进程? 守护进程是运行在后台的一种用来提供服务的进程,他脱离控制台独立运行,守护进程是一种很有用的进 程. Linux的大多数服务器就是用守护进程实现的.比如,Internet服务器inetd ...

  4. Web挖掘

    Web挖掘 Web挖掘的目标是从Web的超链接.网页内容和使用日志中探寻有用的信息.依据Web挖掘任务,可以划分为三种主要类型:Web结构挖掘.Web内容挖掘和Web使用挖掘.Web结构挖掘简单的说就 ...

  5. Thrift结构分析及增加取客户端IP功能实现

    目录 目录 1 1. 前言 1 2. 示例Service 1 3. 网络部分类图 2 4. 线程模式 3 4.1. IO线程 3 4.2. 工作线程 4 4.2.1. 工作线程类图 4 4.2.2.  ...

  6. 看图说说JVM新生代垃圾收集器

  7. swift - 动画学习

    // //  ViewController.swift //  MapAnimation // //  Created by su on 15/12/10. //  Copyright © 2015年 ...

  8. Android-XML与JSON的理解-JSON的数据格式

    据我了解,在多年以前浏览器客户端和服务器它们的通讯数据交互格式是XML, 使用XML来规定数据格式可读性确实非常强,XML的魅力确实很大,也很成熟,但是也有不足之处,就是在网络传输的时候对流量要求特别 ...

  9. 二道Const,readonly 和 override, new的面试题

    1. Const 和 readonly ; ; ; ; static void Main(string[] args) { Console.WriteLine("aa:{0},bb:{1}, ...

  10. centos 7.6 开机报错信息(一):welcome to emergency mode!

    welcome to emergency mode!after logging in ,type "journalctl -xb" to view system logs,&quo ...