freddon

发表于2016-10-21

阅读 1252 |

评论 1

使用JNI fork进程实现 监控自身App被卸载

首先这个功能仅供cankao 也可以使用linux exec命令跳转到浏览器的反馈页

我使用的环境

IDE:Android Studio 2.0

gj:Android 4.4.2

Android.mk

```shell

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE:=uninstall

LOCAL_SRC_FILES:=uninstall.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include

LOCAL_SHARED_LIBRARIES := liblog libcutils

LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

```

C代码:

原理是借用网上的监控文件夹变化(因为如果应用卸载了 对应的/data/data/包 文件夹也会发生变化)

```c

// uninstall.c

// Created by Fred on 16/4/19.

//

// email:gsiner@live.com

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "GUNetDef.h"

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "fred", __VA_ARGS__)

static char c_TAG[] = "onEvent";

#define BUFFER_SIZE 2

/**

* 感谢大风提供的三个发送或处理请求的方法

* @edited fred

*/

int Connect(int *sock, const char *address, unsigned short port) {

int _sk = socket(AF_INET, SOCK_STREAM, 0);

if (_sk == INVALID_SOCKET)

return NET_SOCKET_ERROR;

struct sockaddr_in sockAddr;

memset(&sockAddr, 0, sizeof(sockAddr));

sockAddr.sin_family = AF_INET;

sockAddr.sin_addr.s_addr = inet_addr(address);

sockAddr.sin_port = htons(port);

if (sockAddr.sin_addr.s_addr == INADDR_NONE) {

struct hostent *host = gethostbyname(address);

if (host == NULL) {

return NET_SOCKET_ERROR;

}

sockAddr.sin_addr.s_addr = ((struct in_addr *) host->h_addr)->s_addr;

}

fcntl(_sk, F_SETFL, O_NONBLOCK | fcntl(_sk, F_GETFL)); // 设置成非阻塞

int ret = connect(_sk, (struct sockaddr *) &sockAddr, sizeof(struct sockaddr));

fd_set fdset;

struct timeval tmv;

FD_ZERO(&fdset);

FD_SET(_sk, &fdset);

tmv.tv_sec = 15; // 设置超时时间

tmv.tv_usec = 0;

ret = select(_sk + 1, 0, &fdset, 0, &tmv);

if (ret == 0) {

return NET_CONNNECT_TIMEOUT;

}

else if (ret < 0) {

return NET_SOCKET_ERROR;

}

int flags = fcntl(_sk, F_GETFL, 0);

flags &= ~O_NONBLOCK;

fcntl(_sk, F_SETFL, flags); // 设置成阻塞

*sock = _sk;

return SUCCESS;

}

int SendData(int _sk, const UInt8 *buffer, int bufferSize) {

int ret = send(_sk, buffer, bufferSize, 0);

if (ret != bufferSize)

return NET_SOCKET_ERROR;

return SUCCESS;

}

int DisConnect(int _sk) {

if (_sk != INVALID_SOCKET) {

close(_sk);

_sk = INVALID_SOCKET;

}

return SUCCESS;

}

// signal(SIGPIPE,SIG_IGN);//自己可以处理一些信号

void request(char *host, int port, char *reqHead) {

int sk = INVALID_SOCKET;

int ret = Connect(&sk, host, port);

if (ret != SUCCESS)

return;

ret = SendData(sk, reqHead, strlen(reqHead));

LOGI("SendData TRACE");

if (ret != SUCCESS) {

LOGI("send data error");

return;

}

LOGI("send data success");

DisConnect(sk);

LOGI("DisConnect");

//后续处理返回的数据即可 由于本功能不需要 so省略

}

typedef struct paraStruct {

char *watch_path;

char *cpath;

char *chost;

char *para;

char *method;

int cport;

} paraStruct;

int httpRequester(paraStruct *data) {

char s[2048] = {0};

LOGI("c-code::method=%s",data->method);

if (strcmp(data->method, "POST") == 0) {

sprintf(s,

"POST %s HTTP/1.1\r\nHost: %s\r\nCache-Control: no-cache\r\nContent-Length: %d\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n%s",

data->cpath, data->chost, strlen(data->para), data->para);

}

else if (strcmp(data->method, "GET") == 0) {

sprintf(s,

"GET %s HTTP/1.1\r\nHost: %s\r\nCache-Control: no-cache\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n",

data->cpath, data->chost);

} else {

return 1;

}

request(data->chost, data->cport, s);

return 0;

}

void *threadBegin(void *arg) {

paraStruct *data = arg;

if (data) {

int fileDescriptor = inotify_init();

if (fileDescriptor < 0) {

LOGI("inotify_init failed !!!");

exit(1);

}

int watchDescriptor;

watchDescriptor = inotify_add_watch(fileDescriptor,

data->watch_path, IN_DELETE);

if (watchDescriptor < 0) {

LOGI("inotify_add_watch failed !!!");

exit(1);

}

//分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event

void *p_buf = malloc(sizeof(struct inotify_event));

if (p_buf == NULL) {

LOGI("malloc failed !!!");

exit(1);

}

//开始监听

LOGI("start observer");

//read会阻塞进程,b

size_t readBytes = read(fileDescriptor, p_buf,

sizeof(struct inotify_event));

//走到这里说明收到目录被删除的事件,注销监听器

free(p_buf);

inotify_rm_watch(fileDescriptor, IN_DELETE);

//目录不存在log

LOGI("uninstalled");

//扩展:可以执行其他shell命令,am(即activity manager),可以打开某程序、服务,broadcast intent,等等

data->method="POST";

httpRequester(data);

exit(0);

}

return 0;

}

int commonJavaSegment(const char *watch_path,

const char *cpath, const char *chost,

const char *para, int port) {

//初始化log

LOGI("init OK");

//fork子进程,以执行轮询任务

pid_t pid;

pid = fork();

if (pid) {

LOGI("pid=%d", pid);

} else if (pid == 0) {

//子进程注册目录监听器

LOGI("child:: start");

paraStruct *data = malloc(sizeof(paraStruct));

data->watch_path = watch_path;

data->cpath = cpath;

data->chost = chost;

data->para = para;

data->cport = port;

threadBegin(data);

} else {

//父进程直接退出,使子进程被init进程领养,以避免子进程僵死

LOGI("fork failed !!!");

}

return 0;

}

static int start_count=0;//【当然也可以通过互斥锁来标记子进程是否正在运行】

//this jni method made by rec unistall event and feedback

JNIEXPORT int Java_com_freddon_android_app_utils_JNILoader_uninstall(JNIEnv *env, jobject thiz,

jarray path,

jstring httppath, jstring cs,

jstring host, jint port) {

++start_count;

if(start_count>2){//为什么会设置大于2不是1 我测试下来至少为2才会执行

return 0;//说明已经执行过了监控代码

}else{

const char *watch_path = (*env)->GetStringUTFChars(env, path, NULL);

const char *cpath = (*env)->GetStringUTFChars(env, httppath, JNI_FALSE);

const char *chost = (*env)->GetStringUTFChars(env, host, JNI_FALSE);

const char *para = (*env)->GetStringUTFChars(env, cs, JNI_FALSE);

return commonJavaSegment(watch_path, cpath, chost, para, port);

}

}

/**

* C语言的Http请求方法 【此方法备用】

* @author fred

*/

JNIEXPORT int Java_com_freddon_android_app_utils_JNILoader_httpConnect(JNIEnv *env, jobject thiz,

jstring method,

jstring host,

jstring httppath, jstring cs,

jint port) {

const char *cpath = (*env)->GetStringUTFChars(env, httppath, JNI_FALSE);

const char *chost = (*env)->GetStringUTFChars(env, host, JNI_FALSE);

const char *para = (*env)->GetStringUTFChars(env, cs, JNI_FALSE);

const char *cmethod = (*env)->GetStringUTFChars(env, method, JNI_FALSE);

paraStruct *data = malloc(sizeof(paraStruct));

data->cpath = (char *)cpath;

data->chost =(char *)chost;

data->para = (char *)para;

data->cport = port;

data->method = (char *)cmethod;

return httpRequester(data);

}

```

