BluetoothAdapter.java中有low enery(le)的一些方法,android提供了这些方法,但源码中并未找到这些方法的调用之处。本文档主要分析这类方法的执行流程,来了解下le到底做了些什么。

本文主要就是分析下startLeScan方法(两个重载方法)。

    public boolean startLeScan(LeScanCallback callback) {
return startLeScan(null, callback);
} public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); synchronized(mLeScanClients) {
if (mLeScanClients.containsKey(callback)) {
if (DBG) Log.e(TAG, "LE Scan has already started");
return false;
} try {
//获取BluetoothGattBinder类的实例,该类的定义在GattService.java中
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return false;
} UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
//重点分析该方法。作用是为本地设备进行注册,以及启动扫描
//wrapper是GattCallbackWrapper类的对象。该类注册了一些Gatt协议的回调方法
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
if (wrapper.scanStarted()) {
mLeScanClients.put(callback, wrapper);
return true;
}
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
return false;
}

下面来分析下iGatt.registerClient(new ParcelUuid(uuid), wrapper)方法,路径如下:(packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java::BluetoothGattBinder)

        public void registerClient(ParcelUuid uuid, IBluetoothGattCallback callback) {
GattService service = getService();
if (service == null) return;
service.registerClient(uuid.getUuid(), callback);
}

接着会调用GattService服务的同名方法

    void registerClient(UUID uuid, IBluetoothGattCallback callback) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid);
mClientMap.add(uuid, callback);
gattClientRegisterAppNative(uuid.getLeastSignificantBits(),
uuid.getMostSignificantBits());
}

接下来会调用jni层com_android_bluetooth_gatt.cpp文件中的gattClientRegisterAppNative方法。

static void gattClientRegisterAppNative(JNIEnv* env, jobject object,
jlong app_uuid_lsb, jlong app_uuid_msb )
{
bt_uuid_t uuid; if (!sGattIf) return;
set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
sGattIf->client->register_client(&uuid);
}

分析sGattIf->client->register_client(&uuid);语句

(1)sGattIf是一个静态变量,定义是static const btgatt_interface_t *sGattIf = NULL;

又是这种类型的变量。第一反应就是去找btgatt_interface_t结构体定义的头文件(一般在hardware目录),然后再搜索调用的c文件(一般在external/bluetooth/bluedroid,有时找到的c文件与头文件同名)。

btgatt_interface_t结构体的定义:hardware/libhardware/include/hardware/bt_gatt.h

/** Represents the standard Bluetooth GATT interface. */
typedef struct {
/** Set to sizeof(btgatt_interface_t) */
size_t size; /**
* Initializes the interface and provides callback routines
*/
bt_status_t (*init)( const btgatt_callbacks_t* callbacks ); /** Closes the interface */
void (*cleanup)( void ); /** Pointer to the GATT client interface methods.*/
const btgatt_client_interface_t* client; /** Pointer to the GATT server interface methods.*/
const btgatt_server_interface_t* server;
} btgatt_interface_t;

btgatt_interface_t结构体的对象:external/bluetooth/bluedroi/btif/src/btif_gatt.c

static const btgatt_interface_t btgattInterface = {
sizeof(btgattInterface), btif_gatt_init,
btif_gatt_cleanup, &btgattClientInterface,
&btgattServerInterface,
};

回到sGattIf->client->register_client(&uuid);语句,它调用了sGattIf结构体对象中的client对象的register_client函数,那么就是btgattClientInterface对象的register_client函数。

由结构体的定义可知client对象的类型是btgatt_client_interface_t结构体。同理分析可得以下结果,

btgatt_client_interface_t结构体的定义:hardware/libhardware/include/hardware/ bt_gatt_client.h

typedef struct {
/** Registers a GATT client application with the stack */
bt_status_t (*register_client)( bt_uuid_t *uuid ); /** Unregister a client application from the stack */
  bt_status_t (*unregister_client)(int client_if );
  ......
}

btgatt_client_interface_t结构体的对象:external/bluetooth/bluedroi/btif/src/btif_gatt_client.c

const btgatt_client_interface_t btgattClientInterface = {
btif_gattc_register_app,
btif_gattc_unregister_app,
btif_gattc_scan,
  ......
};

