在前面测试通过odoo登录的功能,这次的问题重点是如何访问后台具体的业务类的接口呢?这次就以我们在odoo中安装的lunch模块为例,目标是获取lunch.alert的数据,如下图

具体过程接上次文章,继续完善OdooJsonRpc类的代码,首先是基础代码,这个是需要提供具体的model名称和具体方法,也是是一个很基础的方法,后台的odoo网站会利用类似C#反射的机制调用目标类的方法。

    /**
* Calls the method of that particular model
* @param model Model name
* @param method Method name of particular model
* @param args Array of fields
* @param kwargs Object
*/
public call(model: string, method: string, args: any, kwargs?: any)
{
kwargs = kwargs || {};
let params =
{
model: model,
method: method,
args: args,
kwargs: kwargs == false ? {} : kwargs,
context: this.getContext()
};
return this.sendRequest("/web/dataset/call_kw", params);
}

调用以上基础call方法的几个基本封装函数有如下几个,基本实现了对数据的CRUD功能,当然基本上是针对一条数据的操作

    /**
* Reads that perticular fields of that particular ID
* @param model Model Name
* @param id Id of that record which you want to read
* @param mArgs Array of fields which you want to read of the particular id
*/ public read(model: string, id: number, mArgs: any): Promise<any>
{
let args =
[
id, [mArgs]
]
return this.call(model, 'read', args)
} /**
* Provide the name that you want to search
* @param model Model name
* @param name Name that you want to search
*/
public nameSearch(model: string, name: string): Promise<any>
{
let kwargs =
{
name: name,
args: [],
operator: "ilike",
limit:
}
return this.call(model, 'name_search', [], kwargs)
} /**
* Provide the IDs and you will get the names of that paticular IDs
* @param model Model name
* @param mArgs Array of IDs that you want to pass
*/
public nameGet(model: string, mArgs: any): Promise<any>
{
let args = [mArgs]
return this.call(model, 'name_get', args)
} /**
* Create a new record
* @param model Model name
* @param mArgs Object of fields and value
*/
public createRecord(model: string, mArgs: any)
{
let args = [mArgs];
return this.call(model, "create", args, null)
} /**
* Delete the record of particular ID
* @param model Model Name
* @param id Id of record that you want to delete
*/
public deleteRecord(model: string, id: number)
{
let mArgs = [id]
return this.call(model, "unlink", mArgs, null)
} /**
* Updates the record of particular ID
* @param model Model Name
* @param id Id of record that you want to update the.
* @param mArgs The Object of fields and value that you want to update
* (e.g)
* let args = {
* "name": "Mtfa"
* }
*/
public updateRecord(model: string, id: number, mArgs: any)
{
let args =
[
[id], mArgs
]
return this.call(model, "write", args, null)
}

针对单条数据的读取,这里还有另外一种方法,类似上面的read函数

    /**
* Loads all data of the paricular ID
* @param model Model name
* @param id Id of that particular data which you want to load
*/
public load(model: string, id: number): Promise<any>
{
let params =
{
model: model,
id: id,
fields: [],
context: this.getContext()
}
return this.sendRequest("/web/dataset/load", params)
}

还有对odoo多条件查询的情况,尤其还有分页的问题,这里也有这样一个很实用的分页查询的方法,这个在日后app的开发中会经常用到

    /**
* Fires query in particular model with fields and conditions
* @param model Model name
* @param domain Conditions that you want to fire on your query
* (e.g) let domain = [
* ["id","=",11]
* ]
* @param fields Fields names which you want to bring from server
* (e.g) let fields = [
* ["id","name","email"]
* ]
* @param limit limit of the record
* @param offset
* @param sort sorting order of data (e.g) let sort = "ascending"
*/
public searchRead(model: string, domain: any, fields: any, limit: number, offset: any, sort: string)
{
let params =
{
model: model,
fields: fields,
domain: domain,
offset: offset,
limit: limit,
sort: sort,
context: this.getContext()
};
return this.sendRequest("/web/dataset/search_read", params);
}

到此,访问odoo具体业务类的OdooJsonRpc类封装完毕,完整的代码在这里。接下来怎么获取我们的lunch.alert数据呢??

首先定义我们访问odoo业务类的成员变量,这里我们的model类是lunch.alert,需要获取的字段是message和id,其他参数我们暂使用默认的值

    private lunchAlert = "lunch.alert";