GUNetDef.h  相关错误码

```c

//

// GUNetDef.h

//

#ifndef MyGoUDome_GUNetDef_h

#define MyGoUDome_GUNetDef_h

#define NET_Header_ID (0x7F)

#define NET_Header_VER (0x01)

#define NET_MAX_PACKET_SIZE (4096)

#define NET_NETWORK_PUBLIC (0) // 是否为公网环境

#if NET_NETWORK_PUBLIC

# define NET_AUTHSERVER_ADDR "s1.goyou.cn"

# define NET_AUTHSERVER_PORT (33880)

#else

# define NET_AUTHSERVER_ADDR "192.168.108.181"

# define NET_AUTHSERVER_PORT (33880)

#endif

#define NET_TOKEN_SIZE (16)

#define NET_UUID_SIZE (16)

typedef unsigned char UInt8;

// 数据头

typedef struct {

UInt8 head4[4]; // index 0: NET_Header_ID; index 1: NET_Header_VER; 2:插件号 ;3:命令字

int dataLen;

}t_net_header;

#define kMyGoUNetworkErrorDomain @"MyGoUNetworkErrorDomain"

// 通信错误码定义

#define SUCCESS 0

// socket 相关错误码

#define INVALID_SOCKET (~0)

#define NET_SOCKET_ERROR -1

#define NET_CONNNECT_TIMEOUT -100

#define NET_RECV_TIMEOUT -200

#define NET_COMMUNICATOR_ERROR -500

#endif

```