因此client->register_client就是调用了btif_gattc_register_app方法[-->btif_gatt_client.c]。

static bt_status_t btif_gattc_register_app(bt_uuid_t *uuid)
{
CHECK_BTGATT_INIT();
btif_gattc_cb_t btif_cb;
memcpy(&btif_cb.uuid, uuid, sizeof(bt_uuid_t));
return btif_transfer_context(btgattc_handle_event, BTIF_GATTC_REGISTER_APP,
(char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}

分析btgattc_handle_event函数

static void btgattc_handle_event(uint16_t event, char* p_param)
{
......
btif_gattc_cb_t* p_cb = (btif_gattc_cb_t*)p_param;
if (!p_cb) return; switch (event)
{
case BTIF_GATTC_REGISTER_APP:
btif_to_bta_uuid(&uuid, &p_cb->uuid);
//为uuid注册回调函数
BTA_GATTC_AppRegister(&uuid, bte_gattc_cback);
break;
.......
}
}

分析BTA_GATTC_AppRegister函数

-------------------------------------------------------------------------------------------------------

void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb)
{
tBTA_GATTC_API_REG *p_buf; /* register with BTA system manager */
  GKI_sched_lock();
  //注册Gatt客户端主事件处理函数bta_gattc_hdl_event,在bta_gatt_reg结构体中定义。
bta_sys_register(BTA_ID_GATTC, &bta_gatt_reg);
GKI_sched_unlock(); if ((p_buf = (tBTA_GATTC_API_REG *) GKI_getbuf(sizeof(tBTA_GATTC_API_REG))) != NULL)
{
p_buf->hdr.event = BTA_GATTC_API_REG_EVT;
if (p_app_uuid != NULL)
memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID));
p_buf->p_cback = p_client_cb; bta_sys_sendmsg(p_buf);
}
return;
}

(a)通过bta_sys_register函数注册了bta_gatt_reg结构体中定义的客户端主事件处理函数bta_gattc_hdl_event;然后设置event为BTA_GATTC_API_REG_EV,触发bta_gattc_hdl_event函数。

BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg)
{
tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
tBTA_GATTC_CLCB *p_clcb = NULL; #if BTA_GATT_DEBUG == TRUE
APPL_TRACE_DEBUG1("bta_gattc_hdl_event: Event [%s]", gattc_evt_code(p_msg->event));
#endif
switch (p_msg->event)
{
case BTA_GATTC_API_REG_EVT:
bta_gattc_register(p_cb, (tBTA_GATTC_DATA *) p_msg);
break;
......
  }
}

(b)调用bta_gattc_register函数。该函数用来注册一个客户端Gatt应用程序。

void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data)
{
......
/* callback with register event */
if (p_data->api_reg.p_cback)
{
(*p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT, (tBTA_GATTC *)&cb_data);
}
}

调用相关event(BTA_GATTC_REG_EVT)的回调函数。

到此,BTA_GATTC_AppRegister函数分析完毕,接下来分析BTA_GATTC_AppRegister(&uuid, bte_gattc_cback);中的参数部分。

ps:上述的回调函数就是这里的参数:bte_gattc_cback函数。那么BTA_GATTC_REG_EVT事件就调用该函数处理了。

-------------------------------------------------------------------------------------------------------

分析回调函数bte_gattc_cback

static void bte_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
{
bt_status_t status = btif_transfer_context(btif_gattc_upstreams_evt,
(uint16_t) event, (void*)p_data, sizeof(tBTA_GATTC), NULL);
ASSERTC(status == BT_STATUS_SUCCESS, "Context transfer failed!", status);
}

分析btif_gattc_upstreams_evt函数,在该函数中会处理BTA_GATTC_REG_EVT事件。

static void btif_gattc_upstreams_evt(uint16_t event, char* p_param)
{
tBTA_GATTC *p_data = (tBTA_GATTC*)p_param;
switch (event)
{
case BTA_GATTC_REG_EVT:
{
bt_uuid_t app_uuid;
bta_to_btif_uuid(&app_uuid, &p_data->reg_oper.app_uuid);
HAL_CBACK(bt_gatt_callbacks, client->register_client_cb
, p_data->reg_oper.status
, p_data->reg_oper.client_if
, &app_uuid
);
break;
}
    ......
  }
}