private fields = ["message", "id"];
private domain = []
private sort = ""
private limit =
private offset =
private items: Array<{ id: number, message: string }> = []

第二步是,与odoo后台网站交互获取数据的过程

    private TestMyOdooModel()
{
this.odooRpc.searchRead(this.lunchAlert,this.domain, this.fields, this.limit, this.offset,this.sort)
.then((lunchAlerts: any) =>
{
let json = JSON.parse(lunchAlerts._body);
if (!json.error)
{
let query = json["result"].records
for (let i in query)
{
this.items.push
({
id: query[i].id,
message: query[i].message
})
}
this.utils.presentAlert("Lunch Message", this.items[].message,[{text: "Ok"}])
}
else
{
this.utils.presentAlert("LunchAlert", "Parse lunch alert error",[{text: "Ok"}])
}
})
}

如果正确获取数据,我们会弹出第一条数据对应的message.

编译打包成*.apk,在手机上测试,确实成功了,如下图所示。

由于这些都是测试阶段的代码,将来可能会改变,现阶段把这些代码全部放在这里。

测试部分的Page页面odooLogin.ts

import { Component } from '@angular/core';
import {
AlertController,
IonicPage,
Loading,
LoadingController,
NavController,
NavParams
} from 'ionic-angular';
import { OdooJsonRpc } from '../../../../providers/baseService/Odoojsonrpc';
import { Utils } from "../../../../providers/baseService/Utils"; @IonicPage()
@Component
({
selector: 'page-odooLogin',
templateUrl: 'odooLogin.html',
}) export class OdooLoginPage
{
private listForProtocol: Array<{ protocol: string}> = []
public perfectUrl: boolean = false
public odooUrl
public selectedProtocol
private dbList: Array<{ dbName: string}> = []
private selectedDatabase
private email
private password constructor(public navCtrl: NavController,
private alert: AlertController, public navParams: NavParams,
private odooRpc: OdooJsonRpc, private loadingCtrl: LoadingController,
private utils: Utils)
{
this.listForProtocol.push({ protocol: "http" })
this.listForProtocol.push({protocol: "https"})
} public checkUrl()
{
this.utils.presentLoading("Please Wait")
this.odooRpc.init
({
odoo_server: this.selectedProtocol + "://" + this.odooUrl
//http_auth: 'username:password' // optional
}) this.odooRpc.getDbList().then((dbList: any) =>
{
console.log(dbList)
this.perfectUrl = true
this.utils.dismissLoading()
this.fillData(dbList)
}).
catch((err: any) =>
{
console.log(err)
this.utils.presentAlert("Error", "You Entered a wrong Odoo URL",
[{
text: "Ok"
}])
this.utils.dismissLoading()
});
} public fillData(res: any)
{
let body = JSON.parse(res._body)
let json = body['result'];
this.dbList.length = ;
for (var key in json)
{
this.dbList.push({ dbName: json[key] });
}
} private login()
{
this.utils.presentLoading("Please wait", , true)
this.odooRpc.login(this.selectedDatabase, this.email, this.password)
.then((res: any) =>
{
let logiData: any = JSON.parse(res._body)["result"];
logiData.password = this.password
localStorage.setItem("token", JSON.stringify(logiData));
//this.utils.dismissLoading()
this.utils.presentAlert("Congratulation", "You login success",[{text: "Ok"}])
this.TestMyOdooModel()
}).
catch((err) =>
{
this.utils.presentAlert("Error", "Username or password must be incorrect",
[{
text: "Ok"
}])
});
} private lunchAlert = "lunch.alert";
private fields = ["message", "id"];
private domain = []
private sort = ""
private limit =
private offset =
private items: Array<{ id: number, message: string }> = [] private TestMyOdooModel()
{
this.odooRpc.searchRead(this.lunchAlert,this.domain, this.fields, this.limit, this.offset,this.sort)
.then((lunchAlerts: any) =>
{
let json = JSON.parse(lunchAlerts._body);
if (!json.error)
{
let query = json["result"].records
for (let i in query)
{
this.items.push
({
id: query[i].id,
message: query[i].message
})
}
this.utils.presentAlert("Lunch Message", this.items[].message,[{text: "Ok"}])
}
else
{
this.utils.presentAlert("LunchAlert", "Parse lunch alert error",[{text: "Ok"}])
}
})
}
}