android

‍‍‍‍‍‍‍‍‍

```

//JNILoader.java

package com.freddon.android.app.utils;

/**

* Created by fred on 16/4/16.

*/

public class JNILoader {

static {

System.loadLibrary("uninstall");

}

/**

* 【Native】核心方法 只支持POST 可以自己更改 [不处理响应]【请处理异常】

*

* @param path 包名data/data 的包路径

* @param path

* @param cs

* @param host

* @param port

* @return

*/

public static native int uninstall(String androidPath, String path, String cs, String host, int port);

/**

* 【Native】发送http请求 [不处理响应]【请处理异常】

*

* @param method 请求方式 大写

* @param host 主机地址 域名或ip [不需要携带协议名]

* @param path 请求路径 /开头

* @param cs 携带参数 仅POST使用

* @param port 端口 目前固定 80

*/

public static native int httpConnect(String method, String path, String cs, String host, int port);

}

```

```java

//调用代码

String host = 192.168.111.10;//host地址【域名也可以不要带协议头】

String path = "/unistall";

String csv = "ver=1.1";

return JNILoader.unistl("/data/data/" + context.getPackageName(), path, csv, host, port);

```

> `注意:代码仅供参考,因为在生产环境出现过很奇怪的bug;`

分类 :工作日志

首先这个功能仅供cankao 也可以使用linux exec命令跳转到浏览器的反馈页

我使用的环境

IDE:Android Studio 2.0

gj:Android 4.4.2

Android.mk

```shell

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE:=uninstall

LOCAL_SRC_FILES:=uninstall.c

LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include

LOCAL_SHARED_LIBRARIES := liblog libcutils

LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

```

C代码:

原理是借用网上的监控文件夹变化(因为如果应用卸载了 对应的/data/data/包 文件夹也会发生变化)