bt_gatt_callbacks对象的类型是btgatt_callbacks_t,其定义在hardware/libhardware/include/hardware/bt_gatt.h文件中。现在对bt_gatt_callbacks对象从头开始分析其来源。

在GattService.java::start()方法中,调用了initializeNative方法。继而调用JNI层initializeNative方法。贴出该方法。

static const btgatt_interface_t *sGattIf = NULL;
static const bt_interface_t* btIf;
......
static void initializeNative(JNIEnv *env, jobject object) {
/* getBluetoothInterface 函数返回sBluetoothInterface对象,在android4.3 bt 扫描分析.docx中已说明该对象的来源*/
if ( (btIf = getBluetoothInterface()) == NULL) {
error("Bluetooth module is not loaded");
return;
}
  ......
  //(a)
  // BT_PROFILE_GATT_ID的值是”gatt”
if ( (sGattIf = (btgatt_interface_t *)
btIf->get_profile_interface(BT_PROFILE_GATT_ID)) == NULL) {
error("Failed to get Bluetooth GATT Interface");
return;
}   bt_status_t status;
  //(b)
  /* sGattCallbacks的定义
  static const btgatt_callbacks_t sGattCallbacks = {
   sizeof(btgatt_callbacks_t),
   &sGattClientCallbacks,
   &sGattServerCallbacks
  };*/
if ( (status = sGattIf->init(&sGattCallbacks)) != BT_STATUS_SUCCESS) {
error("Failed to initialize Bluetooth GATT, status: %d", status);
sGattIf = NULL;
return;
} mCallbacksObj = env->NewGlobalRef(object);
}

(a) 分析

static const void* get_profile_interface (const char *profile_id)
{
......
#if BTA_GATT_INCLUDED == TRUE
if (is_profile(profile_id, BT_PROFILE_GATT_ID))
return btif_gatt_get_interface();
#endif
return NULL;
}

分析btif_gatt_get_interface函数

const btgatt_interface_t *btif_gatt_get_interface()
{
return &btgattInterface;
}

btgattInterface对象的类型是btgatt_interface_t结构体。再贴一遍该结构体的定义,如下:

typedef struct {
/** Set to sizeof(btgatt_interface_t) */
size_t size; /**
* Initializes the interface and provides callback routines
*/
bt_status_t (*init)( const btgatt_callbacks_t* callbacks ); /** Closes the interface */
void (*cleanup)( void ); /** Pointer to the GATT client interface methods.*/
const btgatt_client_interface_t* client; /** Pointer to the GATT server interface methods.*/
const btgatt_server_interface_t* server;
} btgatt_interface_t;

另,btgattInterface对象定义如下:

static const btgatt_interface_t btgattInterface = {
sizeof(btgattInterface), btif_gatt_init,
btif_gatt_cleanup, &btgattClientInterface,
&btgattServerInterface,
};

所以sGattIf 就是btgattInterface对象。

(b) 接下来调用sGattIf->init函数。由上可知,即为btif_gatt_init函数。

static bt_status_t btif_gatt_init( const btgatt_callbacks_t* callbacks )
{
  /*bt_gatt_callbacks由参数赋值,该参数是sGattCallbacks。
  sGattCallbacks的定义
  static const btgatt_callbacks_t sGattCallbacks = {
   sizeof(btgatt_callbacks_t),
   &sGattClientCallbacks,
   &sGattServerCallbacks
  };*/
bt_gatt_callbacks = callbacks; BTA_GATTC_Init();
BTA_GATTS_Init(); return BT_STATUS_SUCCESS;
}

到此为止,调用语句中bt_gatt_callbacks对象我们已经清楚了,就是sGattCallbacks对象。现在分析client->register_client_cb。

HAL_CBACK(bt_gatt_callbacks, client->register_client_cb
, p_data->reg_oper.status
, p_data->reg_oper.client_if
, &app_uuid
);

client对象是在btgatt_callbacks_t结构体中定义的一个变量,其初始化是在bt_gatt_callbacks对象(即sGattCallbacks对象)中。