页面布局部分odooLogin.html

<ion-content class="background">
<ion-card>
<ion-card-content> <div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-label style="color: #fff">Select Protocol</ion-label>
<ion-select [(ngModel)]="selectedProtocol" style="color: #fff" name="dbNames">
<ion-option *ngFor="let item of listForProtocol" value="{{item.protocol}}">{{item.protocol}}</ion-option>
</ion-select>
</ion-item>
<div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-input [(ngModel)]="odooUrl" type="url" name="odooUrl" placeholder="Odoo Url"></ion-input>
</ion-item>
<div class="spacer" style="height: 10px;"></div>
<button ion-button block round outline color="light" (click)="checkUrl()" text-center>
Check Url
<ion-icon name="md-arrow-round-forward"></ion-icon>
</button>
<div [hidden]="!perfectUrl">
<form (ngSubmit)="login()" #registerForm="ngForm">
<div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-input type="email" [(ngModel)]="email" name="email" placeholder="Email" required></ion-input>
</ion-item>
<div class="spacer" style="height: 5px;"></div>
<ion-item>
<ion-input type="password" [(ngModel)]="password" name="pass" placeholder="Password" required></ion-input>
</ion-item>
<div class="spacer" style="height: 10px;"></div>
<div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-label style="color: #fff">Select Database</ion-label>
<ion-select [(ngModel)]="selectedDatabase" name="selectDatabase" style="color: #fff" required>
<ion-option *ngFor="let item of dbList" value="{{item.dbName}}">{{item.dbName}}</ion-option>
</ion-select>
</ion-item>
<button ion-button block round outline color="light" [disabled]="!registerForm.form.valid" (click)="signin()">Login</button>
</form>
</div>
</ion-card-content>
</ion-card>
</ion-content

CSS效果部分odooLogin.scss

page-odooLogin {
.background {
height: %;
width: %;
background-size: cover !important;
background-position: center center !important;
background-image: url('../assets/imgs/mountain.jpg')
} ion-card.card {
margin-top: %;
box-shadow: none;
background: rgba(, , , 0.5);
border-radius: 5px;
}
a, p,
ion-card-header.card-header {
color: #fff!important;
} .list > .item-block:first-child {
border: medium none;
} .item {
margin-bottom: 10px;
background: rgba(, , , 0.5);
border: medium none; .text-input, {
color: #fff;
} input::-moz-placeholder{
color: #fff!important;
}
input:-moz-placeholder {
color: #fff!important;
}
*:-moz-placeholder{
color: #fff!important;
}
*:-ms-input-placeholder{
color: #fff!important;
}
*::-webkit-input-placeholder{
color: #fff!important;
}
}
}

OdooJsonRpc部分Odoojsonrpc.ts

