Android 6.0一个完整的native service
上一篇博客《Android 6.0 如何添加完整的系统服务(app-framework-kernel)》http://www.cnblogs.com/hackfun/p/7418902.html
介绍了如何添加一个系统服务,客户端和服务端都是基于JAVA实现的OpersysService。经过进一步的学习,我将
演示如何使用C++实现一个相同功能的系统服务hfnativeservice。为了兼容OpersysService,将保留Opersys-
Service服务端中的HAL和driver,供hfnativeservice使用,即OpersysService和hfnativeservice这两个Service
都是用相同的HAL和driver。其中,hfnativeservice增加了一个服务端死亡通知机制,即hfnative-service的服
务端进程被杀掉时,客户端会收到这个通知,并做相应的清理工作。
主要围绕以下几个步骤添加一个完整的C++系统服务:
(A) 添加HAL和Driver
(B) 添加服务接口,生成动态库
(C) 添加服务端
(D) 注册服务端
(E) 添加客户端
(F) 测试
(A) 添加HAL和Driver
这部分参考上一篇博客《Android 6.0 如何添加完整的系统服务(app-framework-kernel)》的
(A) 添加circular-char驱动
(B) 添加opersyshw_qemu HAL
(B) 添加服务接口,生成动态库
为了对外只提供服务端或客户端的接口,这里把客户端和服务端之间的通信实现细节放在一起,生成动态库so
文件,服务端和客户端在使用的时候,加载这个so就可以了。IHfNativeService.cpp对客户端和服务端提供了相同
的接口,并实现了proxy和native之间的Binder通信细节。HfNativeManager.cpp根据IHfNativeService.cpp提供的
接口,进一步封装,隐藏了客户端的是操作细节,如服务的获取,注册死亡通知等。
相关头文件:
frameworks/native/include/hfnative/HfNativeManager.h
#ifndef ANDROID_HACKFUN_HACKFUN_NATIVE_SERVICE_H
#define ANDROID_HACKFUN_HACKFUN_NATIVE_SERVICE_H #include <stdint.h>
#include <sys/types.h> #include <binder/IBinder.h> #include <utils/RefBase.h>
#include <utils/Singleton.h>
#include <utils/threads.h>
#include <hfnative/IHfNativeService.h> namespace android {
// --------------------------------------------------------------------------- class HfNativeManager : public Singleton<HfNativeManager>
{
public:
HfNativeManager();
~HfNativeManager(); int init_hfnative(void);
void deinit_hfnative(void);
int read_queue(char *buff, int len);
int write_queue(char *buff, int len);
int test_hfnative(int value); status_t assertState();
bool checkService() const;
void resetServiceStatus(); private:
bool isDied;
// DeathRecipient interface
void hfNativeServiceDied(); mutable sp<IHfNativeService> mHfNativeServer;
mutable sp<IBinder::DeathRecipient> mDeathObserver;
}; }; // namespace android #endif // ANDROID_HACKFUN_HACKFUN_NATIVE_SERVICE_H
frameworks/native/include/hfnative/IHfNativeService.h
#ifndef ANDROID_HACKFUN_HACKFUN_COMPOSER_CLIENT_H
#define ANDROID_HACKFUN_HACKFUN_COMPOSER_CLIENT_H #include <stdint.h>
#include <sys/types.h> #include <utils/Errors.h>
#include <utils/RefBase.h> #include <binder/IInterface.h> namespace android {
// ---------------------------------------------------------------------------- class IHfNativeService : public IInterface
{
public:
DECLARE_META_INTERFACE(HfNativeService); virtual int init_native(void) = ;
virtual void finalize_native(void) = ;
virtual int read_native(char *Buff, int Len) = ;
virtual int write_native(char *Buff, int Len) = ;
virtual int test_native(int value) = ;
}; // ---------------------------------------------------------------------------- class BnHfNativeService: public BnInterface<IHfNativeService> {
public:
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = );
}; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_HACKFUN_HACKFUN_COMPOSER_CLIENT_H
源文件:
frameworks/native/libs/hfnative/IHfNativeService.cpp
#define LOG_TAG "HfNativeService" #include <stdio.h>
#include <stdint.h>
#include <malloc.h>
#include <sys/types.h> #include <binder/Parcel.h>
#include <binder/IMemory.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <hfnative/IHfNativeService.h> namespace android { enum {
INIT_NATIVE = IBinder::FIRST_CALL_TRANSACTION,
FINALIZE_NATIVE,
READ_NATIVE,
WRITE_NATIVE,
TEST_NATIVE
}; class BpHfNativeService : public BpInterface<IHfNativeService>
{
public:
BpHfNativeService(const sp<IBinder>& impl)
: BpInterface<IHfNativeService>(impl)
{
} int init_native(void)
{
Parcel data, reply; data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
remote()->transact(INIT_NATIVE, data, &reply); return (int)reply.readInt32();
} void finalize_native(void)
{
Parcel data, reply; data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
remote()->transact(FINALIZE_NATIVE, data, &reply);
} int read_native(char *Buff, int Len)
{
Parcel data, reply; data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
data.writeInt32(Len);
remote()->transact(READ_NATIVE, data, &reply);
reply.read((void *)Buff, (size_t)Len);
return (int) reply.readInt32();
} int write_native(char *Buff, int Len)
{
Parcel data, reply; data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
data.writeInt32(Len);
data.write((const void *)Buff, (size_t)Len);
remote()->transact(WRITE_NATIVE, data, &reply);
return (int) reply.readInt32();
} int test_native(int value)
{
Parcel data, reply; data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
data.writeInt32(value);
remote()->transact(TEST_NATIVE, data, &reply);
return (int) reply.readInt32();
}
}; IMPLEMENT_META_INTERFACE(HfNativeService, "android.hfnative.HfNativeService"); status_t BnHfNativeService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
char *buff;
int len, retval;
status_t status; switch(code) {
case INIT_NATIVE:
CHECK_INTERFACE(IHfNativeService, data, reply);
retval = init_native();
reply->writeInt32(retval);
return NO_ERROR; case FINALIZE_NATIVE:
CHECK_INTERFACE(IHfNativeService, data, reply);
finalize_native();
return NO_ERROR; case READ_NATIVE: {
CHECK_INTERFACE(IHfNativeService, data, reply);
len = data.readInt32();
buff = (char *)malloc(len);
retval = read_native(buff, len);
reply->write((const void *)buff, (size_t)len);
free(buff);
reply->writeInt32(retval);
return NO_ERROR;
} break; case WRITE_NATIVE: {
CHECK_INTERFACE(IHfNativeService, data, reply);
len = data.readInt32();
buff = (char *)malloc(len);
status = data.read((void *)buff, (size_t)len);
retval = write_native(buff, len);
free(buff);
reply->writeInt32(retval);
return NO_ERROR;
} break; case TEST_NATIVE:
CHECK_INTERFACE(IHfNativeService, data, reply);
retval = test_native(data.readInt32());
reply->writeInt32(retval);
return NO_ERROR; default:
return BBinder::onTransact(code, data, reply, flags);
}
} }; // namespace android
frameworks/native/libs/hfnative/HfNativeManager.cpp
#define LOG_TAG "HfNative" #include <stdint.h>
#include <sys/types.h> #include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h> #include <binder/IBinder.h>
#include <binder/IServiceManager.h> #include <hfnative/IHfNativeService.h>
#include <hfnative/HfNativeManager.h> // ----------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------- HfNativeManager::HfNativeManager() : isDied(false)
{ } HfNativeManager::~HfNativeManager()
{
} void HfNativeManager::hfNativeServiceDied()
{
isDied = true;
mHfNativeServer.clear();
} status_t HfNativeManager::assertState() {
if (mHfNativeServer == NULL) {
// try for one second
const String16 name("hfnativeservice");
for (int i= ; i< ; i++) {
status_t err = getService(name, &mHfNativeServer);
if (err == NAME_NOT_FOUND) {
usleep();
continue;
}
if (err != NO_ERROR) {
return err;
}
break;
} init_hfnative();
ALOGI("test hfnativeservice [%d]", test_hfnative()); class DeathObserver : public IBinder::DeathRecipient {
HfNativeManager& mHfNativeManager;
virtual void binderDied(const wp<IBinder>& who) {
ALOGW("hfnativeservice died [%p]", who.unsafe_get());
mHfNativeManager.hfNativeServiceDied();
}
public:
DeathObserver(HfNativeManager& mgr) : mHfNativeManager(mgr) { }
}; mDeathObserver = new DeathObserver(*const_cast<HfNativeManager *>(this));
mHfNativeServer->asBinder(mHfNativeServer)->linkToDeath(mDeathObserver);
} return NO_ERROR;
} bool HfNativeManager::checkService() const
{
return isDied? true:false;
} void HfNativeManager::resetServiceStatus()
{
isDied = false;
} int HfNativeManager::init_hfnative(void)
{
return mHfNativeServer->init_native();
} void HfNativeManager::deinit_hfnative(void)
{
mHfNativeServer->finalize_native();
} int HfNativeManager::read_queue(char *buff, int len)
{
return mHfNativeServer->read_native(buff,len);
} int HfNativeManager::write_queue(char *buff, int len)
{
return mHfNativeServer->write_native(buff,len);
} int HfNativeManager::test_hfnative(int value)
{
return mHfNativeServer->test_native(value);
}
// ----------------------------------------------------------------------------
}; // namespace android
frameworks/native/libs/hfnative/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) LOCAL_SRC_FILES:= \
IHfNativeService.cpp \
HfNativeManager.cpp LOCAL_SHARED_LIBRARIES := \
libbinder \
libcutils \
libutils LOCAL_MODULE:= libhfnativemgriface #ifneq ($(filter generic%,$(TARGET_DEVICE)),)
# Emulator build
# LOCAL_CFLAGS += -DUSE_FENCE_SYNC
#endif include $(BUILD_SHARED_LIBRARY) #ifeq (,$(ONE_SHOT_MAKEFILE))
#include $(call first-makefiles-under,$(LOCAL_PATH))
#endif
(C) 添加服务端
服务端说白了就是客户端的远程调用,如,客户端调用write_native()的时候,服务端的write_native()
也会被调用。为什么客户端不能直接调用服务端的write_native(),就是因为客户端和服务端分别处于不同的
进程中,进程间的通讯必须通过Binder、socket等机制进行传递。
frameworks/native/services/hfnativeservice/HfNativeService.h
#ifndef ANDROID_HACKFUN_NATIVE_SERVICE_H
#define ANDROID_HACKFUN_NATIVE_SERVICE_H #include <stdint.h>
#include <sys/types.h> #include <cutils/compiler.h> #include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/threads.h> #include <binder/BinderService.h> #include <hfnative/IHfNativeService.h> namespace android { // --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
class HfNativeService : public BinderService<HfNativeService>,
public BnHfNativeService
{
public:
static char const* getServiceName() {
return "hfnativeservice";
} HfNativeService(); private:
virtual int init_native(void);
virtual void finalize_native(void);
virtual int read_native(char *Buff, int Len);
virtual int write_native(char *Buff, int Len);
virtual int test_native(int value);
}; // ---------------------------------------------------------------------------
}; // namespace android #endif // ANDROID_HACKFUN_NATIVE_SERVICE_H
frameworks/native/services/hfnativeservice/HfNativeService.cpp
#include <stdint.h>
#include <math.h>
#include <sys/types.h> #include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
#include <utils/String16.h> #include <binder/BinderService.h>
#include <binder/IServiceManager.h> #include <hfnative/IHfNativeService.h> #include "HfNativeService.h" #include <utils/misc.h>
#include <hardware/hardware.h>
#include <hardware/opersyshw.h> #include <stdio.h> namespace android {
// --------------------------------------------------------------------------- opersyshw_device_t* opersyshw_dev; HfNativeService::HfNativeService()
{
} int HfNativeService::init_native(void)
{
int err;
hw_module_t* module;
opersyshw_device_t* dev = NULL; ALOGI("init_native()"); err = hw_get_module(OPERSYSHW_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == ) {
if (module->methods->open(module, "", ((hw_device_t**) &dev)) != ) {
ALOGE("Can't open opersys module!!!");
return ;
}
} else {
ALOGE("Can't get opersys module!!!");
return ;
} opersyshw_dev = dev; return ;
} void HfNativeService::finalize_native(void)
{
opersyshw_device_t* dev = opersyshw_dev; ALOGI("finalize_native()"); if (dev == NULL) {
return;
} dev->close(); free(dev);
} int HfNativeService::read_native(char *Buff, int Len)
{
opersyshw_device_t* dev = opersyshw_dev;
char* real_byte_array = Buff;
int length; ALOGI("read_native()"); if (dev == NULL) {
return ;
} length = dev->read((char*) real_byte_array, Len); ALOGI("read data from hal: %s", (char *)real_byte_array); return length;
} int HfNativeService::write_native(char *Buff, int Len)
{
opersyshw_device_t* dev = opersyshw_dev;
char* real_byte_array = Buff;
int length; ALOGI("write_native()"); if (dev == NULL) {
return ;
} length = dev->write((char*) real_byte_array, Len); ALOGI("write data to hal: %s", (char *)real_byte_array); return length;
} int HfNativeService::test_native(int value)
{
opersyshw_device_t* dev = opersyshw_dev; if (dev == NULL) {
return ;
} ALOGI("test_native()"); return dev->test(value);
} // ---------------------------------------------------------------------------
}; // namespace android
frameworks/native/services/hfnativeservice/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) LOCAL_SRC_FILES:= \
HfNativeService.cpp \ LOCAL_CFLAGS:= -DLOG_TAG=\"HfNativeService\" LOCAL_C_INCLUDES += \
$(call include-path-for, libhardware)/hardware LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
libhardware \
libhfnativemgriface LOCAL_MODULE:= libhfnativeservice include $(BUILD_SHARED_LIBRARY)
(D) 注册服务端
这里启动添加的的服务,使其运行于一个独立的进程中,等待客户端的请求。
frameworks/native/cmds/hfnative/main_hfnativeservice.cpp
#include <binder/BinderService.h>
#include <HfNativeService.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h> #include <hfnative/IHfNativeService.h> using namespace android; int main(int argc, char** argv) {
#if 1
HfNativeService::publishAndJoinThreadPool(true);
// Like the SurfaceFlinger, limit the number of binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount();
#else sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm = defaultServiceManager(); sm->addService(String16("hfnativeservice"), new HfNativeService()); ProcessState::self()->startThreadPool();
ProcessState::self()->giveThreadPoolName();
IPCThreadState::self()->joinThreadPool();
ProcessState::self()->setThreadPoolMaxThreadCount();
#endif
return ;
}
frameworks/native/cmds/hfnative/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) LOCAL_SRC_FILES:= \
main_hfnativeservice.cpp LOCAL_SHARED_LIBRARIES := \
libhfnativeservice \
libbinder \
libutils LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../../services/hfnativeservice LOCAL_MODULE:= hfnativeservice include $(BUILD_EXECUTABLE)
(E) 添加客户端
用户可以根据客户端提供的接口直接使用,无需关心复杂的客户端和服务端的通信细节。使用HfNativeManager
提供的接口,就能实现远程调用。这里创建了一个独立的线程用于等待接收服务端的死亡通知。
frameworks/base/tests/Hfnative/main_hfnativeclient.cpp
#define LOG_TAG "HfNativeClient" #include <fcntl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <unistd.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h> #include <hfnative/HfNativeManager.h> using namespace android; class HfThread: public Thread {
public:
HfNativeManager *hfmgr; HfThread(void *ptr) {
this->hfmgr = (HfNativeManager *)ptr;
} bool threadLoop();
}; bool HfThread::threadLoop()
{
if (hfmgr->checkService()) {
ALOGW("hfnativeservice Died, please do some clear!");
hfmgr->resetServiceStatus();
} usleep(); return true;
} int main(int argc, char **argv)
{
const char *str = {"Hello, Android !\0"};
char buff[strlen(str)]; HfNativeManager *hfmgr = new HfNativeManager(); if (hfmgr->assertState() == NO_ERROR) {
hfmgr->write_queue(const_cast<char *>(str), strlen(str));
usleep();
hfmgr->read_queue(buff, sizeof(buff));
ALOGI("Service returned: %s", buff);
} sp<HfThread> th = new HfThread((void *)hfmgr);
th->run(); ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool(); return ;
}
frameworks/base/tests/Hfnative/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) LOCAL_SRC_FILES:= \
main_hfnativeclient.cpp LOCAL_SHARED_LIBRARIES := \
libhfnativemgriface \
libbinder \
libutils \
libcutils LOCAL_C_INCLUDES := \
$(ANDROID_SOURCE)/frameworks/native/include/ LOCAL_MODULE:= hfnativeclient include $(BUILD_EXECUTABLE)
(F) 测试
编译后生成对应文件:
out/target/product/<device>/.../system/lib/libhfnativemgriface.so
out/target/product/<device>/.../system/lib/libhfnativeservice.so
out/target/product/<device>/.../system/bin/hfnativeservice
out/target/product/<device>/.../system/bin/hfnativeclient
然后push到机器的相应目录
在机器根目录下,执行以下命令,并观察对应的输出打印(注意要先启动服务端进程):
# cd system/bin
# hfnativeservice &
# service check hfnativeservice
Service hfnativeservice: found
# hfnativeclient &
# kill - pid
对应的输出打印:
......
- ::55.148 I HfNativeService: init_native() - ::55.149 D opersyshw_qemu: OPERSYS HW has been initialized - ::55.150 I HfNativeService: test_native() - ::55.151 I HfNative: test hfnativeservice [] - ::55.151 I HfNativeService: write_native() - ::55.151 D opersyshw_qemu: OPERSYS HW - write()for bytes called - ::55.151 D opersyshw_qemu: write data to driver: Hello, Android ! - ::55.151 I HfNativeService: write data to hal: Hello, Android ! - ::55.252 I HfNativeService: read_native() - ::55.252 D opersyshw_qemu: OPERSYS HW - read()for bytes called - ::55.252 D opersyshw_qemu: read data from driver: Hello, Android ! - ::55.252 I HfNativeService: read data from hal: Hello, Android ! - ::55.252 I HfNativeClient: Service returned: Hello, Android !
......
- ::08.210 W HfNative: hfnativeservice died [0xb6cc90c0] - ::08.210 I ServiceManager: service 'hfnativeservice' died - ::08.269 W HfNativeClient: hfnativeservice Died, please do some clear!
......
Android 6.0一个完整的native service的更多相关文章
- 【Android】 分享一个完整的项目,适合新手!
写这个app之前是因为看了头条的一篇文章:http://www.managershare.com/post/155110,然后心想要不做一个这样的app,让手机计算就行了.也就没多想就去开始整了. ...
- 我的Android进阶之旅------>如何解决Android 5.0中出现的警告: Service Intent must be explicit:
我的Android进阶之旅-->如何解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...
- 我的Android进阶之旅------>怎样解决Android 5.0中出现的警告: Service Intent must be explicit:
我的Android进阶之旅-->怎样解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...
- Android 6.0 如何添加完整的系统服务(app-framework-kernel)
最近学习了如何在Android 6.0上添加一个系统服务,APP如何通过新增的系统服务访问底层驱动.在这学习过程中,收获颇多,并结合学习了<Embeded Android>--Karim ...
- 从零開始怎么写android native service?
从零開始怎么写android native service Android service对于从事android开发的人都不是一个陌生的东西,非常多人可能会认为服务非常easy. 服务是简单,由于复杂 ...
- 王家林的81门一站式云计算分布式大数据&移动互联网解决方案课程第14门课程:Android软硬整合设计与框架揭秘: HAL&Framework &Native Service &App&HTML5架构设计与实战开发
掌握Android从底层开发到框架整合技术到上层App开发及HTML5的全部技术: 一次彻底的Android架构.思想和实战技术的洗礼: 彻底掌握Andorid HAL.Android Runtime ...
- 搭建一个完整的Android工程(一)Dagger2
写在前面 现在越来越多的使用到了开源项目,但是仅限于使用,却不了解,更谈不上深入.也是因为越来越多的开源项目,平时工作中遇到问题也是第一时间寻找对应的开源项目,少了许多独立的思考.现在虽然能很轻松的完 ...
- Kubernetes — 从0到1:搭建一个完整的Kubernetes集群
准备工作 首先,准备机器.最直接的办法,自然是到公有云上申请几个虚拟机.当然,如果条件允许的话,拿几台本地的物理服务器来组集群是最好不过了.这些机器只要满足如下几个条件即可: 满足安装 Docker ...
- Android(4.0.3+): Service, AsyncTask, 定时任务和UI通信
Service使用AlarmManager实现后台定时任务 在Android 4.0.3版本中, 使用AlarmManager实现后台定时任务是比较好的方案, 其实现机制, 是利用Service的 o ...
随机推荐
- zoj 2110 很好的dfs+奇偶剪枝
//我刚开始竟然用bfs做,不断的wa,bfs是用来求最短路的而这道题是求固定时间的 //剪纸奇偶剪枝加dfs #include<stdio.h> #include<queue> ...
- Sql语句中关于如何在like '%?%'中给?赋值
做模糊查询用户的时候,如果 String sql="select * from users where name like %?%"; String[] param={userna ...
- codeforces gym 100357 H (DP 高精度)
题目大意 有r*s张扑克牌,数字从1到 r,每种数字有s种颜色. 询问对于所有随机的d张牌,能选出c张组成顺子的概率和组成同花的概率. 解题分析 对于组成顺子的概率,令dp[i][j][k]表示一共选 ...
- jd-eclipse插件的安装
一,资源 jd-eclipse-site-1.0.0-RC2.zip 百度网盘链接:https://pan.baidu.com/s/1GTFFY_1jg4k9vjZNE4JliQ 提 ...
- 从理论到实践,全方位认识DNS(理论篇)
对于 DNS(Domain Name System) 大家肯定不陌生,不就是用来将一个网站的域名转换为对应的IP吗.当我们发现可以上QQ但不能浏览网页时,我们会想到可能是域名服务器挂掉了:当我们用别人 ...
- 一个unity3d lightmap问题
上周美术同学在使用unity3d制作lightmap的过程中,发现部分被lightmap影响的模型在移动端上效果与pc端不一致.当时我大概看了下,分析后,得到一个结论是“在移动端上lightmap的h ...
- 读书笔记-APUE第三版-(7)进程环境
本章关注单进程执行环境:启动&终止.參数传递和内存布局等. 进程启动终止 如图所看到的: 启动:内核通过exec函数执行程序,在main函数执行之前.会调用启动例程(start-up rout ...
- C#根据规则生成6位随机码
#region 获得6位优惠码 zhy public static string CreatePromoCode(string code) { if (code == "") { ...
- Shiro学习(22)集成验证码
在做用户登录功能时,非常多时候都须要验证码支持,验证码的目的是为了防止机器人模拟真有用户登录而恶意訪问,如暴力破解用户password/恶意评论等. 眼下也有一些验证码比較简单,通过一些OCR工具就能 ...
- UVa563_Crimewave(网络流/最大流)(小白书图论专题)
解题报告 思路: 要求抢劫银行的伙伴(想了N多名词来形容,强盗,贼匪,小偷,sad.都认为不合适)不在同一个路口相碰面,能够把点拆成两个点,一个入点.一个出点. 再设计源点s连向银行位置.再矩阵外围套 ...