btgatt_callbacks_t结构体如下:

typedef struct {
/** Set to sizeof(btgatt_callbacks_t) */
size_t size; /** GATT Client callbacks */
const btgatt_client_callbacks_t* client; /** GATT Server callbacks */
const btgatt_server_callbacks_t* server;
} btgatt_callbacks_t;

因此client对应的就是sGattCallbacks对象中的sGattClientCallbacks对象。sGattClientCallbacks对象定义如下(在JNI层的com_android_bluetooth_gatt.cpp文件中定义):

static const btgatt_client_callbacks_t sGattClientCallbacks = {
btgattc_register_app_cb,
btgattc_scan_result_cb,
......
};

而sGattClientCallbacks对象的类型是btgatt_client_callbacks_t结构体,如下

typedef struct {
register_client_callback register_client_cb;
scan_result_callback scan_result_cb;
connect_callback open_cb;
disconnect_callback close_cb;
......
} btgatt_client_callbacks_t;

因此,client->register_client_cb就是调用了sGattClientCallbacks 对象中的btgattc_register_app_cb函数。

void btgattc_register_app_cb(int status, int clientIf, bt_uuid_t *app_uuid)
{
CHECK_CALLBACK_ENV
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status,
clientIf, UUID_PARAMS(app_uuid));
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

JNI层的method_onClientRegistered 函数对应java层的onClientRegistered方法[-->GattService.java]。

    void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
throws RemoteException {
UUID uuid = new UUID(uuidMsb, uuidLsb);
if (DBG) Log.d(TAG, "onClientRegistered() - UUID=" + uuid + ", clientIf=" + clientIf);
ClientMap.App app = mClientMap.getByUuid(uuid);
if (app != null) {
app.id = clientIf;
app.linkToDeath(new ClientDeathRecipient(clientIf));
app.callback.onClientRegistered(status, clientIf);
}
}

此callback其实是GattCallbackWrapper类的对象。

分析mClientMap对象,在registerClient方法中调用了ClientMap的父类ContextMap::add方法,将GattCallbackWrapper类对象wrapper作为callback参数添加到mClientMap对象中。

接下来重新分析:

onClientRegistered方法[--->BluetoothAdapter::GattCallbackWrapper类]

        public void onClientRegistered(int status, int clientIf) {
if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status +
" clientIf=" + clientIf);
synchronized(this) {
if (mLeHandle == -1) {
if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled");
} if (status == BluetoothGatt.GATT_SUCCESS) {
mLeHandle = clientIf;
IBluetoothGatt iGatt = null;
try {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter != null) {
iGatt = adapter.getBluetoothManager().getBluetoothGatt();
//调用startLeScan方法时,传递过来的参数为null,执行此处
if (mScanFilter == null) {
iGatt.startScan(mLeHandle, false);
} else {
ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
for(int i = 0; i != uuids.length; ++i) {
uuids[i] = new ParcelUuid(mScanFilter[i]);
}
iGatt.startScanWithUuids(mLeHandle, false, uuids);
}
} else {
Log.e(TAG, "onClientRegistered, BluetoothAdapter null");
mLeHandle = -1;
}
} catch (RemoteException e) {
Log.e(TAG, "fail to start le scan: " + e);
mLeHandle = -1;
}
......
}

接下来分析startScan方法,在GattService.java中。

    void startScan(int appIf, boolean isServer) {
......
if (getScanClient(appIf, isServer) == null) {
if (DBG) Log.d(TAG, "startScan() - adding client=" + appIf);
mScanQueue.add(new ScanClient(appIf, isServer));
} gattClientScanNative(appIf, true);
}

JNI层gattClientScanNative函数

static void gattClientScanNative(JNIEnv* env, jobject object, jint clientIf, jboolean start)
{
if (!sGattIf) return;
sGattIf->client->scan(clientIf, start);
}

同之前分析register_client的步骤,分析的scan函数对应btif_gattc_scan函数。