import { Injectable } from '@angular/core';
import 'rxjs/add/operator/toPromise';
import 'rxjs/Rx'; import { Headers, Http } from '@angular/http';
import { Utils } from './Utils'; @Injectable()
export class OdooJsonRpc
{
private jsonRpcID: number = ;
private headers: Headers;
private odoo_server: string;
private http_auth: string;
private list = "/web/database/list";
private get_list = "/web/database/get_list";
private jsonrpc = "/jsonrpc"; constructor(private http: Http, private utils: Utils)
{
this.http = http;
} /**
* Builds a request for odoo server
* @param url Odoo Server URL
* @param params Object
*/
private buildRequest(url: String, params: any)
{
this.jsonRpcID += ;
return JSON.stringify
({
jsonrpc: "2.0",
method: "call",
id: this.jsonRpcID,
params: params,
});
} /**
* Returns the error message
* @param response Error response from server
*/
public handleOdooErrors(response: any)
{
let err: string = response.error.data.message
let msg = err.split("\n")
let errMsg = msg[] this.utils.presentAlert("Error", errMsg, [{
text: "Ok",
role: "cancel"
}])
} /**
* Handles HTTP errors
*/
public handleHttpErrors(error: any)
{
return Promise.reject(error.message || error);
} /**
* Sends a JSON request to the odoo server
* @param url Url of odoo
* @param params Object
*/
public sendRequest(url: string, params: Object): Promise<any>
{
let options = this.buildRequest(url, params);
this.headers = new Headers({
'Content-Type': 'application/json; charset=utf-8',
}); let result = this.http.post(this.odoo_server + url, options, { headers: this.headers })
.toPromise()
return result;
} public init(configs: any)
{
this.odoo_server = configs.odoo_server;
this.http_auth = configs.http_auth || null;
} public setOdooServer(odoo_server: string)
{
this.odoo_server = odoo_server;
} public setHttpAuth(http_auth: string)
{
this.http_auth = http_auth;
} /**
* Gets the server info
*/
public getServerInfo()
{
return this.sendRequest("/web/webclient/version_info", {});
} /**
* Gets the session info
*/
public getSessionInfo()
{
return this.sendRequest("/web/session/get_session_info", {});
} /**
* Gets the Odoo Server Version Number
*/
public getServerVersionNumber(): Promise<number>
{
return this.getServerInfo().then((res: any): Promise<number> =>
{
return new Promise<number>((resolve) =>
{
resolve(JSON.parse(res._body)["result"]["server_version_info"][]);
});
});
} /**
* Get the database list
*/
public getDbList(): Promise<string>
{
let dbParams =
{
context: {}
}
return this.getServerVersionNumber().then((data: number) =>
{
if (data <= )
{
return this.sendRequest(this.get_list, dbParams);
}
else if (data == )
{
return this.sendRequest(this.jsonrpc, dbParams);
}
else
{
return this.sendRequest(this.list, dbParams);
}
})
} /**
* Returns all modules that are installed in your database
*/
public modules(): Promise<string>
{
let params =
{
context: {}
}
return this.sendRequest("/web/session/modules", params)
} /**
* Login to the database
* @param db Database name of odoo
* @param login Username
* @param password password
*/
public login(db: string, login: string, password: string)
{
let params =
{
db: db,
login: login,
password: password,
base_location: this.odoo_server,
context: {}
};
return this.sendRequest("/web/session/authenticate", params)
} /**
* Check whether the session is live or not
*/
public check(): Promise<string>
{
let params =
{
context: this.getContext()
}
return this.sendRequest("/web/session/check", params)
} /**
* Destroy the session
*/
public destroy()
{
let params =
{
context: {}
}
return this.sendRequest("/web/session/destroy", params)
} /**
* Fires query in particular model with fields and conditions
* @param model Model name
* @param domain Conditions that you want to fire on your query
* (e.g) let domain = [
* ["id","=",11]
* ]
* @param fields Fields names which you want to bring from server
* (e.g) let fields = [
* ["id","name","email"]
* ]
* @param limit limit of the record
* @param offset
* @param sort sorting order of data (e.g) let sort = "ascending"
*/
public searchRead(model: string, domain: any, fields: any, limit: number, offset: any, sort: string)
{
let params =
{
model: model,
fields: fields,
domain: domain,
offset: offset,
limit: limit,
sort: sort,
context: this.getContext()
};
return this.sendRequest("/web/dataset/search_read", params);
} /**
* Calls the method of that particular model
* @param model Model name
* @param method Method name of particular model
* @param args Array of fields
* @param kwargs Object
*/
public call(model: string, method: string, args: any, kwargs?: any)
{ kwargs = kwargs || {};
let params =
{
model: model,
method: method,
args: args,
kwargs: kwargs == false ? {} : kwargs,
context: this.getContext()
};
return this.sendRequest("/web/dataset/call_kw", params);
} /**
* Reads that perticular fields of that particular ID
* @param model Model Name
* @param id Id of that record which you want to read
* @param mArgs Array of fields which you want to read of the particular id
*/ public read(model: string, id: number, mArgs: any): Promise<any>
{
let args =
[
id, [mArgs]
]
return this.call(model, 'read', args)
} /**
* Loads all data of the paricular ID
* @param model Model name
* @param id Id of that particular data which you want to load
*/
public load(model: string, id: number): Promise<any>
{
let params =
{
model: model,
id: id,
fields: [],
context: this.getContext()
}
return this.sendRequest("/web/dataset/load", params)
} /**
* Provide the name that you want to search
* @param model Model name
* @param name Name that you want to search
*/
public nameSearch(model: string, name: string): Promise<any>
{
let kwargs =
{
name: name,
args: [],
operator: "ilike",
limit:
}
return this.call(model, 'name_search', [], kwargs)
} /**
* Provide the IDs and you will get the names of that paticular IDs
* @param model Model name
* @param mArgs Array of IDs that you want to pass
*/
public nameGet(model: string, mArgs: any): Promise<any>
{
let args = [mArgs]
return this.call(model, 'name_get', args)
} /**
* Create a new record
* @param model Model name
* @param mArgs Object of fields and value
*/
public createRecord(model: string, mArgs: any)
{
let args = [mArgs];
return this.call(model, "create", args, null)
} /**
* Delete the record of particular ID
* @param model Model Name
* @param id Id of record that you want to delete
*/
public deleteRecord(model: string, id: number)
{
let mArgs = [id]
return this.call(model, "unlink", mArgs, null)
} /**
* Updates the record of particular ID
* @param model Model Name
* @param id Id of record that you want to update the.
* @param mArgs The Object of fields and value that you want to update
* (e.g)
* let args = {
* "name": "Mtfa"
* }
*/
public updateRecord(model: string, id: number, mArgs: any)
{
let args =
[
[id], mArgs
]
return this.call(model, "write", args, null)
} /**
* Get the User Context from the response of odoo server
*/
private getContext()
{
let response = localStorage.getItem("token");
let jsonData = JSON.parse(response);
let context = jsonData["user_context"];
return context;
}
}