```c

// uninstall.c

// Created by Fred on 16/4/19.

//

// email:gsiner@live.com

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "GUNetDef.h"

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "fred", __VA_ARGS__)

static char c_TAG[] = "onEvent";

#define BUFFER_SIZE 2

/**

* 感谢大风提供的三个发送或处理请求的方法

* @edited fred

*/

int Connect(int *sock, const char *address, unsigned short port) {

int _sk = socket(AF_INET, SOCK_STREAM, 0);

if (_sk == INVALID_SOCKET)

return NET_SOCKET_ERROR;

struct sockaddr_in sockAddr;

memset(&sockAddr, 0, sizeof(sockAddr));

sockAddr.sin_family = AF_INET;

sockAddr.sin_addr.s_addr = inet_addr(address);

sockAddr.sin_port = htons(port);

if (sockAddr.sin_addr.s_addr == INADDR_NONE) {

struct hostent *host = gethostbyname(address);

if (host == NULL) {

return NET_SOCKET_ERROR;

}

sockAddr.sin_addr.s_addr = ((struct in_addr *) host->h_addr)->s_addr;

}

fcntl(_sk, F_SETFL, O_NONBLOCK | fcntl(_sk, F_GETFL)); // 设置成非阻塞

int ret = connect(_sk, (struct sockaddr *) &sockAddr, sizeof(struct sockaddr));

fd_set fdset;

struct timeval tmv;

FD_ZERO(&fdset);

FD_SET(_sk, &fdset);

tmv.tv_sec = 15; // 设置超时时间

tmv.tv_usec = 0;

ret = select(_sk + 1, 0, &fdset, 0, &tmv);

if (ret == 0) {

return NET_CONNNECT_TIMEOUT;

}

else if (ret < 0) {

return NET_SOCKET_ERROR;

}

int flags = fcntl(_sk, F_GETFL, 0);

flags &= ~O_NONBLOCK;

fcntl(_sk, F_SETFL, flags); // 设置成阻塞

*sock = _sk;

return SUCCESS;

}

int SendData(int _sk, const UInt8 *buffer, int bufferSize) {

int ret = send(_sk, buffer, bufferSize, 0);

if (ret != bufferSize)

return NET_SOCKET_ERROR;

return SUCCESS;

}

int DisConnect(int _sk) {

if (_sk != INVALID_SOCKET) {

close(_sk);

_sk = INVALID_SOCKET;

}

return SUCCESS;

}

// signal(SIGPIPE,SIG_IGN);//自己可以处理一些信号

void request(char *host, int port, char *reqHead) {

int sk = INVALID_SOCKET;

int ret = Connect(&sk, host, port);

if (ret != SUCCESS)

return;

ret = SendData(sk, reqHead, strlen(reqHead));

LOGI("SendData TRACE");

if (ret != SUCCESS) {

LOGI("send data error");

return;

}

LOGI("send data success");

DisConnect(sk);

LOGI("DisConnect");

//后续处理返回的数据即可 由于本功能不需要 so省略

}

typedef struct paraStruct {

char *watch_path;

char *cpath;

char *chost;

char *para;

char *method;

int cport;

} paraStruct;

int httpRequester(paraStruct *data) {

char s[2048] = {0};

LOGI("c-code::method=%s",data->method);

if (strcmp(data->method, "POST") == 0) {

sprintf(s,

"POST %s HTTP/1.1\r\nHost: %s\r\nCache-Control: no-cache\r\nContent-Length: %d\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n%s",

data->cpath, data->chost, strlen(data->para), data->para);

}

else if (strcmp(data->method, "GET") == 0) {

sprintf(s,

"GET %s HTTP/1.1\r\nHost: %s\r\nCache-Control: no-cache\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n",

data->cpath, data->chost);

} else {

return 1;

}

request(data->chost, data->cport, s);

return 0;

}

void *threadBegin(void *arg) {

paraStruct *data = arg;

if (data) {

int fileDescriptor = inotify_init();

if (fileDescriptor < 0) {

LOGI("inotify_init failed !!!");

exit(1);

}

int watchDescriptor;

watchDescriptor = inotify_add_watch(fileDescriptor,

data->watch_path, IN_DELETE);

if (watchDescriptor < 0) {

LOGI("inotify_add_watch failed !!!");

exit(1);

}

//分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event

void *p_buf = malloc(sizeof(struct inotify_event));

if (p_buf == NULL) {

LOGI("malloc failed !!!");

exit(1);

}

//开始监听

LOGI("start observer");

//read会阻塞进程,b

size_t readBytes = read(fileDescriptor, p_buf,

sizeof(struct inotify_event));

//走到这里说明收到目录被删除的事件,注销监听器

free(p_buf);

inotify_rm_watch(fileDescriptor, IN_DELETE);

//目录不存在log

LOGI("uninstalled");

//扩展:可以执行其他shell命令,am(即activity manager),可以打开某程序、服务,broadcast intent,等等

data->method="POST";

httpRequester(data);

exit(0);

}

return 0;

}

int commonJavaSegment(const char *watch_path,

const char *cpath, const char *chost,

const char *para, int port) {

//初始化log

LOGI("init OK");

//fork子进程,以执行轮询任务

pid_t pid;

pid = fork();

if (pid) {

LOGI("pid=%d", pid);

} else if (pid == 0) {

//子进程注册目录监听器

LOGI("child:: start");

paraStruct *data = malloc(sizeof(paraStruct));

data->watch_path = watch_path;

data->cpath = cpath;

data->chost = chost;

data->para = para;

data->cport = port;

threadBegin(data);

} else {

//父进程直接退出,使子进程被init进程领养,以避免子进程僵死

LOGI("fork failed !!!");

}

return 0;

}

static int start_count=0;//【当然也可以通过互斥锁来标记子进程是否正在运行】

//this jni method made by rec unistall event and feedback

JNIEXPORT int Java_com_freddon_android_app_utils_JNILoader_uninstall(JNIEnv *env, jobject thiz,

jarray path,

jstring httppath, jstring cs,

jstring host, jint port) {

++start_count;

if(start_count>2){//为什么会设置大于2不是1 我测试下来至少为2才会执行

return 0;//说明已经执行过了监控代码

}else{

const char *watch_path = (*env)->GetStringUTFChars(env, path, NULL);

const char *cpath = (*env)->GetStringUTFChars(env, httppath, JNI_FALSE);

const char *chost = (*env)->GetStringUTFChars(env, host, JNI_FALSE);

const char *para = (*env)->GetStringUTFChars(env, cs, JNI_FALSE);

return commonJavaSegment(watch_path, cpath, chost, para, port);

}

}

/**

* C语言的Http请求方法 【此方法备用】

* @author fred

*/

JNIEXPORT int Java_com_freddon_android_app_utils_JNILoader_httpConnect(JNIEnv *env, jobject thiz,

jstring method,

jstring host,

jstring httppath, jstring cs,

jint port) {

const char *cpath = (*env)->GetStringUTFChars(env, httppath, JNI_FALSE);

const char *chost = (*env)->GetStringUTFChars(env, host, JNI_FALSE);

const char *para = (*env)->GetStringUTFChars(env, cs, JNI_FALSE);

const char *cmethod = (*env)->GetStringUTFChars(env, method, JNI_FALSE);

paraStruct *data = malloc(sizeof(paraStruct));

data->cpath = (char *)cpath;

data->chost =(char *)chost;

data->para = (char *)para;

data->cport = port;

data->method = (char *)cmethod;

return httpRequester(data);

}

```

