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 ...
随机推荐
- RabbitMQ-linux安装rabbitmq(二)
说明 本地装了个虚拟机模拟集群 所以记下安装步骤 安装Erlang 安装类库 yum -y install ncurses-devel yum -y install openssl-devel yum ...
- [luoguP3565] [POI2014]HOT-Hotels(dfs)
传送门 三点在树上距离相等的情况只有一种,就是以某一个点为中心,三个点到这个点的距离相等. 所以直接枚举每个点作为中心,dfs这个中心的子树,根据乘法原理统计答案即可. 时间复杂度 O(n2) (n ...
- Node.js和Chrome V8 引擎了解
说起Node就不得不先介绍一个Chrome V8 引擎. 随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScr ...
- SecureCRT复制粘贴快捷键
复制:[Ctrl]+[Insert] 粘贴:[Shift]+[Insert]
- mybatis指定jdbctype
MyBatis 插入空值时,需要指定JdbcType mybatis insert空值报空值异常,但是在pl/sql不会提示错误,主要原因是mybatis无法进行转换 所以在MyBatis映射文件中要 ...
- CentOS 7: 设置时区和时间
查看当前时区和时间 $ date $ ls -l /etc/localtime 查看所有可用时区 $ timedatectl list-timezones | grep Asia 设置时区 $ tim ...
- java编程思想——java IO系统
一.什么是IO io在本质上是单个字节的移动.而流能够说是字节移动的载体和方式,它不停的向目标处移动数据.我们要做的就是依据流的方向从流中读取数据或者向流中写入数据. 二.java中支持IO操作的库类 ...
- 百度MP3+图片+文字:生成结果文件;(声音58秒,视频59秒,同步性需要进一步优化)
import os os_sep = os.sep this_file_abspath = os.path.abspath(__file__) this_file_dirname, this_file ...
- seq2seq里的数学
seq2seq模型详解 原创 2017年12月25日 09:41:04 标签: seq2seq / 自然语言 / 机器人 在李纪为博士的毕业论文中提到,基于生成的闲聊机器人中,seq2seq是一种 ...
- yii2表单,用惯yii1的可以看一下,有很大不同哦
使用表单 本章节将介绍如何创建一个从用户那搜集数据的表单页.该页将显示一个包含 name 输入框和 email 输入框的表单.当搜集完这两部分信息后,页面将会显示用户输入的信息. 为了实现这个目标,除 ...