通用类Utils.ts

import { Injectable } from "@angular/core";
import
{
AlertController, Loading,
LoadingController, Toast, ToastController,
ActionSheetController
} from "ionic-angular"; @Injectable()
export class Utils
{
private loading: Loading constructor(private alrtCtrl: AlertController,
private loadingCtrl: LoadingController,
private toastCtrl: ToastController,
private actionSheetCtrl: ActionSheetController)
{ } public presentAlert(title: string,
message: string,
buttons: [{}],
subtitle?: string,
enableBackdropDismiss?: boolean,
inputs?: [{}]): void
{ let alrt = this.alrtCtrl.create
({
title: title,
subTitle: subtitle,
message: message,
buttons: buttons,
enableBackdropDismiss: enableBackdropDismiss,
inputs: inputs
}) alrt.present()
} public presentToast(message: string, duration?: number,
dissmissOnPageChange?: boolean,
position?: string,
showCloseButton?: boolean,
closeButtonText?: string): void
{
let toast = this.toastCtrl.create
({
message: message,
position: position,
dismissOnPageChange: dissmissOnPageChange,
duration: duration,
showCloseButton: showCloseButton,
closeButtonText: closeButtonText
})
toast.present()
} public presentLoading(content: string, duration?: number,
dissmissOnPageChange?: boolean,
enableBackDropDismiss?: boolean,
showBackDrop?: boolean,
spinner?: string): void
{
this.loading = this.loadingCtrl.create
({
content: content,
dismissOnPageChange: dissmissOnPageChange,
duration: duration,
enableBackdropDismiss: enableBackDropDismiss,
showBackdrop: showBackDrop,
spinner: spinner
})
this.loading.present()
} public dismissLoading(): void
{
this.loading.dismiss()
} public presentActionSheet(buttons: [{}], title: string, subtitle?: string,
enableBackdropDismiss?: boolean): void
{
let actionCtrl = this.actionSheetCtrl.create
({
buttons: buttons,
subTitle: subtitle,
title: title,
enableBackdropDismiss: enableBackdropDismiss
})
actionCtrl.present()
}
}