static bt_status_t btif_gattc_scan( int client_if, bool start )
{
CHECK_BTGATT_INIT();
btif_gattc_cb_t btif_cb;
btif_cb.client_if = (uint8_t) client_if;
return btif_transfer_context(btgattc_handle_event, start ? BTIF_GATTC_SCAN_START : BTIF_GATTC_SCAN_STOP,
(char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}

在btgattc_handle_event函数中处理BTIF_GATTC_SCAN_START事件

        case BTIF_GATTC_SCAN_START:
btif_gattc_init_dev_cb();
// BTA_DmBleObserve发出消息,包含BTA_DM_API_BLE_OBSERVE_EVT事件
BTA_DmBleObserve(TRUE, 0, bte_scan_results_cb);
break;

调用bte_scan_results_cb函数,

static void bte_scan_results_cb (tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
{
btif_gattc_cb_t btif_cb;
uint8_t len; switch (event)
{
case BTA_DM_INQ_RES_EVT:
...... case BTA_DM_INQ_CMPL_EVT:
......
btif_transfer_context(btif_gattc_upstreams_evt, BTIF_GATT_OBSERVE_EVT,
(char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}

在btif_gattc_upstreams_evt函数中处理BTIF_GATT_OBSERVE_EVT事件。

        case BTIF_GATT_OBSERVE_EVT:
{
btif_gattc_cb_t *p_btif_cb = (btif_gattc_cb_t*)p_param;
if (!btif_gattc_find_bdaddr(p_btif_cb->bd_addr.address))
{
btif_gattc_add_remote_bdaddr(p_btif_cb->bd_addr.address, p_btif_cb->addr_type);
btif_gattc_update_properties(p_btif_cb);
}
HAL_CBACK(bt_gatt_callbacks, client->scan_result_cb,
&p_btif_cb->bd_addr, p_btif_cb->rssi, p_btif_cb->value);
break;
}

同分析register_client_cb函数,在JNI层com_android_bluetooth_gatt.cpp文件中定义,分析得scan_result_cb对应函数btgattc_scan_result_cb。

void btgattc_scan_result_cb(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data)
{
......
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult
, address, rssi, jb);
......
}

对应java层文件GattService类onScanResult方法。

    void onScanResult(String address, int rssi, byte[] adv_data) {
for (ScanClient client : mScanQueue) {
......
if (!client.isServer) {
ClientMap.App app = mClientMap.getById(client.appIf);
if (app != null) {
try {
app.callback.onScanResult(address, rssi, adv_data);
} catch (RemoteException e) {
Log.e(TAG, "Exception: " + e);
mClientMap.remove(client.appIf);
mScanQueue.remove(client);
}
}
}
......
}
  }

callback为GattCallbackWrapper类的对象,因此调用GattCallbackWrapper类中的onScanResult方法。

        public void onScanResult(String address, int rssi, byte[] advData) {
......
try {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter == null) {
Log.d(TAG, "onScanResult, BluetoothAdapter null");
return;
}
mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
}

mLeScanCb对象为LeScanCallback接口的对象,不过源码中并没有类来实现该接口,故只能分析到这里了。扫描到此结束,over~~

-------------------------------------------------------------------------------------------------------------

贴出流程图,see see,5个步骤:

1.startLeScan(JAVA-->JNI)

-------------------------------------------------------------------------------------------------------------

2.startLeScan(蓝牙栈)

-------------------------------------------------------------------------------------------------------------

3.startLeScan(JNI-->JAVA)

-------------------------------------------------------------------------------------------------------------

4.startLeScan(蓝牙栈)

-------------------------------------------------------------------------------------------------------------

5.startLeScan(JNI-->JAVA)

-------------------------------------------------------------------------------------------------------------

android4.3 Bluetooth(le)分析之startLeScan分析的更多相关文章

  1. ZT Android4.2关于bluetooth在HAL层的分析(1)

    我的电子杂烩饭 http://blog.sina.com.cn/wuchuchu2012 [订阅][手机订阅] 首页 博文目录 图片 关于我 正文 字体大小:大 中 小 Android4.2关于blu ...

  2. Bluetooth LE(低功耗蓝牙) - 第三部分

    回顾 在本系列的前两篇文章中,我们已经了解了一些关于Bluetooth LE的背景并建立一个简单的Activity / Service框架.   在这篇文章中,我们将探讨Bluetooth LE的细节 ...

  3. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

  4. Bluetooth LE(低功耗蓝牙) - 第二部分

    回顾 在前面的文章中我们介绍了Bluetooth LE的背景也说明了我们在本系列文章中将要开发什么,但是还没有实际的代码.我们将在这篇文章中纠正这一点,我们将通过定义 Service/Activity ...

  5. Bluetooth LE(低功耗蓝牙) - 第一部分

    前言 在写这篇文章的时候,谷歌刚刚发布了Android Wear ,摩托罗拉也发布了 Moto 360 智能手表.Android Wear的API还是相当基本的,是很好的文档材料,而且还会不断的更新, ...

  6. 低功耗之战!ANT VS Bluetooth LE

    利用近距离无线通信技术将手机及可穿戴式传感器终端等与智能电话连接起来,实现新的功能.最近,以此为目标的行动正在展开.其中备受关注的近距离无线方式是“ANT”和“Bluetooth LE”.为了在各种便 ...

  7. Memcached源代码分析 - Memcached源代码分析之消息回应(3)

    文章列表: <Memcached源代码分析 - Memcached源代码分析之基于Libevent的网络模型(1)> <Memcached源代码分析 - Memcached源代码分析 ...

  8. mysql 分析3使用分析sql 性能 show profiles ;

    show variables like '%profiling%';    查看状态  查看时间去哪了``` set  profiling=1;// 打开 show profiles;  查看执行过的 ...

  9. [转载] 常用 Java 静态代码分析工具的分析与比较

    转载自http://www.oschina.net/question/129540_23043 简介: 本文首先介绍了静态代码分析的基本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代 ...

随机推荐

  1. 找出linux服务器IO占用高的程序

     一台服务器比较性能无外乎内存.cpu使用率.IO使用率,把这3样优化好了,你服务器的负载就要小很多,当然网络情况不在我的考虑范围,毕竟网络这个情况是很不稳定,就算你服务器上把网络优化得再好,idc不 ...

  2. BlockingQueue深入分析

    1.BlockingQueue定义的常用方法如下   抛出异常 特殊值 阻塞 超时 插入 add(e) offer(e) put(e) offer(e,time,unit) 移除 remove() p ...

  3. c++ 中__declspec 的用法

    __declspec ( extended-decl-modifier-seq )扩展修饰符:1:align(#)    用__declspec(align(#))精确控制用户自定数据的对齐方式 ,# ...

  4. 保持const和non-const函数代码的一致

    在用C++进行面向对象编程的时候,有时需要在一个类里包含两个代码相似的函数,而它们之间的唯一区别是,一个为const类型,一个是non-const类型. 此时如果将相同的代码写在两个函数中,则严重违反 ...

  5. EF 示例

    EF有三种数据库访问方式,这里只介绍Code First. 1.DB First,类似Linq to sql中拖拽一个DB到方案中 2.Model First,没试过,不能自动生成数据库只能生成表 3 ...

  6. 谈谈我对DSP和FPGA的看法

    1.DSP 在DSP里,你是一个软件设计者,硬件已经完全固化,你所要做的,就是在这个固定的硬件平台实现算法改进与优化, DSP的关键优势在于能够运行多种算法的灵活性: 2.FPGA 对于FPGA来说, ...

  7. 云存储性能测试工具--COSBench安装

    COSBench安装 Cosbench是Intel的开源云存储性能测试软件,COSBench目前已经广泛使用与云存储测试,并作为云存储的基准测试工具使用 1 环境 1.1 操作系统 COSBench可 ...

  8. Clustering with the ArcGIS API for Flex

    Clustering is an excellent technique for visualizing lotss of point data. We've all seen application ...

  9. Jmeter组件3. HTTP Cookie Manager

    两个坑的地方 如果一个域(scope)内有两个cookie manager,Jmeter说,我分不清了,你自己看着办吧,所以不要没事找事,一个域内一个cookie manager够了 用户自定义coo ...

  10. 30. PL/SQL Developer连接服务器查询时,数据乱码处理

    在windows中创 建一个名为“NLS_LANG”的系统环境变量,设置其值为"AMERICAN_AMERICA.ZHS16GBK",   NLS_LANG的值为:select u ...