GUNetDef.h  相关错误码

```c

//

// GUNetDef.h

//

#ifndef MyGoUDome_GUNetDef_h

#define MyGoUDome_GUNetDef_h

#define NET_Header_ID (0x7F)

#define NET_Header_VER (0x01)

#define NET_MAX_PACKET_SIZE (4096)

#define NET_NETWORK_PUBLIC (0) // 是否为公网环境

#if NET_NETWORK_PUBLIC

# define NET_AUTHSERVER_ADDR "s1.goyou.cn"

# define NET_AUTHSERVER_PORT (33880)

#else

# define NET_AUTHSERVER_ADDR "192.168.108.181"

# define NET_AUTHSERVER_PORT (33880)

#endif

#define NET_TOKEN_SIZE (16)

#define NET_UUID_SIZE (16)

typedef unsigned char UInt8;

// 数据头

typedef struct {

UInt8 head4[4]; // index 0: NET_Header_ID; index 1: NET_Header_VER; 2:插件号 ;3:命令字

int dataLen;

}t_net_header;

#define kMyGoUNetworkErrorDomain @"MyGoUNetworkErrorDomain"

// 通信错误码定义

#define SUCCESS 0

// socket 相关错误码

#define INVALID_SOCKET (~0)

#define NET_SOCKET_ERROR -1

#define NET_CONNNECT_TIMEOUT -100

#define NET_RECV_TIMEOUT -200

#define NET_COMMUNICATOR_ERROR -500

#endif

```

android

‍‍‍‍‍‍‍‍‍

```

//JNILoader.java

package com.freddon.android.app.utils;

/**

* Created by fred on 16/4/16.

*/

public class JNILoader {

static {

System.loadLibrary("uninstall");

}

/**

* 【Native】核心方法 只支持POST 可以自己更改 [不处理响应]【请处理异常】

*

* @param path 包名data/data 的包路径

* @param path

* @param cs

* @param host

* @param port

* @return

*/

public static native int uninstall(String androidPath, String path, String cs, String host, int port);

/**

* 【Native】发送http请求 [不处理响应]【请处理异常】

*

* @param method 请求方式 大写

* @param host 主机地址 域名或ip [不需要携带协议名]

* @param path 请求路径 /开头

* @param cs 携带参数 仅POST使用

* @param port 端口 目前固定 80

*/

public static native int httpConnect(String method, String path, String cs, String host, int port);

}

```

```java

//调用代码

String host = 192.168.111.10;//host地址【域名也可以不要带协议头】

String path = "/unistall";

String csv = "ver=1.1";

return JNILoader.unistl("/data/data/" + context.getPackageName(), path, csv, host, port);

```

> `注意:代码仅供参考,因为在生产环境出现过很奇怪的bug;`

评论(1)

先登录,才能发评论哦~