ionic 访问odoo11之具体业务类api接口的更多相关文章

  1. 一次php访问sql server 2008的API接口的采坑

    2018年6月21日17:17:09,注意:不是详细文档,新手可能会看不懂 windows下安装 项目是sql server 2008的k3,php连接数据库写的API,因为是买的时候是别人的程序,测 ...

  2. ionic访问odoo 11接口

    在架设完毕odoo 11的网站之后,第一次面临手机app该如何访问后台网站的问题,是不是模式类似asp.net mvc 那样的模式,或者还存在其他的访问方法,带着这个疑问与困惑,开始的我的研究学习之路 ...

  3. Http下的各种操作类.WebApi系列~通过HttpClient来调用Web Api接口

    1.WebApi系列~通过HttpClient来调用Web Api接口 http://www.cnblogs.com/lori/p/4045413.html HttpClient使用详解(java版本 ...

  4. 熟练掌握HDFS的Java API接口访问

    HDFS设计的主要目的是对海量数据进行存储,也就是说在其上能够存储很大量文件(可以存储TB级的文件).HDFS将这些文件分割之后,存储在不同的DataNode上, HDFS 提供了两种访问接口:She ...

  5. Winform混合式开发框架访问Web API接口的处理

    在我的混合式开发框架里面,集成了WebAPI的访问,这种访问方式不仅可以实现简便的数据交换,而且可以在多种平台上进行接入,如Winform程序.Web网站.移动端APP等多种接入方式,Web API的 ...

  6. Java知多少(107)几个重要的java数据库访问类和接口

    编写访问数据库的Java程序还需要几个重要的类和接口. DriverManager类 DriverManager类处理驱动程序的加载和建立新数据库连接.DriverManager是java.sql包中 ...

  7. 如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问。

    由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题. 刚开始没做任何处理,用jsonp的方式调用 web api 接口, ...

  8. Asp.Net Web Api 接口,拥抱支持跨域访问。

    如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问. 由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题 ...

  9. Tomcat 配置 项目 到tomcat目录外面 和 域名绑定访问(api接口、前端网站、后台管理网站)

    先停止tomcat服务 1.进入apache-tomcat-7.0.68/conf/Catalina/localhost(如果之前还都没有启动过tomcat,是不会有此目录的,先启动一次再关闭,会自动 ...

随机推荐

  1. 西安OpenParty11月29日活动高清图文回顾——新增西安APEC蓝美图!

    本次活动由西安OpenParty负责线下活动组织运营,线上由InfoQ-QClub.OSChina协办. OSChina活动召集帖:运维为王——应用系统.DevOps与Docker(11月29日) I ...

  2. window服务器上mongodb的安装与如何将mongodb设置为服务,为mongodb设置管理用户,mongodb连接字符串配置

    最近公司有一个项目模块让用nosql-mongodb替换了,故,对mongodb做了一点研究,然后分享一下! 1.首先说一下安装时的坑 下载mongodb,如果你从官网下载,将会是一件很慢的事情,在公 ...

  3. 操作过程-CentOS7下添加新硬盘扩充已经存在的逻辑卷分区的存储空间

    Linux添加硬盘扩充已有分区存储空间方式 总体步骤 磁盘初始化分区 创建物理卷 扩展卷组 扩展逻辑卷 通知文件系统生效 ​ 磁盘初始化分区 [root@oracledb ~]# fdisk -l 磁 ...

  4. SolrCloud集群搭建(基于zookeeper)

    1. 环境准备 1.1 三台Linux机器,x64系统 1.2 jdk1.8 1.3 Solr5.5 2. 安装zookeeper集群 2.1 分别在三台机器上创建目录 mkdir /usr/hdp/ ...

  5. postgresql-JSON使用

    json,jsonb区别 json和jsonb,而两者唯一的区别在于效率,json是对输入的完整拷贝,使用时再去解析,所以它会保留输入的空格,重复键以及顺序等.而jsonb是解析输入后保存的二进制,它 ...

  6. 描述性统计的matlab实现

    理论讲的再多不会做也白弄 直接上手 一.针对接近正态分布的(均值,方差,标准差,极差,变异系数,偏度,峰度) 这里我必须提前说明一点就是,你在写好函数后,函数的名是dts,你保存的文件名也必须是dts ...

  7. 成功激活Win8.1专业版方法

    前两天安装了Win8.1,感觉界面效果都很简约,速度很快,很喜欢,但是不是破解版,本想将就着用,等到真正破解工具出来了再激活,但是用了两天发现系统右下角屏幕上有未激活的提示,时常弹出激活设置,看着有点 ...

  8. PHP PC端支付宝扫码支付

    前面的文章已经描述过在蚂蚁金服开放平台创建应用签约等流程,详见:PHP App端支付宝支付,这里就不多说了,剩下的分两步,第一步是支付前的准备工作,也就是整合支付类文件,我已经整合好可以直接用,代码开 ...

  9. Eclipse中定位当前文件在项目中的位置

    点击红色框内的按钮,就能定位当前文件在项目中的位置, 另外, 找到位置后记得再点击一下这个按钮, 要不然每次打开一个文件都会自动定位

  10. swift class的虚函数表、扩展、@objc修饰、虚函数的派发方式研究

    swift class的虚函数表.扩展.@objc修饰的研究 工具: swiftc -emit-sil BaseClass.swift | xcrun swift-demangle > Clas ...