android点击跳转卸载,[android]通过JNI实现卸载自身App后台发送Http请求~相关推荐

  1. android 点击跳转到qq,android开发使用WebView点击网页链接跳转至QQ临时会话

    在PC端,我们可以通过一个URL链接,点击后启动QQ,这是很好的用户跳转体验.很方便. 使用的链接如下: click 其中把574201314 换成你的QQ号. 那么在android里可以这么使用么? ...

  2. Android 点击跳转到蓝牙设置界面

    点击跳转到蓝牙设置界面 1.布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...

  3. android 点击图片动画效果,Android仿微信图片点击全屏效果

    废话不多说,先看下Android图片点击全屏效果: 先是微信的 再是模仿的 先说下实现原理,再一步步分析 这里总共有2个Activity一个就是主页,一个就是显示我们图片效果的页面,参数通过Inten ...

  4. android+点击屏幕隐藏键盘,Android点击EditText文本框之外任何地方隐藏键盘的解决办法...

    1,实现方法一: 通过给当前界面布局文件的父layout设置点击事件(相当于给整个Activity设置点击事件),在事件里进行键盘隐藏 android:id="@+id/traceroute ...

  5. Android点击通知进入详情,Android 点击通知进入正在运行的程序

    好久没写文章,今天遇到一个新需求,app正在通话过程切换到后台,点击通知栏的时候 回到通话界面. 直接上代码吧首先是实现通知的代码 NotificationCompat.Builder notific ...

  6. android 点击跳转应用市场评分页面

    我们上传应用到各个应用市场后,我们大多数的app都会有的一个功能就是点击一个按钮,跳转到应用市场上去评分或评价,这个对于收集用户体验和使用状态有很大好处,因此今天就实现一下这个小功能.点击按钮之后,执 ...

  7. android 点击跳转到qq,Android应用跳转到手机QQ的方法

    Android应用跳转到手机QQ的方法 发布时间:2020-11-04 17:45:06 来源:亿速云 阅读:106 作者:Leah Android应用跳转到手机QQ的方法?相信很多没有经验的人对此束 ...

  8. android打开蓝牙设置界面,Android 点击跳转到蓝牙设置界面

    基于H5的微信支付开发详解 这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲 ...

  9. android 点击跳转到qq,网页端如何实现点击链接跳转到QQ(手机QQ)打开会话

    浏览器QQ对话 Android:String url="mqqwpa://im/chat?chat_type=wpa&uin=11111111"; startActivit ...

最新文章

  1. 百度开源业内首个口罩人脸检测及分类模型,携手开发者共同抗疫
  2. Android按钮事件的4种写法
  3. 上海网络推广浅析外链对网站优化的影响有多大?需要注意什么?
  4. boost::proto::make_expr相关的测试程序
  5. xtrabackup 9.0备份出错的解决方法
  6. leetcode 822. Card Flipping Game | 822. 翻转卡片游戏(Java)
  7. IDEA自动生成序列化ID
  8. Java实现简易的文本编辑器
  9. 一位全减器逻辑电路图_全减器(全减器逻辑电路图)
  10. 拆机专用磁力桌垫:保证一颗螺丝也不漏网
  11. mysql constant number,Mysql报Too many connections,不要乱用ulimit了,看看如何正确修改进程的最大文件数...
  12. Page Cache:为什么我的容器内存使用量总是在临界点?
  13. 在线文档预览解决方案-Office Web Apps在Windows Server 2008 R2部署教程
  14. JavaScript提升(你不知道的JavaScript)
  15. linux双系统的启动,解决Linux双系统安装卡在启动LOGO
  16. 工具介绍:ITerm 2
  17. 查看附件html,附件查看器
  18. js页面将数据内容以文件形式下载
  19. uplink端口能接路由器吗_这些常见的网络故障,你都知道如何解决吗,一文告诉你解决方法...
  20. mybatis resultMap之collection聚集两种实现方式

热门文章

  1. 请教各位仁兄:如何取得本机所有硬盘的所有罗辑盘符?
  2. 小水电站生态下泄流量监测方式
  3. python合集(3)-------字符串
  4. L1-016 查验身份证 (15分)
  5. 尼基塔第一季/全集Nikita迅雷下载
  6. 盒子模型之爱奇艺播放列表
  7. java实现分布式事务
  8. 硬盘安装工具cgi_300元固态硬盘,INTEL洋垃圾1PB写入之后,寿命依然100%想死都难...
  9. 判定有向图中的单连通图
  10. EasyUI的combobox使用总结