SSDB:高性能数据库服务器 - 张善友
SSDB是一个开源的高性能数据库服务器, 使用Google LevelDB作为存储引擎, 支持T级别的数据, 同时支持类似Redis中的zset和hash等数据结构, 在同时需求高性能和大数据的条件下, 作为Redis的替代方案.
因为SSDB的最初目的是替代Redis, 所以SSDB会经常和Redis进行比较. 我们知道, Redis是经常的”主-从”架构, 虽然可以得到负载均衡以及数据跨地域备份的功能, 但无法实现高可用性. 考虑这种情况, Redis的主和从分别在两个IDC机房, 当主所在的机房出现故障时, 整个服务其实就相当于停止了. 因为所有写操作都失败, 而应用一般不会实现自动降级服务.
而SSDB支持”双主”架构(SSDB分布式架构: https://github.com/ideawu/ssdb/wiki/Replication), 两个或者更多的主服务器. 当其中一部分出现故障时, 剩余的主服务器仍然能正常接受写请求, 从而保证服务正常可用, 再将DNS解析修改之后, 就能在机房故障后立即恢复100%可用.
SSDB 被开发和开源出来后, 已经在生产环境经受了3个季度的考验,SSDB最先在”IT牛人博客聚合网站“进行尝试应用, 接着在360游戏部门得到大规模应用, 目前支撑的数据量已经达到数百G. 这些应用最终是使用Redis的, 迁移到SSDB的成本非常低, 涉及的代码改动极小.
SSDB开源数据库项目地址: https://github.com/ideawu/ssdb
作者博客地址: http://www.ideawu.net/blog/ssdb
安装
SSDB 的建议安装?方式是源码编译安装, 建议运?行环境是主流 Linux 发?行版. 远程 SSH 登录你的服务器, 然后?用下?面的命令下载, 编译, 安装和运?行:
$ sudo wget --no-check-certificate https://github.com/ideawu/ssdb/archive/master.zip
$ sudo unzip master
$ cd ssdb-master
$ sudo make
$ #optional, install ssdb in /usr/local/ssdb
$ sudo make install
# start master
$ sudo ./ssdb-server ssdb.conf
# or start as daemon
$ sudo ./ssdb-server -d ssdb.conf
# ssdb command line
$ sudo ./ssdb-cli -p 8888
# stop ssdb-server
$ sudo kill `cat ./var/ssdb.pid`
SSDB 默认安装在 /usr/local/ssdb ??目录下. ssdb-server 是服务器的程序, ssdb-cli 是命令?行客户端.
SSDB配置文件【http://www.ideawu.net/blog/archives/733.html】
SSDB 附带的 ssdb.conf 你不用修改便可以使用. 如果你要高度定制, 还是需要修改一些配置的. 下面做介绍. SSDB 的配置文件是一种层级 key-value 的静态配置文件, 通过一个 TAB 缩进来表示层级关系. 以 ‘#’ 号开始的行是注释. 标准的配置文件如下:
# ssdb-server config
# MUST indent by TAB!
# relative to path of this file, directory must exists
work_dir = ./var
pidfile = ./var/ssdb.pid
server:
ip: 127.0.0.1
port: 8888
# bind to public ip
#ip: 0.0.0.0
# format: allow|deny: all|ip_prefix
# multiple allows or denys is supported
#deny: all
#allow: 127.0.0.1
#allow: 192.168
replication:
slaveof:
# to identify a master even if it moved(ip, port changed)
# if set to empty or not defined, ip:port will be used.
#id: svc_2
# sync|mirror, default is sync
#type: sync
#ip: 127.0.0.1
#port: 8889
logger:
level: info
output: log.txt
rotate:
size: 1000000000
leveldb:
# in MB
cache_size: 500
# in KB
block_size: 32
# in MB
write_buffer_size: 64
# in MB
compaction_speed: 1000
# yes|no
compression: no
work_dir: ssdb-server 的工作目录, 启动后, 会在这个目录下生成 data 和 meta 两个目录, 用来保存 LevelDB 的数据库文件. 这个目录是相对于 ssdb.conf 的相对路径, 也可以指定绝对路径.
server: ip 和 port 指定了服务器要监听的 IP 和端口号. 如果 ip 是 0.0.0.0, 则表示绑定所有的 IP. 基于安全考虑, 可以将 ip 设置为 127.0.0.1, 这样, 只有本机可以访问了. 如果要做更严格的更多的网络安全限制, 就需要依赖操作系统的 iptables.
replication: 用于指定主从同步复制. slaveof.ip, slaveof.port 表示, 本台 SSDB 服务器将从这个目标机上同步数据(也即这个配置文件对应的服务器是 slave). 你可以参考 ssdb_slave.conf 的配制.
logger: 配置日志记录. level 是日志的级别, 可以是 trace|debug|info|error. output 是日志文件的名字, SSDB 支持日志轮转, 在日志文件达到一定大小后, 将 log.txt 改名, 然后创建一个新的 log.txt.
leveldb: 配置 LevelDB 的参数. 你一般想要修改的是 cache_size 参数, 用于指定缓存大小. 适当的缓存可以提高读性能, 但是过大的缓存会影响写性能.
在使?用?自带的 ssdb.conf 配置?文件时, SSDB ?生成的?日志?文件按体积进?行分割, 仅此?而已. 所以, 你需要编写?自?己的 crontab 进?行?日志压缩和定期清理.
如果出现服务器掉电, kernel panic 等系统故障, 在系统重新启动之后, 你需要?手动删除 ssdb的 PID ?文件 ssdb.pid, 然后才能启动 ssdb-server.另外, 你可以参考下?面的做法, 在系统启动和关机时, 启动和关闭 ssdb-server:
# /bin/sh
#
# chkconfig:345 98 98
# description: SSDB is a fast NoSQL database for storing big list of billions of elements
# processname:ssdb
case "$1" in
'start')
/usr/local/ssdb/ssdb-server -d /usr/local/ssdb/ssdb.conf
echo "ssdb started."
;;
'stop')
kill `cat /usr/local/ssdb/var/ssdb.pid`
echo "ssdb stopped."
;;
'restart')
kill `cat /usr/local/ssdb/var/ssdb.pid`
echo "ssdb stopped."
sleep 0.5
/usr/local/ssdb/ssdb-server -d /usr/local/ssdb/
ssdb.conf
echo "ssdb started."
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0
把?件保存为 /etc/init.d/ssdb.sh(需要 root 权限), 然后执?:
chmod ugo+x /etc/init.d/ssdb.sh
把 ssdb加入chkconfig,并设置开机启动。
[azureuser@mono init.d]$ sudo chkconfig --add ssdb.sh
[azureuser@mono init.d]$ chkconfig ssdb.sh on
启动、停止,查看状态的命令如下:
[azureuser@mono init.d]$ sudo service ssdb.sh stop
ssdb stopped.
[azureuser@mono init.d]$ sudo service ssdb.sh start
ssdb 1.6.7
Copyright (c) 2012-2013 ideawu.com
ssdb started.
项目代码中已经加入c# 的api,https://github.com/ideawu/ssdb/tree/stable-1.6.7/api/dotnet,在Mono上跑了一下:
[azureuser@mono dotnet]$ sudo dmcs Client.cs Link.cs Program.cs -out:ssdbClient.exe
[azureuser@mono dotnet]$ mono ssdbClient.exe
ok
1
ok
100
99
-----------------
1 kvs
a: 99
-----------------
0 kvs
-----------------
0 kvs
0
SSDB 入门基础(Chinese)
网络协议设计思想与SSDB网络协议
本文链接:http://www.cnblogs.com/shanyou/p/3496163.html,转载请注明。
博客园_首页 于 2013-12-29 20:31:15
CS中调用微软自带com组件实现音频视频播放(wf/wpf) - YEKEYISHUO
1.mp3播放器:工具箱中右键,在弹出的菜单中选择“选择项”,添加“com组件”,选择名称“windows Media Player",点击确定就会在工具箱新增一个“windows Media Player"控件。程序中隐藏到了wf本身的边框,用picturebox实现移动无边框窗体效果,通过定时器实时更新进度条,最后理解控件playState的几种状态就没有什么了。
#region 移动无边框窗体
private void pictureBox3_MouseDown(object sender, MouseEventArgs e)
{
int xOffset;
int yOffset;
if (e.Button == MouseButtons.Left)
{
xOffset = -e.X;
yOffset = -e.Y;
mouseOffset = new Point(xOffset, yOffset);
isMouseDown = true;
}
}
private void pictureBox3_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown)
{
Point mousePos = Control.MousePosition;
mousePos.Offset(mouseOffset.X, mouseOffset.Y);
Location = mousePos;
}
}
private void pictureBox3_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
isMouseDown = false;
}
}
#endregion
int m = 0;
private void timer1_Tick(object sender, EventArgs e)
{
int i = (int)axWindowsMediaPlayer1.playState;
switch (i)
{
case 1: lblStauts.Text = "状态:停止"; break;
case 2: lblStauts.Text = "状态:暂停"; break;
case 3: lblStauts.Text = "状态:播放"; break;
case 6: lblStauts.Text = "状态:正在缓冲"; break;
case 9: lblStauts.Text = "状态:正在连接"; break;
case 10: lblStauts.Text = "状态:准备就绪"; break;
}
lbljindu.Text = axWindowsMediaPlayer1.Ctlcontrols.currentPositionString;
if (m == 1)
{
hScrollBar1.Maximum = (int)axWindowsMediaPlayer1.currentMedia.duration;
hScrollBar1.Minimum = 0;
hScrollBar1.Value = (int)axWindowsMediaPlayer1.Ctlcontrols.currentPosition;
hScrollBar2.Value = axWindowsMediaPlayer1.settings.volume;
}
}
mp3player
2.wpf音频视频播放器
本视频播放器主要用到MediaElement控件(微软官网例子),它是wpf中提供的一个包含音频和视频的控件,位于System.Windows.Controls下,方法同第一个mp3播放器的类似,不过由于wpf中没有”打开“对话框的相关类OpenFileDialog,所以需要添加wf窗体应用程序中基础类库的引用(右键”添加引用"----".Net"----"System.Windows.Forms")
用于处理全屏的类:
public static class AllScreen
{
private static Window _fullWindow;
private static WindowState _windowState;
private static WindowStyle _windowStyle;
private static bool _windowTopMost;
private static ResizeMode _windowResizeMode;
private static Rect _windowRect;
///
/// 进入全屏
///
///
public static void GoFullscreen(this Window window)
{
//存储窗体信息
_windowState = window.WindowState;
_windowStyle = window.WindowStyle;
_windowTopMost = window.Topmost;
_windowResizeMode = window.ResizeMode;
_windowRect.X = window.Left;
_windowRect.Y = window.Top;
_windowRect.Width = window.Width;
_windowRect.Height = window.Height;
//变成无边窗体
window.WindowState = WindowState.Normal;//假如已经是Maximized,就不能进入全屏,所以这里先调整状态
window.WindowStyle = WindowStyle.None;
window.ResizeMode = ResizeMode.NoResize;
window.Topmost = true;//最大化后总是在最上面
//获取窗口句柄
var handle = new WindowInteropHelper(window).Handle;
//获取当前显示器屏幕
Screen screen = Screen.FromHandle(handle);
//调整窗口最大化,全屏的关键代码就是下面3句
window.MaxWidth = screen.Bounds.Width;
window.MaxHeight = screen.Bounds.Height;
window.WindowState = WindowState.Maximized;
//解决切换应用程序的问题
window.Activated += new EventHandler(window_Activated);
window.Deactivated += new EventHandler(window_Deactivated);
//记住成功最大化的窗体
_fullWindow = window;
}
static void window_Deactivated(object sender, EventArgs e)
{
var window = sender as Window;
window.Topmost = false;
}
static void window_Activated(object sender, EventArgs e)
{
var window = sender as Window;
window.Topmost = true;
}
///
/// 退出全屏
///
///
public static void ExitFullscreen(this Window window)
{
//恢复窗口先前信息,这样就退出了全屏
window.Topmost = _windowTopMost;
window.WindowStyle = _windowStyle;
window.ResizeMode = ResizeMode.CanResize;//设置为可调整窗体大小
window.Left = _windowRect.Left;
window.Width = _windowRect.Width;
window.Top = _windowRect.Top;
window.Height = _windowRect.Height;
window.WindowState = _windowState;//恢复窗口状态信息
window.ResizeMode = _windowResizeMode;//恢复窗口可调整信息
//移除不需要的事件
window.Activated -= window_Activated;
window.Deactivated -= window_Deactivated;
_fullWindow = null;
}
///
/// 窗体是否在全屏状态
///
///
///
public static bool IsFullscreen(this Window window)
{
if (window == null)
{
throw new ArgumentNullException("window");
}
return _fullWindow == window;
}
}
3.wpf播放器扩展
这个在窗体上放入3个MediaElement,然后调整一下样式。。。
private void btnPause_Click(object sender, RoutedEventArgs e)
{
mmBear.Pause();
mmFractal.Pause();
mmButterfly.Pause();
}
private void btnPlay_Click(object sender, RoutedEventArgs e)
{
mmBear.Play();
mmFractal.Play();
mmButterfly.Play();
}
private void btnRewind_Click(object sender, RoutedEventArgs e)
{
mmBear.Position = new TimeSpan(0);
mmFractal.Position = new TimeSpan(0);
mmButterfly.Position = new TimeSpan(0);
}
本文链接:http://www.cnblogs.com/YEKEYISHUO/p/3496642.html,转载请注明。
博客园_首页 于 2013-12-29 20:01:08
开始mono开发 - dreamhappy
使用mono框架开发android程序,第一步当然是构建开发环境,严格意义上说是使用 mono for android开发android程序。
参考Mono for Android安装配置破解 mono for android官网上有一个一键安装包,但是安装不是很方便。
整个环境涉及到很多组件,像搭建lamp环境一样,这些都可以自己安装完成。因为刚开始都涉及到破解版本问题,所以建议大家单个安装。 组件的功能需要搞清楚,有些还是很容易混淆的。
mono是一个开源项目,而mono for android是一个运行时和开发堆栈。
Android环境
主要是JDK,AndroidSDK相关,如果不熟悉 可以参考这篇文章 体验phonegap3.0
Mono Windows运行时
windows下使用mono环境 需要一个MONO 在windows下的运行时
针对vs的mono开发工具是作为一个插件添加到vs的工具。
Xamarin IDE
mono有单独的开发工具MonoDevelop 官网,但是Xamarin提供的IDE名字叫XamarinStudio,实际上是MonoStudio4.0后改的名字。Xamarin Studio 下载地址
模拟器
在IDE中开发应用需要借助设备,设备分模拟设备和真机。使用模拟器调试的整个过程就是把程序安装到模拟中,运行程序,在运行的过程中调试。根据我的测试,当那你的程序发生改变时, IDE会自动部署 安装,app到设备上,如果之前存在的安装,会先卸载。
在Android环境中, adb.exe 工具用于直接操作管理android模拟器或者真实的andriod设备(手机).全称Android Debug Bridge
它的主要功能有:
* 运行设备的shell(命令行)
* 管理模拟器或设备的端口映射
* 计算机和设备之间上传/下载文件
* 将本地apk软件安装至模拟器或android设备
需要注意的是Mono环境安装不顺利,好多问题是因为android sdk安装有问题,比如环境变量设置不正确,版本不一致,安装不完整等。
而对于adb,最新的SDK内部有一个调整 The adb tool has moved to platform-tools from tools directory
ADB是一个客户端-服务器端程序, 其中客户端是你用来操作的电脑, 服务器端是android设备..
先说安装方法, 电脑上需要安装客户端. 客户端包含在sdk里. 设备上不需要安装, 只需要在手机上打开选项设置-应用程序-USB调试(4.0:设备-开发人员选项)
adb 有各个系统的版本的,Liunx,Mac,Windows,根据你自己的操作系统来选择要下载的Android SDK就可以使用adb命令了.
adb工具中比较实用的几个命令
查看设备 adb devices
在设备管理中建立虚拟设备后,通过 adb devices查看当前连接的设备, 连接到计算机的android设备或者模拟器将会列出显示
启动和关闭 adb
adb start-server
adb kill-server
遇到的问题:
模拟器
1 使用编译器时遇到一个问题,对于模拟器设备,设备列表里总是没有设备
新建并刷新也不会有。之后我安装了BlueStacks,显示有所变化,BlueStacks是一个很好用的模拟器调试工具,稍后我会写一篇博文专门记录。
其实设备列表里有没有设备无所谓了,点开新建虚拟设备,你新建过的设备都会列在那里,直接选择启动即可
应用部署
应用安装到模拟器出现错误
Deployment failed. FastDev directory creation failed.
解决方法:
如下图,关闭fast deloyment
Mono架构支持设置
编译部署提示架构不支持相关的错误,可以尝试从以下几个方面解决
Android 工程设置 支持多个平台
为什么会出现架构支持的问题,因为android需要匹配不同架构的cpu,在新建虚拟设备时,如下图 你可以观察到每个虚拟设备所支持的平台。
其实AndroidSdk 中提供了专门针对平台的包,可以按需求下载。
ListView 内置视图不出现
问题:没有显示系统默认的ListView 内置资源视图
对于继承ListActivity 的Activity,使用ListView显示数据,可以利用默认的Androidn内置资源Android.Resource.Layout.SimpleListItem1布局视图,起初我根本找不到这个引用,最后发现是项目的命名空间是 Mono.Android,这会造成Android的命名空间冲突,默认的内置资源都不会出现
string[] items = { };
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
try {
base.OnCreate (bundle);
items = new string[]{ "1", "2", "3", "4" };
this.ListAdapter = new ArrayAdapter
} catch (Exception ex) {
Toast.MakeText (this, "发生异常:"+ex.Message,ToastLength.Long).Show ();
};
}
参考
Mono for Android 4.2.5
http://www.51mono.com/
mono环境官方网站下载地址
MVVM开发模式
ADB命令详解
Xamarin Mono For Android 4.10.01068
本文链接:http://www.cnblogs.com/needrunning/p/3439665.html,转载请注明。
博客园_首页 于 2013-12-29 20:01:07
ChartDirector应用笔记(一) - 24K纯开源
ChartDirector介绍
ChartDirector是一款小巧精细的商业图表库。其适用的语言范围非常广泛,包括.Net, Java, Asp, VB, PHP, Python, Ruby, C++等。ChartDirector既可以为WEB应用提供图表支持,还能为桌面应用提供良好的图表体验。除此之外,ChartDirector还能与MFC, Qt等界面框架无缝结合。这一点,在官方提供的帮助文档中即可窥知一二。在本系列文章中,也将以Qt应用程序框架作为基础,编写各种图表的应用实例。
正如上面所说的,ChartDirector是一款商业图表库。在未获得官方使用授权的情况下使用,图表中会出现黄色标识符。作为独立的个人开发者,显然无法也不必要去购买该库的使用证书。网上的破解方式也比较多,基本上是对官方附带的Dll文件进行破解覆盖即可。
ChartDirector类层次
ChartDirector库中有三大类继承结构较为常用,下面画出了这三大类继承结构:
图表类继承层次是对整个ChartDirector库的功能总结。从上图可以看出,ChartDirector类库能够绘制七大种类的图表:MultiChart, PolarChart, ThreeDChart, BaseMeter, PieChart, XYChart, PyramidChart。其中,金融类图表FinanceChart继承自MultiChart。金融类图表常常包含各种参数和指标,因此使用MultiChart作为基类继承也是情理中的事情。ThreeDChart类派生出了两个子类:SurfaceChart和ThreeDScatterChart。这两个子类用于绘制3D图表,3D图表经常用于立体表现数据差异,绘制出来的图表具有较强的立体视效。BaseMeter类代表仪表基类,派生子类包括:AngularMeter和LinearMeter类。如类名所示的那样,AngularMeter绘制的仪表常常带有弧度,而LinearMeter类则是线性绘制仪表,通常具有水平或者垂直外观。
XYChart类是ChartDirector类库中的一个大类,凡是包含X、Y坐标的图表都属于XYChart。因此,用于XYChart类的Layer类也派生出了丰富的子类,用于实现不同类型的XY图表。Layer类的具体作用是什么呢?由于官方文档上并未对ChartDirector的设计架构进行阐述,因此只能对Layer的功能进行推测理解。我的理解是,Layer就是一般意义上的图层,如果需要在同一个画板上绘制多个XY图表,需要组合不同的Layer类。不同的Layer可以组合在一起,实现复杂的功能。一般在绘制图表的时候,需要根据不同的图表类型添加不同的Layer派生类。
下面是Polar/Radar类的图层类继承层次:
可以看到,PolarChart类的图层类相对较少,样式比较固定。以PolarLayer类为基类派生出了PolarLineLayer, PolarAreaLayer, PolarVectorLayer三个类。
整个ChartDirector类库基本上主要就是由上述这些类组成的。当然,还包括Array, Axis, Box, Sector类辅助类。从这方面考察,ChartDirector类库还是足够轻巧的,学习使用上也较为简单。遗憾的是,附带的说明文档并未涉及太多的架构方面的说明。因此,在完全理解ChartDirector库的设计理念方面存在一定困难。
重要概念
要熟练运用ChartDirector类库需要理解好一些概念。下面是本人的一些总结和理解:
Chart对象。Chart对象是绘图的基础,可以是上述图表类继承层次中的任意一个类的对象。每一个绘制出来的图表都属于特定的图表类。如帮助文档中的Simple Bar Chart,这是一个简单的XYChart的应用实例,生成的chart对象是XYChart类的实例。总之,在绘制每一个图表前都需要实例化一个Chart对象。
Layer类。Layer类在上面稍微提过,我将之理解为图片处理中的图层。Layer之间可以叠加以组合绘制出更加复杂的图表。值得注意的是,不同的图表组件需要对应的Layer类来实现。在XYChart类中,实现了很多了add*函数用于给chart对象添加图层。如addLineLayer, addBarLayer, addBoxLayer等。
PlotArea。PlotArea类表示XY图表中的绘图区域。在绘制XY图表的过程中,设置PlotArea是Chart对象实例化完成后需要做的第一件事。PlotArea使用像素坐标系统,也就是说:(0,0)位于绘制区域的左上方,X轴往右增加,Y轴往下增加。对于其他种类的图表,PlotArea这个概念并不存在。
DrawArea。DrawArea类处于绘图底层,是整个类库绘图的重要基础。ChartDirector类库中所有的图表类都在内部维护一个DrawArea对象,该对象用于提供绘制原语,如绘制线条、形状、文本等。用户也可以通过获取DrawArea对象来自定义绘制选项。
QChartViewer. QChartViewer是用来整合ChartDirector类库到Qt框架中来的中间类。该类继承自QLabel和ViewPortManager类,用于表现图表绘制结果,处理鼠标、键盘交互事件等。在ChartDirector类库中,QChartViewer类以源代码的形式附带。用户可以查看其的实现方式,也可以直接包含进Qt工程中进行使用。
绘制步骤
以simple bar chart为例:
生成Chart对象:XYChart* c = new XYChart(250, 250);
设置绘图区域(可选):c->setPlotArea(30, 20, 200, 200);
添加图层(可选):c->addBarLayer(...);
设置坐标参数:c->xAxis()->setLabels(...); c->yAxis()->setLabels(...);
下一篇讲述Simple Bar Chart的使用。
本文链接:http://www.cnblogs.com/csuftzzk/p/3496695.html,转载请注明。
博客园_首页 于 2013-12-29 20:01:06
银监会 8 号文前,是否有 1-7 号文?分别是那几篇规定,对中国金融业造成了什么影响?
所谓8号文,全称是《中国银监会关于规范商业银行理财业务投资运作有关问题的通知》(银监发〔2013〕8号),这是今年银监会发布的监管规定中较为重要的一个,其中关于银行理财投资非标资产的规定给银行的理财业务造成了较大的影响,发布第二日数支银行股跌停。在市场提及此文时因文件名较长,常以其文号简称。但文号只是银监会发布文件时按时间顺序排列的编号,便于整理归档,因此1—7号文和8号之间并没有直接关系,只是表示该文件是“银监发”这一文种今年的第几个而已。明年也会出现“银监发〔2014〕8号”的文件。因此,题主若想研究银监会发布的对市场影响较大的文件,应从文件具体内容入手,而非文号。
鉴于8号文是规范理财业务,因此顺便推荐给题主银监会理财业务方面的两个文件,近两年影响较大的一是2011年发布的商业银行理财业务销售管理办法(之前没有专门的监管要求规范),二是2010年的《关于规范银信理财合作业务有关事项的通知》(银监发〔2010〕72号),叫停银信合作并要求相关业务进表,其后还有几个补充通知,对市场也造成了较大影响。
8号文为今年三月下发,文号编到8,而后面第二个文当年是八月下发,所以文号已经编到了72,当年也是简称72号文的。
另补充9号文的说法由来。目前银监会在研究制定同业业务的相关管理办法,因为近年银行同业业务发展较快,规模也较大,一旦出台规范也对市场造成较大影响,因此市场也非常关注该办法的动向。市场冠以9号文,是觉得它是8号文之后又一重要规定,但实际上即使出台,编号也不可能是9了,至于是多少,是不可能提前知道的,要等到时候真正发文的时候按时间顺序来,排到几号就是几号了。
银监会发布文件有多种公文形式,包括主 席令、银监发、银监办发等等,具体视文件法律地位、重要性和内容等综合确定。
8号文的称呼,说明这个文件非常重要且影响面广,各方反复提及时嫌麻烦拿文号做了简称;9号文的称呼,说明大家不够了解银监会(我觉得不光是银监会,也包括其他政府机关单位)的文件编号方式哇^_^
— 完 —
本文作者:Blanca Yolanda
【知乎日报新版上架】无网络也能点收藏,举报评论功能新鲜开启,将精彩长评优先展示, 点此立刻更新~
此问题还有 1 个回答,查看全部。
延伸阅读:
银监会用于「规范商业银行理财业务投资运作」的「8 号文」,针对的是信托还是银行?接下来会对哪些与银行相关的金融产业带来影响?
如何解读2013年3月27日银监会《中国银监会关于规范商业银行理财业务投资运作有关问题的通知》(简称“8号文”)?
知乎每日精选 于 2013-12-29 20:00:38
会计合并报表的要点有哪些?
按照我的合并经验,主要是发现各个合并单体之间存在哪些关联往来,哪些在合并报表跟单体报表列示不一致的情况等。合并报表,不光是抵销一些母子公司往来,还需要注意一些合并报表上跟单体列示存在差异的因素。比如,对于一些单体之间,用票据进行融资的情况,单体作为应付票据,但合并应该作为一笔“短期借款”等。
我觉得合并一个企业报表,主要分为以下几个步骤:
一、分清楚什么是合并范围的单体,并还原成本法核算的单体。
将按照成本法核算的单体,还原成为权益法核算。按照被合并的单体的净利润,调整投资收益;按照被合并单体的其他权益变动,调整资本公积。一般的调整分录如下:
DR.长期股权投资-损益调整
DR.长期股权投资-其他权益变动
CR.资本公积-其他资本公积
CR.投资收益
注:如果被合并的单体的会计政策与母公司不一致,需要先将被合并的单体的会计政策调整为与母公司一致。
二、将母公司的长期股权投资和被合并单体的所有者权益,进行抵销。
抵销权益:
DR.实收资本(股本)
DR.年初未分配利润
DR.投资收益
DR.少数股东损益
CR.长期股权投资-成本
CR.长期股权投资-损益调整
CR.长期股权投资-其他权益变动
CR.少数股东权益
并抵销计提的盈余公积:
DR.盈余公积-法定盈余公积
CR.利润分配-提取盈余公积
CR.年初分配利润
三、抵销关联
(一)抵销关联往来
DR.其他应付款
CR.其他应收款
DR.应付账款
CR.原材料(暂估)/库存商品(暂估)等
DR.应付账款
CR.应收账款
并抵销关联往来对应的坏账准备
DR.坏账准备-其他应收
DR.坏账准备-应收
CR.年初未分配利润
CR.资产减值损失
并抵销相关的递延所得税资产
DR.所得税费用-递延
DR.年初未分配利润
CR.递延所得税资产
(二)抵销关联交易
DR.主营业务收入
CR.主营业务成本
DR.其他业务收入
CR.其他业务成本
未实现利润抵销:
DR.年初未分配利润
DR.主营业务成本
CR.原材料
CR.库存商品
三、同时挂账外单位
DR.应付账款
CR.应收账款
等分录,视情况入定。
四、采购固定资产合并抵消。
。。。。。。
总之,合并报表,有些东西是比较死板的,但有些东西是需要全面考虑,并灵活应用的。。。。
— 完 —
本文作者:薇薇安
【知乎日报新版上架】无网络也能点收藏,举报评论功能新鲜开启,将精彩长评优先展示, 点此立刻更新~
此问题还有 1 个回答,查看全部。
延伸阅读:
如何判断财务报表的质量?
怎样成为优秀的注册会计师?
知乎每日精选 于 2013-12-29 19:46:05
用户愿意为保护隐私的应用付款?
maskme-digital-privacy
Instapaper 的创始人是收费应用的支持者,但是他最近也承认,付费下载应用的好日子已经结束。这是因为随着应用商店的不断壮大,免费的替代品也越来越多。为此,开发者不得不采取新的收费方式。如今,许多应用可以免费下载,但会通过其他方式收费,比如去除广告,解锁更多的功能等等。不过,那些功能是用户需要的?他们是否会因为免费的原因,忍受广告的干扰?这些都是开发者们烦恼的问题。
据 Atlantic 网站报道,科罗拉多大学的经济学家在调查 1700 名智能手机用户后发现,用户乐意为保护隐私的应用付款。
在这次调查中,两位学者 Scott J. Savage 和 Donald M. Waldman 向手机用户展示了一组应用。其中,一个应用是真正的免费应用,可以在苹果和 Google 的应用商店内下载,另外五个应用是编造出来的,它们与免费应用的功能完全一样,但是有着不同程度的隐私保护,并且都需要付费。他们发现,用户乐意付款来保护自己的数据,至于付款多少,取决于数据的具体内容。平均下来看,消费者会为一个不读取浏览器历史的应用支付 2.28 美元,为不追踪其位置的应用支付 1.19 美元,为不获取手机 ID 的应用支付 1.75 美元,为不读取短信内容的应用支付 3.58 美元,为一个无广告的应用支付 2.12 美元。
他们对 Android 应用进行了取样,在测试了 1.5 万个应用后,发现多数的应用在展示广告的同时,也会读取用户的位置信息和手机 ID。如果这样的应用收费的话,其价格应该在 5 美元左右。如果按照每人 20 多个应用来计算,美国的智能手机用户能够为此支付 160 亿美元。
当然,这只是一种理论上的推算,现实中难以实现。在因为下载应用的时候,智能手机用户或许不会去了解其是否侵犯隐私,或者并不在意这个问题。不过,这次调查至少说明,当用户对应用的认识增强之后,有可能改变自己的购买决定。看来,对于平台提供商和应用的开发者来说,不应该简单而粗暴地否定付费应用。他们需要了解用户的心理,引导其理性消费,保证应用生态圈的健康发展。
图片来自 redorbit
积木
May the road rise up to meet you. May the wind always be at your back. May the sun shine warm upon your face, and rains fall soft upon your fields.
邮箱 Twitter 新浪微博 Google+
#欢迎关注爱范儿认证微信公众号:AppSolution(微信号:appsolution),发现新酷精华应用。
爱范儿 · Beats of Bits | 原文链接 · 查看评论 · 新浪微博 · 微信订阅 · 加入爱范社区!
爱范儿 · Beats of Bits 于 2013-12-29 19:31:55
架构设计:远程调用服务架构设计及zookeeper技术详解(上篇) - 夏天的森林
一、序言
Hadoop是一个技术生态圈,zookeeper是hadoop生态圈里一个非常重要的技术,当我研究学习hadoop的相关技术时候,有两块知识曾经让我十分的困惑,一个是hbase,一个就是zookeeper,hbase的困惑源自于它在颠覆了我对数据库建模的理解,而zookeeper的困惑却是我无法理解它到底是干嘛的。
前不久我结合我了解的一种远程调用服务的设计来帮助我理解zookeeper在实际的生产中运用,该文章的地址是:
http://www.cnblogs.com/sharpxiajun/p/3297852.html
其实这篇文章写完后,我自己感觉并不是太好,因为写本文的时候,对远程调用服务的设计以及zookeeper的理解都不是很到位,但是这篇文章还是受到了大家很大的关注,被博客园作为了推荐文章,还有很多网友希望我写一篇更加详尽的文章,还有童鞋留言说这个设计方案和淘宝开源的dubbo和hsf类似。我相信大家的关注就是意味着这个主题是当下技术的热点,所以今天我要写一篇主题和上篇文章一样的博文,这是上篇文章的升级版,不管是远程调用服务还是zookeeper我都会给出更加详尽的讲解。不过这里还是要说明下,这篇文章里远程服务的设计我还是没有参照dubbo,因为最近实在太忙,没有时间研究淘宝的dubbo,但是我希望学习过dubbo的童鞋可以帮我对比下我的方案和dubbo的区别,区别就会产生新的问题,也会有新的知识需要研究。
本文是该主题的上篇,主要是讲解远程调用服务的相关知识,下篇则是根据远程调用服务架构设计中zookeeper的相关应用方法详细讲解关于zookeeper的知识。
二、远程调用服务的架构设计总述
首先我们要再深入理解下为什么应用软件服务里需要一个远程调用服务,远程调用服务解决了软件设计中的什么问题,它的架构设计又有什么理论根据了?
我曾写了一篇关于分布式网站架构设计的文章,文章地址是:
http://www.cnblogs.com/sharpxiajun/archive/2013/05/11/3072798.html
在文章开头我就把这个新的网站架构方案和传统的企业软件的B/S架构作了对比,我将一个网站里提供业务服务的组件抽象为独立的服务系统,接收用户信息的逻辑部分抽象为前端系统,服务系统和前端系统使用netty这样的通讯组件进行通讯,而到了讲解远程调用服务的框架设计时候我将netty通讯组件进一步抽象为一个通讯独立系统及远程调用服务,这就是为什么要设计远程调用服务的缘起了,远程调用服务又会带来了网站架构的升级,如果传统的企业B/S架构为1.0版,我将前端和业务服务端分离为独立系统则是2.0版,那么引入了远程调用服务网站就是3.0版了,3.0版的架构带来的好处就是可以将N多的前端系统和N多的业务服务端系统融为一个整体,网站的规模会越来越大,提供的服务也会越来越多,这既避免重复造轮子的问题还使得网站规模越来越大。
3.0版本的网站架构带来了新的网站架构总图,如下所示:
有了远程调用服务,我们可以做到业务级别的集群,例如:一个制造企业,一般都会有采购业务,生产业务、销售业务以及财务业务,按照传统的思路我们都会给每个业务独立开发一个系统,如果引用了远程调用服务,我们可以将这些业务都做成独立的服务,这些服务组成业务集群,而这些服务都是用统一的远程调用服务作为操作的入口,换句话说不管什么样的服务对于调用者来说都是统一的,这样前端的调用者可以做到应用的统一,所谓的应用的统一淘宝网站是最典型的代表,我们在一个同一的网站里可以操作各种不同的应用,而不会发生因为应用的不同我们就得重新访问新的地址或者重新登录到另外一个系统里做其他业务的操作。而服务端这边,完全可以摆脱传统的客户端和服务端耦合的开发,增强了整个服务端的专业性和稳定性,这样更易于服务端的扩展性和可维护性。如果服务端之间也需要相互调用也可以通过远程调用服务实现,由于远程调用服务的统一性,这样就避免了服务调用之间报文和调用方式的不统一,规范了整个开发的流程。如果远程调用服务还有负载均衡功能,整个服务集群就变成了一个私有的云,所以说远程调用服务是云计算的重要组成部分,这个说法一点都不为过。
远程调用服务的理论依据是什么,这个问题的表述可能有点问题,其实我要讲的是远程调用服务的技术原型就是SOA(Service-Oriented Architecture),在云计算出现前,SOA曾一度是IT的技术热点,虽然之后很多人说中国的SOA做的一点不好,就和早年的DHTML一样,诟病远多于赞赏,写本文时候我在京东里搜索了下SOA,从书籍的出版日期和书籍评价数就可以看出SOA已经有点无人问津的凄凉了。下面我要简单介绍下SOA,SOA的定义:
SOA是一个软件架构,它包含四个关键概念:应用程序前端、服务、服务库和服务总线。一个服务包含一个合约、一个活多个接口以及一个实现。
应用程序前端可以理解为我上面所讲述的调用者和前端系统,服务库可以理解为服务集群,这里还有个服务是什么呢?服务就是调用者和服务提供者完成某一个特定业务的合约,换句话说就是封装的业务规则,打个比方,我们在淘宝去购物,下订单,付款,查物流,确定付款这些操作在服务端都有独立的服务提供,但是从购物这个概念去理解,这些独立的服务才能构成这个完整的购物行为,如果其中有地方出了问题,会有相应不同的操作,那么这个就绝对不是调用者简单调用服务接口的问题,需要更高层次的业务封装,将上面这些操作封装为一个统一的服务,这个就是所谓的服务。最后一个要素服务总线,这就是我们本文所谈的重要主题:远程调用服务了。
这里谈谈SOA的目的是想起到抛砖引玉的作用,让那些想深入研究远程调用服务的人可以从SOA的角度理解远程调用服务,而那些还是不明白远程调用是何物的童鞋可以通过SOA的概念来理解远程调用服务。
三、远程调用服务技术详解
远程调用服务技术详解,详解,呜呜~~,这两个字很有压力,我怕有童鞋看了这个标题会以为我会将整套技术实现方案写到里面,这个难度太高了,写几万字估计都说不清楚,再说真的写的那么细致,估计很多人都看不懂了(嘿嘿,我自己也没有技术实现过哦,这些都是构思,构思哦),所以详解就是详解原理。
下面我将上篇文章的架构图放进来,大家再仔细看看这张图:
传统的服务调用都是服务提供者和服务调用者的直接调用,从架构图里我们看到这里多了一个远程调用管理组件,远程调用管理组件是一个独立的服务系统,为了保证该系统的稳定性,它也一定是一个分布式的系统,但是这个分布式系统和Web的分布式系统是完全不同的分布式系统,传统Web应用集群是基于HTTP协议的无状态的特点设计的,因为每个HTTP请求都是一个独立的事务,不同请求之间是没有任何关系的,所以我们可以将Web应用部署到不同服务器上,请求不管到了那台服务器,都能正常的给用户提供相应的服务,但是Web应用的session机制是有状态的,所以传统Web集群都是要有session同步的操作,大型网站往往会把session功能抽象为独立的缓存系统,但是这里的远程调用管理组件的集群原理或者说分布式原理是有别于Web应用集群分布式原理的,远程调用管理组件可以当做一个注册中心,它会记录下服务提供者和服务调用者的相关信息,并将这些信息推送给服务提供者或者服务调用者,为了保证系统的执行效率,这些注册信息都是记录在内存里,我们试想下,如果这些注册信息丢失,整个系统将会不可用,因此远程调用管理组件的集群是一种保证数据可靠性和服务提供健壮性的集群,而不是建立在HTTP无状态特性基础上的集群。我们这里假想下远程调用服务的集群运行场景,我们假如有5台服务器作为远程调用服务运行的服务器,那么每台服务器都必须有注册信息的冗余备份,当服务运行时候其中一台服务器发生了故障,这台故障的服务器上的数据不会丢失,此外集群应该还要有一个检查故障的机制,当发现有台服务器不可用的时候,能及时剔除该服务器,而zookeeper就是解决这种问题的技术框架。此外除了保证系统的稳定性和可用性外,集群的数据存储方式也是很重要的,前面我讲到集群的数据存储要有一个冗余机制,除了冗余机制还要有一个很适合快速访问和读写的数据模型,而zookeeper正好包含这种数据模型,所以我设计的远程调用服务是一个很适合zookeeper应用的场景,至于zookeeper的详细知识我会在下篇里详细讲到。
远程调用管理组件还有一个心跳机制,心跳机制的作用是检测服务提供者的健康性及服务提供者是否可用,服务提供者启动时候会将自己的注册信息发送给远程调用管理组件,这个注册信息里包含服务端的ip地址和端口号,远程调用管理组件会启动一个线程,根据定时对这个ip地址和端口号去ping这个ip和端口号对应的应用是否可用,如果不可用远程调用管理组件会反复尝试几次,这个次数和多久检测心跳都是可以配置的,如果反复几次还是不通,那么就认定该服务不可用了。有网友在QQ上问我,为什么不检测服务调用者的心跳,这个完全没必要哦,调用者是主动方,提供者是被动方,这就好比你访问网站,如果你生病了不去访问了,系统没有必要检查你是否已经生病了。
远程调用框架需要使用序列化和反序列化技术,这点也让很多童鞋不太理解,不理解的原因还是对序列化和反序列化技术的不理解,序列化技术主要是应用与数据持久化(数据存到硬盘)或者网络通讯,不管是数据存储到硬盘还是进行网络通讯,这些数据都会转化为二进制,序列化就是将正在运行的对象转化为可以存储和传输的二进制数据,而反序列化是可以将这些二进制数据反向还原成原来的对象信息,还原的对象还是可以被程序操作的,而我们设计的远程调用框架传递就是不同系统之间可以相互使用的程序代码,所以我们需要使用序列化和反序列化技术。这里就有一个问题,例如我们传输一个对象,这个对象对应的类是N多个类的继承子类,而且这个对象里可能还会引用其他的对象例如String,ArrayList等等,那么为了让反序列化的对象可用,序列化的时候就会将这些信息也包含在二进制数据里,并且这些信息一起进行网络传输,这就导致数据传输量特别大,而jdk自带的序列化机制会导致这些附带信息更大,所以有必要使用比jdk更好的序列化机制,让数据量变小,并且序列化和反序列化的效率更高,上篇文章里我推荐了一种序列化框架hession,当然用户想使用什么序列化机制这个我也让用户可以自己配置,这也是外部配置文件的一个选项。
前面文章里我还讲到了压缩技术并且推荐了google公司使用snappy,这个压缩技术也是为了让网络传输的数据量变小,提升网络的传输效率。
对于服务提供者和服务调用者我会提供一个jar包,这些工程都要引入这个jar包,同时还需要一个配置文件来定义一些需要用户定义的参数,例如我们使用一个名字叫ycdy_config.properties配置文件,里面的key值介绍如下:
Config_center_url=ip:port;这个就是配置远程管理中心的ip地址和端口号;
Server_type=provider/consumer;配置是服务调用者还是提供者,不配置默认是提供者;
Provider_post=9999,这是服务提供者的端口号,调用者可以不配置,其实调用配置了也没啥用,如果提供者不配置,会有默认值的;
Provider_session_timeout=9000;服务提供者的超时时间,如果实际调用超过了这个时间,那么说明服务调用超时,适用于服务调用者,提供者无效
Tick_time=3000;心跳时间,这是远程调用中心检测服务端心跳的间隔时间,适用于服务提供者;
Again_time=3;当服务提供者不通的时候,心跳反复检测的次数,超过了次数就标记该服务不可用;Provider_session_timeout、Tick_time和Again_time三者之间是有一定关系,这个关系要实现这具体把控了;
Ip_include_pattern=172\\.17\\.138.*|192\\.168\\.0\\..*,这个适用于服务提供者,因为一台服务器可能存在多个ip地址,当远程调用服务组件接收到提供者的ip,用这个配置项来辨认那个ip可用,这里采用正则表达式的方式;
Ip_exclude_pattern=用于服务提供者,需要忽略的ip;
Consumer_policy=random/rotate;适用于调用者,调用者向提供者请求的负载均衡策略,我熟悉的只有两种一种是使用随机数,一种使轮询,所以这里目前就这两种选项;
Monitor_log=true/false;是否开启监控日志,适用于服务提供者,任何系统日志时最重要的,否则没法查生产问题,其实这个配置项应该可以充实点,但是我现在还没想好,所以先给个提示,具体到了生产看如何实现吧。
大家看到了不管是作为服务提供者还是服务调用者使用的配置文件是一致的,而且一个应用既可以配置成服务的调用者也可以配置成服务的提供者,非常的灵活。
远程调用服务还需要一个重要的技术就是通讯技术,这里的通讯技术我推荐netty,netty是个非常好的选择,讲到通讯是个复杂的课题,如果以后有空我再做详细介绍,通讯层的东西是封装到服务提供者和服务调用者引入的jar包里,但是通讯的ip地址和端口号则是需要远程调用管理组件推送过来的。
那么在应用里远程调用服务到底如何使用了?哈哈,这时候spring就要上场了,我们看看服务调用者和服务提供者的spring配置,如下所示:
我们发现这个新配置和以前不同了,这个配置将更加适合生成的开发。
我们首先看看serverProvider的设计,这个bean对应的class是cn.com.sharpxiajun.RmifSpringProviderBean,里面有个参数是一个interfaceName即提供者对外的接口,这里我会使用反射机制将接口注入到RmifSpringProviderBean,而target则是具体的实现对象了,这就是业务对象,注意interfaceName一定要是接口,因为调用者会根据接口进行转化,如果是类的话,那么通用性就很差了。
clientConsumer的设计,这个bean所对应的class是cn.com.sharpxiajun.RmifSpringConsumerBean,其中interfaceName的value值对应的就是远程定义的接口,和提供者的interfaceName保持一致,当提供者的数据传导调用者后,就会根据这个双方约定好的接口反序列化成可以操作的对象,serialType是选择序列化机制,不写的话就是调用jdk的序列化机制,这里附带提下啊,外部的序列化程序也是放到jar包里的哦,还有一个选项是compressEnabled作用是是否启用传输报文压缩。
当调用者调用提供者服务时候,jar包里netty程序会根据推送的信息(主要是ip,端口)和spring配置的bean结合起来就可以完成一次服务的调用。
好了,上篇写好了,本篇主要是讲解远程调用服务的架构设计,我自我感觉这篇文章比上篇更接地气,希望看了本文的童鞋,能对远程调用框架设计的原理更加清晰。
其实netty的使用学问也很大,也是远程调用服务的核心之一,本文这块讲的比较少,以后有时间我尽量补充上这块知识。
下篇文章我将详细介绍远程调用框架里使用到的zookeeper技术。
这是2013年的最后一篇博文了,祝大家新年快乐哦。
本文链接:http://www.cnblogs.com/sharpxiajun/p/3496639.html,转载请注明。
博客园_首页 于 2013-12-29 19:31:19
Windows服务编程Demo - 七月流光
实现一个开机自动启动的关机程序,具体代码如下:
1 #include
2
3 void ServiceMain();
4 void ControlHandler(DWORD request);
5 void RunService();
6 void Shutdown();
7 BOOL IsInstall();
8 BOOL Install();
9
10 SERVICE_STATUS_HANDLE hServiceStatus;
11 SERVICE_STATUS status;
12
13 int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )
14 {
15 SERVICE_TABLE_ENTRY st[] =
16 {
17 {TEXT("Shutdown"), (LPSERVICE_MAIN_FUNCTION)ServiceMain},
18 {NULL, NULL}
19 };
20
21 if (!IsInstall())
22 {
23 Install();
24 }
25
26 StartServiceCtrlDispatcher(st);
27
28 RunService();
29
30 return 0;
31 }
32
33 void RunService()
34 {
35 SC_HANDLE schSCManager;
36 SC_HANDLE schService;
37
38 schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
39 if (!schSCManager)
40 {
41 MessageBox(NULL, TEXT("OpenSCManager error"), TEXT("error"), MB_OK | MB_ICONERROR);
42 return;
43 }
44
45 schService = OpenService(schSCManager, TEXT("Shutdown"), SERVICE_ALL_ACCESS);
46 if (!schService)
47 {
48 MessageBox(NULL, TEXT("OpenService error"), TEXT("error"), MB_OK | MB_ICONERROR);
49 }
50
51 BOOL b = StartService(schService, 0, NULL);
52 }
53
54 void ControlHandler(DWORD request)
55 {
56 switch (request)
57 {
58 case SERVICE_CONTROL_STOP:
59 case SERVICE_CONTROL_SHUTDOWN:
60 status.dwWin32ExitCode = 0;
61 status.dwCurrentState = SERVICE_STOPPED;
62 break;
63 default:
64 break;
65 }
66 SetServiceStatus(hServiceStatus, &status);
67 }
68
69 void ServiceMain()
70 {
71 status.dwServiceType = SERVICE_WIN32;
72 status.dwCurrentState = SERVICE_START_PENDING;
73 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
74 status.dwWin32ExitCode = 0;
75 status.dwServiceSpecificExitCode = 0;
76 status.dwCheckPoint = 0;
77 status.dwWaitHint = 0;
78
79 hServiceStatus = RegisterServiceCtrlHandler(TEXT("Shutdown"), (LPHANDLER_FUNCTION)ControlHandler);
80 if ((SERVICE_STATUS_HANDLE)0 == hServiceStatus)
81 {
82 return;
83 }
84
85 status.dwCurrentState = SERVICE_RUNNING;
86 SetServiceStatus(hServiceStatus, &status);
87 Shutdown();
88 }
89
90 void Shutdown()
91 {
92 OSVERSIONINFO os;
93 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
94 GetVersionEx(&os);
95 if (VER_PLATFORM_WIN32_NT == os.dwPlatformId)
96 {
97 HANDLE handle;
98 TOKEN_PRIVILEGES tkp;
99
100 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &handle);
101 LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
102
103 tkp.PrivilegeCount = 1;
104 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
105
106 AdjustTokenPrivileges(handle, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
107
108 if (GetLastError() != ERROR_SUCCESS)
109 {
110 MessageBox(NULL, TEXT("AdjustTokenPrivileges error"), TEXT("error"), MB_OK | MB_ICONERROR);
111 }
112 }
113
114 ExitWindowsEx(EWX_SHUTDOWN, 0);
115 }
116
117 BOOL IsInstall()
118 {
119 BOOL bResult = FALSE;
120
121 SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
122 if (schSCManager)
123 {
124 SC_HANDLE schService = OpenService(schSCManager, TEXT("Shutdown"), SERVICE_ALL_ACCESS);
125 if (schService)
126 {
127 bResult = TRUE;
128 CloseServiceHandle(schService);
129 }
130 CloseServiceHandle(schSCManager);
131 }
132 else
133 {
134 MessageBox(NULL, TEXT("OpenSCManager error"), TEXT("error"), MB_OK | MB_ICONERROR);
135 }
136
137 return bResult;
138 }
139
140 BOOL Install()
141 {
142 SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
143 if (!schSCManager)
144 {
145 MessageBox(NULL, TEXT("OpenSCManager error"), TEXT("error"), MB_OK | MB_ICONERROR);
146 return FALSE;
147 }
148
149 TCHAR szFilePath[MAX_PATH];
150 GetModuleFileName(NULL, szFilePath, MAX_PATH);
151
152 SC_HANDLE schService = CreateService(schSCManager, TEXT("Shutdown"), TEXT("Shutdown"), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL, TEXT(""), NULL, NULL);
153 if (!schService)
154 {
155 CloseServiceHandle(schSCManager);
156 MessageBox(NULL, TEXT("CreateService error"), TEXT("error"), MB_OK | MB_ICONERROR);
157 return FALSE;
158 }
159
160 CloseServiceHandle(schService);
161 CloseServiceHandle(schSCManager);
162
163 return TRUE;
164 }
本程序是开机自动关机的恶搞程序,本人亲自在物理机上测试,在windows server 2008 r2 enterprise版本的系统下测试通过。要解除本恶搞程序对你造成的不便,可以在开机时按F8进入安全模式下删除名字为Shutdown的服务即可。 输入命令: sc delete Shutdown
程序下载连接:http://pan.baidu.com/share/link?shareid=329612&uk=638387423
本文链接:http://www.cnblogs.com/qiyueliuguang/p/3496620.html,转载请注明。
博客园_首页 于 2013-12-29 19:16:49
给iOS开发新手送点福利,简述文本属性Attributes的用法 - 小木盒
文本属性Attributes
1.NSKernAttributeName: @10 调整字句 kerning 字句调整
2.NSFontAttributeName : [UIFont systemFontOfSize:_fontSize] 设置字体
3.NSForegroundColorAttributeName :[UIColor redColor] 设置文字颜色
4.NSParagraphStyleAttributeName : paragraph 设置段落样式
5.NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.alignment = NSTextAlignmentCenter;
6.NSBackgroundColorAttributeName: [UIColor blackColor] 设置背景颜色
7.NSStrokeColorAttributeName设置文字描边颜色,需要和NSStrokeWidthAttributeName设置描边宽度,这样就能使文字空心.
NSStrokeWidthAttributeName这个属性所对应的值是一个 NSNumber 对象(小数)。该值改变描边宽度(相对于字体size 的百分比)。默认为 0,即不改变。正数只改变描边宽度。负数同时改变文字的描边和填充宽度。例如,对于常见的空心字,这个值通常为3.0。
同时设置了空心的两个属性,并且NSStrokeWidthAttributeName属性设置为整数,文字前景色就无效果了
效果:
效果:
8. NSStrikethroughStyleAttributeName 添加删除线,strikethrough删除线
效果:
9. NSUnderlineStyleAttributeName 添加下划线
效果:
10. NSShadowAttributeName 设置阴影,单独设置不好使,必须和其他属性搭配才好使。
和这三个任一个都好使,NSVerticalGlyphFormAttributeName,NSObliquenessAttributeName,NSExpansionAttributeName
11.NSVerticalGlyphFormAttributeName
该属性所对应的值是一个 NSNumber 对象(整数)。0 表示横排文本。1 表示竖排文本。在 iOS 中,总是使用横排文本,0 以外的值都未定义。
效果:
12. NSObliquenessAttributeName设置字体倾斜。Skew 斜
效果:
13. NSExpansionAttributeName 设置文本扁平化
效果:
本文链接:http://www.cnblogs.com/xmqios/p/3496615.html,转载请注明。
博客园_首页 于 2013-12-29 19:16:52
【iScroll源码学习01】准备阶段 - 叶小钗
前言
我们昨天初步了解了为什么会出现iScroll:【SPA】移动站点APP化研究之上中下页面的iScroll化(上),然后简单的写了一个demo来模拟iScroll,其中了解到了以下知识点:
① viewport相关知识点(device-width等)
② CSS3硬件加速
③ 如何暂停CSS动画
④ e.preventDefault导致文本不能获取焦点解决方案
......
当然,我们写的demo自然不能和iScroll本身的代码比肩,但是demo过程中我们也大概了解了iScroll代码过程中需要注意的一些问题
于是,今天让我们进入iScroll的学习吧
初探iScroll
1
2
3
4
5
6
7
8
9
10
11
12
13
14 var myScroll;
15
16 function loaded () {
17 myScroll = new IScroll('#wrapper', {
18 scrollbars: true,
19 mouseWheel: true,
20 interactiveScrollbars: true,
21 shrinkScrollbars: 'scale',
22 fadeScrollbars: true
23 });
24 }
25
26 document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
27
28
29
30
31 * {
32 -webkit-box-sizing: border-box;
33 -moz-box-sizing: border-box;
34 box-sizing: border-box;
35 }
36
37 html {
38 -ms-touch-action: none;
39 }
40
41 body,ul,li {
42 padding: 0;
43 margin: 0;
44 border: 0;
45 }
46
47 body {
48 font-size: 12px;
49 font-family: ubuntu, helvetica, arial;
50 overflow: hidden; /* this is important to prevent the whole page to bounce */
51 }
52
53 #header {
54 position: absolute;
55 z-index: 2;
56 top: 0;
57 left: 0;
58 width: 100%;
59 height: 45px;
60 line-height: 45px;
61 background: #CD235C;
62 padding: 0;
63 color: #eee;
64 font-size: 20px;
65 text-align: center;
66 font-weight: bold;
67 }
68
69 #footer {
70 position: absolute;
71 z-index: 2;
72 bottom: 0;
73 left: 0;
74 width: 100%;
75 height: 48px;
76 background: #444;
77 padding: 0;
78 border-top: 1px solid #444;
79 }
80
81 #wrapper {
82 position: absolute;
83 z-index: 1;
84 top: 45px;
85 bottom: 48px;
86 left: 0;
87 width: 100%;
88 background: #ccc;
89 overflow: hidden;
90 }
91
92 #scroller {
93 position: absolute;
94 z-index: 1;
95 -webkit-tap-highlight-color: rgba(0,0,0,0);
96 width: 100%;
97 -webkit-transform: translateZ(0);
98 -moz-transform: translateZ(0);
99 -ms-transform: translateZ(0);
100 -o-transform: translateZ(0);
101 transform: translateZ(0);
102 -webkit-touch-callout: none;
103 -webkit-user-select: none;
104 -moz-user-select: none;
105 -ms-user-select: none;
106 user-select: none;
107 -webkit-text-size-adjust: none;
108 -moz-text-size-adjust: none;
109 -ms-text-size-adjust: none;
110 -o-text-size-adjust: none;
111 text-size-adjust: none;
112 }
113
114 #scroller ul {
115 list-style: none;
116 padding: 0;
117 margin: 0;
118 width: 100%;
119 text-align: left;
120 }
121
122 #scroller li {
123 padding: 0 10px;
124 height: 40px;
125 line-height: 40px;
126 border-bottom: 1px solid #ccc;
127 border-top: 1px solid #fff;
128 background-color: #fafafa;
129 font-size: 14px;
130 }
131
132
133
134
135
136
137
138
139
- Pretty row 1
- Pretty row 2
- Pretty row 3
- Pretty row 4
- Pretty row 5
- Pretty row 6
- Pretty row 7
- Pretty row 8
- Pretty row 9
- Pretty row 10
- Pretty row 11
- Pretty row 12
- Pretty row 13
- Pretty row 14
- Pretty row 15
- Pretty row 16
- Pretty row 17
- Pretty row 18
- Pretty row 19
- Pretty row 20
- Pretty row 21
- Pretty row 22
- Pretty row 23
- Pretty row 24
- Pretty row 25
- Pretty row 26
- Pretty row 27
- Pretty row 28
- Pretty row 29
- Pretty row 30
- Pretty row 31
- Pretty row 32
- Pretty row 33
- Pretty row 34
- Pretty row 35
- Pretty row 36
- Pretty row 37
- Pretty row 38
- Pretty row 39
- Pretty row 40
- Pretty row 41
- Pretty row 42
- Pretty row 43
- Pretty row 44
- Pretty row 45
- Pretty row 46
- Pretty row 47
- Pretty row 48
- Pretty row 49
- Pretty row 50
- Pretty row 1
- Pretty row 2
- Pretty row 3
- Pretty row 4
- Pretty row 5
- Pretty row 6
- Pretty row 7
- Pretty row 8
- Pretty row 9
- Pretty row 10
- Pretty row 11
- Pretty row 12
- Pretty row 13
- Pretty row 14
- Pretty row 15
- Pretty row 16
- Pretty row 17
- Pretty row 18
- Pretty row 19
- Pretty row 20
- Pretty row 21
- Pretty row 22
- Pretty row 23
- Pretty row 24
- Pretty row 25
- Pretty row 26
- Pretty row 27
- Pretty row 28
- Pretty row 29
- Pretty row 30
- Pretty row 31
- Pretty row 32
- Pretty row 33
- Pretty row 34
- Pretty row 35
- Pretty row 36
- Pretty row 37
- Pretty row 38
- Pretty row 39
- Pretty row 40
- Pretty row 41
- Pretty row 42
- Pretty row 43
- Pretty row 44
- Pretty row 45
- Pretty row 46
- Pretty row 47
- Pretty row 48
- Pretty row 49
- Pretty row 50
- Pretty row 1
- Pretty row 2
- Pretty row 3
- Pretty row 4
- Pretty row 5
- Pretty row 6
- Pretty row 7
- Pretty row 8
- Pretty row 9
- Pretty row 10
- Pretty row 11
- Pretty row 12
- Pretty row 13
- Pretty row 14
- Pretty row 15
- Pretty row 16
- Pretty row 17
- Pretty row 18
- Pretty row 19
- Pretty row 20
- Pretty row 21
- Pretty row 22
- Pretty row 23
- Pretty row 24
- Pretty row 25
- Pretty row 26
- Pretty row 27
- Pretty row 28
- Pretty row 29
- Pretty row 30
- Pretty row 31
- Pretty row 32
- Pretty row 33
- Pretty row 34
- Pretty row 35
- Pretty row 36
- Pretty row 37
- Pretty row 38
- Pretty row 39
- Pretty row 40
- Pretty row 41
- Pretty row 42
- Pretty row 43
- Pretty row 44
- Pretty row 45
- Pretty row 46
- Pretty row 47
- Pretty row 48
- Pretty row 49
- Pretty row 50
- Pretty row 1
- Pretty row 2
- Pretty row 3
- Pretty row 4
- Pretty row 5
- Pretty row 6
- Pretty row 7
- Pretty row 8
- Pretty row 9
- Pretty row 10
- Pretty row 11
- Pretty row 12
- Pretty row 13
- Pretty row 14
- Pretty row 15
- Pretty row 16
- Pretty row 17
- Pretty row 18
- Pretty row 19
- Pretty row 20
- Pretty row 21
- Pretty row 22
- Pretty row 23
- Pretty row 24
- Pretty row 25
- Pretty row 26
- Pretty row 27
- Pretty row 28
- Pretty row 29
- Pretty row 30
- Pretty row 31
- Pretty row 32
- Pretty row 33
- Pretty row 34
- Pretty row 35
- Pretty row 36
- Pretty row 37
- Pretty row 38
- Pretty row 39
- Pretty row 40
- Pretty row 41
- Pretty row 42
- Pretty row 43
- Pretty row 44
- Pretty row 45
- Pretty row 46
- Pretty row 47
- Pretty row 48
- Pretty row 49
- Pretty row 50
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
http://sandbox.runjs.cn/show/pscjy3a3
下面是他初始化时候的核心代码:
1 var myScroll;
2 function loaded () {
3 myScroll = new IScroll('#wrapper', {
4 scrollbars: true,
5 mouseWheel: true,
6 interactiveScrollbars: true,
7 shrinkScrollbars: 'scale',
8 fadeScrollbars: true
9 });
10 }
11 document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
真不得不说,这个滑动起来感觉挺不错的,第一感觉有几大特点:
① 顺畅
② 连续拖动会加速滑动
③ 没有BUG :)
看看他的构造函数,然后去网上找一点参数说明(Iscroll应用文档):
hScroll: true, //是否水平滚动
vScroll: true, //是否垂直滚动
x: 0, //滚动水平初始位置
y: 0, //滚动垂直初始位置
snap: true, //值可以为true或是DOM元素的tagname,当为true时,对齐的坐标会根据可滚动的位置和滚动区域计算得到可滑动几页,如果为tagname,则滑动会对齐到元素上
bounce: true, //是否超过实际位置反弹
bounceLock: false, //当内容少于滚动是否可以反弹,这个实际用处不大
momentum: true, //动量效果,拖动惯性
lockDirection: true, //当水平滚动和垂直滚动同时生效时,当拖动开始是否锁定另一边的拖动
useTransform: true, //是否使用CSS形变
useTransition: false, //是否使用CSS变换
topOffset: 0, //已经滚动的基准值(一般情况用不到)
checkDOMChanges: false, //是否自动检测内容变化(这个检测不是很准)
//Scrollbar相关参数,通过scrollbar这些参数可以配置iscroll的滚动条,通过scrollbarClass可以自己定义一套滚动条的样式。
hScrollbar: true, //是否显示水平滚动条
vScrollbar: true, //同上垂直滚动条
fixedScrollbar: isAndroid, //对andriod的fixed
hideScrollbar: isIDevice, //是否隐藏滚动条
fadeScrollbar: isIDevice && has3d, //滚动条是否渐隐渐显
scrollbarClass: '', //自定义滚动条的样式名
//Zoom放大相关的参数,通过它,对于一个固定显示图片区域的类似应用,可以非常简单的做到固定滚动,包括两指放大的应用。
zoom: false, //默认是否放大
zoomMin: 1, //放大的最小倍数
zoomMax: 4, //最大倍数
doubleTapZoom: 2, //双触放大几倍
wheelAction: 'scroll', //鼠标滚动行为(还可以是zoom)
//自定义Events相关参数
onRefresh: null, //refresh 的回调,关于自身何时调用refresh 后面会继续谈到
onBeforeScrollStart: function(e){ e.preventDefault(); }, //开始滚动前的时间回调,默认是阻止浏览器默认行为
onScrollStart: null, //开始滚动的回调
onBeforeScrollMove: null, //在内容移动前的回调
onScrollMove: null, //内容移动的回调
onBeforeScrollEnd: null, //在滚动结束前的回调
onScrollEnd: null, //在滚动完成后的回调
onTouchEnd: null, //手离开屏幕后的回调
onDestroy: null, //销毁实例的回调
onZoomStart: null, //开始放大前的回调
onZoom: null, //放大的回调
onZoomEnd: null //放大完成后的回调
Iscroll 提供的调用方法
destroy
顾名思义,是用来销毁你实例化的iScroll 实例,包括之前绑定的所有iscroll 事件。
refresh
这个方法非常有用,当你的滚动区域的内容发生改变 或是 滚动区域不正确,都用通过调用refresh 来使得iscroll 重新计算滚动的区域,包括滚动条,来使得iscroll 适合当前的dom。
scrollTo
这个方法接受4个参数 x, y, time, relative x 为移动的x轴坐标,y为移动的y轴坐标, time为移动时间,relative表示是否相对当前位置。
scrollToElement
这个方法实际上是对scrollTo的进一步封装,接受两个参数(el,time),el为需要滚动到的元素引用,time为滚动时间。
scrollToPage
此方法接受三个参数(pageX,pageY,time) 当滚动内容的高宽大于滚动范围时,iscroll 会自动分页,然后就能使用scrollToPage方法滚动到页面。当然,当hscroll 为false 的时候,不能左右滚动。pageX这个参数就失去效果
disable
调用这个方法会立即停止动画滚动,并且把滚动位置还原成0,取消绑定touchmove, touchend、touchcancel事件。
enable
调用这个方法,使得iscroll恢复默认正常状态
stop
立即停止动画
zoom
改变内容的大小倍数,此方法接受4个参数,x,y,scale,time 分别表示的意思为,放大的基准坐标,以及放大倍数,动画时间
isReady
当iscroll 没有处于正在滚动,没有移动过,没有改变大小时,此值为true
功能非常丰富啊,对于应用来说够用了,但是一些功能我这里用不到,就忽略了
功能很好,size为48k,压缩后稍微好一点,将近2000行的代码,作为基础库来说,有点大了,比整个zepto还大
而且整个库的注释写的不好,好像压根就没写......不知道阅读上会不会有障碍,于是我们进入源码
iScroll笔记
requestAnimationFrame
1 var rAF = window.requestAnimationFrame ||
2 window.webkitRequestAnimationFrame ||
3 window.mozRequestAnimationFrame ||
4 window.oRequestAnimationFrame ||
5 window.msRequestAnimationFrame ||
6 function (callback) { window.setTimeout(callback, 1000 / 60); };
这段代码是要做能力检测的,这里来说一下requestAnimationFrame这个东西(参考:http://www.kimhou.com/?p=155)
在jquery中javascript动画是通过定时器(settimeout)实现的,没一个时间点改变一点style,而CSS3后便推出了transition以及animation开始实现动画
我们昨天提到的硬件加速,也是CSS3相关的东西。CSS3动画效率与顺畅度比Js高,所以现在动画开始楚河汉界了
js的好处是可以很好的控制动画状态、css动画带来的性能较高,但是控制度就低一点(是很低)
定时器实现动画
1
2
3
4
5
6
7 Javascript时钟实现动画
8
9
10
11 var pos = 0;
12 var final = 200;
13 var dir = 0;
14 var el = $('#el');
15 function upLeft() {
16 var left = parseInt(el.css('left')) || 0;
17 if (left >= final) dir = 1;
18 if (left <= pos) dir = 0;
19
20 if (dir == 0) {
21 left++;
22 el.css('left', left + 'px');
23 setTimeout(upLeft);
24 } else {
25 left--;
26 el.css('left', left + 'px');
27 setTimeout(upLeft);
28 }
29 }
30 upLeft();
31
32
33
效果见下面
这便是使用javascript实现的一个最简单的动画,各位看到了,里面的定时器不停的在运动,性能不差的话我就改名叫素还真了
这个阶段,比较棘手的问题往往在延迟的计算,间隔要短所以动画顺畅,但是浏览器渲染也得耗费时间,这个就要求每次变化留给浏览器的时间够长了(60HZ/75Hz)
所以之前javascript的间隔一般为20左右,这个时候的动画比较流畅,这个数字与浏览器的频率比较接近(1000/60)
function (callback) { window.setTimeout(callback, 1000 / 60); }
但是通过前面对时钟的学习,我们知道settimeout只是将回调函数加入UI线程队列,那么同一时间有多个动画待执行的话,延迟就发生了,效果也会打折扣
这里原来用js实现坦克大战的朋友就会有所体会了
javascript问题解决
CSS的transition与animations的优势在于浏览器知道哪些动画将会发生,所以动画会得到正确的间隔来刷新UI(javascript当然不知道)
于是这里就多了一个方法:RequestAnimationFrame,他可以告诉浏览器自己要执行动画了,于是js的动画事实上得到了优化
RequestAnimationFrame接受一个参数,也就是屏幕重绘前会调用的函数,这个函数用来改变dom样式,这个方法使用有点类似于settimeout
1
2
3
4
5
6
7
8
9 Javascript时钟实现动画
10
11
12
13 var rAF = window.requestAnimationFrame ||
14 window.webkitRequestAnimationFrame ||
15 window.mozRequestAnimationFrame ||
16 window.oRequestAnimationFrame ||
17 window.msRequestAnimationFrame ||
18 function (callback) { window.setTimeout(callback, 1000 / 60); };
19 var pos = 0;
20 var final = 200;
21 var dir = 0;
22 var el = $('#el');
23 function upLeft() {
24 var left = parseInt(el.css('left')) || 0;
25 if (left >= final) dir = 1;
26 if (left <= pos) dir = 0;
27
28 if (dir == 0) {
29 left++;
30 el.css('left', left + 'px');
31 rAF(upLeft);
32 } else {
33 left--;
34 el.css('left', left + 'px');
35 rAF(upLeft);
36 }
37 }
38 upLeft();
39
40
41
这个动画将会有不一样的感受:
动画效果相关,各位自己去感受,代码各位可以自己调整。如此这个方法其实就是做javascript动画处理优化方案的
PS:尼玛,iScroll第一段就搞了这么久啊这么久
utils
然后iScroll将自己下面会用到的常用操作封装到了这个对象中——utils。
1 var utils = (function () {
2 var me = {};
3
4 var _elementStyle = document.createElement('div').style;
5 var _vendor = (function () {
6 var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
7 transform,
8 i = 0,
9 l = vendors.length;
10
11 for ( ; i < l; i++ ) {
12 transform = vendors[i] + 'ransform';
13 if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
14 }
15
16 return false;
17 })();
18
19 function _prefixStyle (style) {
20 if ( _vendor === false ) return false;
21 if ( _vendor === '' ) return style;
22 return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
23 }
24
25 me.getTime = Date.now || function getTime () { return new Date().getTime(); };
26
27 me.extend = function (target, obj) {
28 for ( var i in obj ) {
29 target[i] = obj[i];
30 }
31 };
32
33 me.addEvent = function (el, type, fn, capture) {
34 el.addEventListener(type, fn, !!capture);
35 };
36
37 me.removeEvent = function (el, type, fn, capture) {
38 el.removeEventListener(type, fn, !!capture);
39 };
40
41 me.momentum = function (current, start, time, lowerMargin, wrapperSize) {
42 var distance = current - start,
43 speed = Math.abs(distance) / time,
44 destination,
45 duration,
46 deceleration = 0.0006;
47
48 destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
49 duration = speed / deceleration;
50
51 if ( destination < lowerMargin ) {
52 destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
53 distance = Math.abs(destination - current);
54 duration = distance / speed;
55 } else if ( destination > 0 ) {
56 destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
57 distance = Math.abs(current) + destination;
58 duration = distance / speed;
59 }
60
61 return {
62 destination: Math.round(destination),
63 duration: duration
64 };
65 };
66
67 var _transform = _prefixStyle('transform');
68
69 me.extend(me, {
70 hasTransform: _transform !== false,
71 hasPerspective: _prefixStyle('perspective') in _elementStyle,
72 hasTouch: 'ontouchstart' in window,
73 hasPointer: navigator.msPointerEnabled,
74 hasTransition: _prefixStyle('transition') in _elementStyle
75 });
76
77 // This should find all Android browsers lower than build 535.19 (both stock browser and webview)
78 me.isBadAndroid = /Android/.test(window.navigator.appVersion) && !(/Chrome\/\d/.test(window.navigator.appVersion));
79
80 me.extend(me.style = {}, {
81 transform: _transform,
82 transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
83 transitionDuration: _prefixStyle('transitionDuration'),
84 transitionDelay: _prefixStyle('transitionDelay'),
85 transformOrigin: _prefixStyle('transformOrigin')
86 });
87
88 me.hasClass = function (e, c) {
89 var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
90 return re.test(e.className);
91 };
92
93 me.addClass = function (e, c) {
94 if ( me.hasClass(e, c) ) {
95 return;
96 }
97
98 var newclass = e.className.split(' ');
99 newclass.push(c);
100 e.className = newclass.join(' ');
101 };
102
103 me.removeClass = function (e, c) {
104 if ( !me.hasClass(e, c) ) {
105 return;
106 }
107
108 var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
109 e.className = e.className.replace(re, ' ');
110 };
111
112 me.offset = function (el) {
113 var left = -el.offsetLeft,
114 top = -el.offsetTop;
115
116 // jshint -W084
117 while (el = el.offsetParent) {
118 left -= el.offsetLeft;
119 top -= el.offsetTop;
120 }
121 // jshint +W084
122
123 return {
124 left: left,
125 top: top
126 };
127 };
128
129 me.preventDefaultException = function (el, exceptions) {
130 for ( var i in exceptions ) {
131 if ( exceptions[i].test(el[i]) ) {
132 return true;
133 }
134 }
135
136 return false;
137 };
138
139 me.extend(me.eventType = {}, {
140 touchstart: 1,
141 touchmove: 1,
142 touchend: 1,
143
144 mousedown: 2,
145 mousemove: 2,
146 mouseup: 2,
147
148 MSPointerDown: 3,
149 MSPointerMove: 3,
150 MSPointerUp: 3
151 });
152
153 me.extend(me.ease = {}, {
154 quadratic: {
155 style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
156 fn: function (k) {
157 return k * ( 2 - k );
158 }
159 },
160 circular: {
161 style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
162 fn: function (k) {
163 return Math.sqrt( 1 - ( --k * k ) );
164 }
165 },
166 back: {
167 style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
168 fn: function (k) {
169 var b = 4;
170 return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
171 }
172 },
173 bounce: {
174 style: '',
175 fn: function (k) {
176 if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
177 return 7.5625 * k * k;
178 } else if ( k < ( 2 / 2.75 ) ) {
179 return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
180 } else if ( k < ( 2.5 / 2.75 ) ) {
181 return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
182 } else {
183 return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
184 }
185 }
186 },
187 elastic: {
188 style: '',
189 fn: function (k) {
190 var f = 0.22,
191 e = 0.4;
192
193 if ( k === 0 ) { return 0; }
194 if ( k == 1 ) { return 1; }
195
196 return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
197 }
198 }
199 });
200
201 me.tap = function (e, eventName) {
202 var ev = document.createEvent('Event');
203 ev.initEvent(eventName, true, true);
204 ev.pageX = e.pageX;
205 ev.pageY = e.pageY;
206 e.target.dispatchEvent(ev);
207 };
208
209 me.click = function (e) {
210 var target = e.target,
211 ev;
212
213 if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {
214 ev = document.createEvent('MouseEvents');
215 ev.initMouseEvent('click', true, true, e.view, 1,
216 target.screenX, target.screenY, target.clientX, target.clientY,
217 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
218 0, null);
219
220 ev._constructed = true;
221 target.dispatchEvent(ev);
222 }
223 };
224
225 return me;
226 })();
兼容性检测
很烦的事情一而再再而三的在浏览器上面出现,比如我们在chrome要定义动画参数得加上一个前缀webkit,然后ff要使用MozT,这个事情很烦,所以iScroll这段代码就在处理这个事情
1 var _vendor = (function () {
2 var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
3 transform,
4 i = 0,
5 l = vendors.length;
6
7 for ( ; i < l; i++ ) {
8 transform = vendors[i] + 'ransform';
9 if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
10 }
11
12 return false;
13 })();
14
15 function _prefixStyle (style) {
16 if ( _vendor === false ) return false;
17 if ( _vendor === '' ) return style;
18 return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
19 }
这里做了几个操作:
① 动态创建标签样式——_elementStyle
② 检测样式支持度,并且返回需要的前缀
③ 获取验证结果,比如在chrome下变会返回webkit-XXX
当然,这里要加前缀的样式,一般都与CSS3有关,而下面就会遇到是transform
getTime
me.getTime = Date.now || function getTime () { return new Date().getTime(); };
//获取当前时间戳
extend
最最简单的扩展对象的方法
1 me.extend = function (target, obj) {
2 for ( var i in obj ) {
3 target[i] = obj[i];
4 }
5 };
addEvent/removeEvent
事件注册相关,我在想,若是使用了zepto这个代码量会减少点么?
1 me.addEvent = function (el, type, fn, capture) {
2 el.addEventListener(type, fn, !!capture);
3 };
4
5 me.removeEvent = function (el, type, fn, capture) {
6 el.removeEventListener(type, fn, !!capture);
7 };
momentum
这个方法比较重要,用于计算动画参数,会根据这个计算结果而决定动画运动效果,其实我们昨天的demo也用到了类似的东西
1 me.momentum = function (current, start, time, lowerMargin, wrapperSize) {
2 var distance = current - start,
3 speed = Math.abs(distance) / time,
4 destination,
5 duration,
6 deceleration = 0.0006;
7
8 destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
9 duration = speed / deceleration;
10
11 if ( destination < lowerMargin ) {
12 destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
13 distance = Math.abs(destination - current);
14 duration = distance / speed;
15 } else if ( destination > 0 ) {
16 destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
17 distance = Math.abs(current) + destination;
18 duration = distance / speed;
19 }
20
21 return {
22 destination: Math.round(destination),
23 duration: duration
24 };
25 };
我们来做一点解释,首先看看我们的参数:
① 这个方法应该是在touchend时候使用,第一个参数为当前鼠标的位置
② 第二个参数是touchstart时候记录的坐标位置
③ 第三个参数为时间参数,便是开始触屏到离开时候所用时间(touchstart到touchend)
PS:这里我们其实可以做一个猜测了,我们有一次触屏的时间与距离,自然可以根据动力加速度计算出此次应该运动的时间与距离
④ 第四个参数是干神马的还不太明确,应该是控制边界位置的,这个就决定了我们不能无限制的拖动wrapper
⑤ 第五个参数为容器的高度
然后我们来以此读一读这里的代码:
① 得出此次拉动的距离distance/然后计算出这次拖动的速度(PS:个人觉得这里操作很不错,我没有想到)
② 然后定义了一些其它参数,deceleration这个用于计算速度/距离的参数,然后两个就是要返回的距离以及时间了
PS:我想说他这里的计算最终位置的函数应该是物理里面的一个计算摩擦参数的公式,尼玛是什么我真的不知道了,还有平方来着......
③ 这里还有一个关键点就是distance有可能是负值,这个会决定向上还是向下运动
④ 一般情况这里就结束来了,然后下面if里面一大段计算是用于处理运动轨迹超出时候的距离与速度重新计算(反弹效果)
好了,这个函数比较关键,他主要返回了最后要去到的位置,已经到这个位置的时间,里面具体的实现我们暂时不关系,后面这个必须理一理
检测与初始化
接下来做了一大段能力检测,比如:
① 是否支持CSS3动画相关(transform、transition)
② 是否支持touch事件
然后做了一些简单的初始化操作,这里新增了一个style对象,为他赋予了CSS动画相关的属性
③ 接下来是一些简单的样式操作,这样有个函数需要注意,他可以获取一个元素真正的位置信息
1 me.offset = function (el) {
2 var left = -el.offsetLeft,
3 top = -el.offsetTop;
4
5 // jshint -W084
6 while (el = el.offsetParent) {
7 left -= el.offsetLeft;
8 top -= el.offsetTop;
9 }
10 // jshint +W084
11
12 return {
13 left: left,
14 top: top
15 };
16 };
④ 动画曲线
1 me.extend(me.ease = {}, {
2 quadratic: {
3 style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
4 fn: function (k) {
5 return k * ( 2 - k );
6 }
7 },
8 circular: {
9 style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
10 fn: function (k) {
11 return Math.sqrt( 1 - ( --k * k ) );
12 }
13 },
14 back: {
15 style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
16 fn: function (k) {
17 var b = 4;
18 return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
19 }
20 },
21 bounce: {
22 style: '',
23 fn: function (k) {
24 if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
25 return 7.5625 * k * k;
26 } else if ( k < ( 2 / 2.75 ) ) {
27 return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
28 } else if ( k < ( 2.5 / 2.75 ) ) {
29 return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
30 } else {
31 return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
32 }
33 }
34 },
35 elastic: {
36 style: '',
37 fn: function (k) {
38 var f = 0.22,
39 e = 0.4;
40
41 if ( k === 0 ) { return 0; }
42 if ( k == 1 ) { return 1; }
43
44 return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
45 }
46 }
47 });
这里定义了动画曲线供我们选取,我一般使用linear......
PS:这个地方的代码,不明觉厉!!!
自定义点击事件
1 me.tap = function (e, eventName) {
2 var ev = document.createEvent('Event');
3 ev.initEvent(eventName, true, true);
4 ev.pageX = e.pageX;
5 ev.pageY = e.pageY;
6 e.target.dispatchEvent(ev);
7 };
8
9 me.click = function (e) {
10 var target = e.target,
11 ev;
12
13 if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {
14 ev = document.createEvent('MouseEvents');
15 ev.initMouseEvent('click', true, true, e.view, 1,
16 target.screenX, target.screenY, target.clientX, target.clientY,
17 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
18 0, null);
19
20 ev._constructed = true;
21 target.dispatchEvent(ev);
22 }
23 };
iScroll这里干了一件坏事,自己定义了tap以及click,意思是他可以触发绑定到dom上的tap或者click事件
然后整个util便结束了,其中momentum方法很是关键,接下来我们跟着程序流程走了
构造函数
构造函数是iScroll的入口,我们来详细读一读:
wrapper/scroller
为我们的外层结构,再里面一点就是拖动元素了,iscroll的处理是认为wrapper下第一个元素就是可拖动元素,我这里任务不妥......
this.scroller = this.wrapper.children[0];
我们拖动的就是这个scroller了,我为什么说这样不妥呢?因为我如果现在又一个弹出层想使用iScroll的话,若是我弹出层有了wrapper了,我想自己往里面装DOM
而我的DOM搞不好有几个兄弟节点,这个时候我肯定不想自己再包裹一层的,所以iScroll这里的scroller我觉得系统构建比较合理(当然这只是个人认为)
下面还缓存了下当前scroll元素的style
this.scrollerStyle = this.scroller.style;
初始化参数
iScroll当然自己会初始化一些默认属性了:
1 this.options = {
2
3 resizeIndicator: true,
4
5 mouseWheelSpeed: 20,
6
7 snapThreshold: 0.334,
8
9 // INSERT POINT: OPTIONS
10
11 startX: 0,
12 startY: 0,
13 scrollY: true,
14 directionLockThreshold: 5,
15 momentum: true,
16
17 bounce: true,
18 bounceTime: 600,
19 bounceEasing: '',
20
21 preventDefault: true,
22 preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ },
23
24 HWCompositing: true,
25 useTransition: true,
26 useTransform: true
27 };
若是我们传了相关属性会被复写的(这里上面定义了extend,却没有使用):
1 for ( var i in options ) {
2 this.options[i] = options[i];
3 }
能力检测
然后下面一大片结果基本做了一些能力检测的功能,然后将检测结果保存,因为我暂时只关心纵向滑动,所以一些地方便不予关心了
1 this.x = 0;
2 this.y = 0;
3 this.directionX = 0;
4 this.directionY = 0;
5 this._events = {};
这一坨东西还是要关注的,方向和初始值
初始化_init
上面一些默认属性定义结束便进入真正的初始化阶段,
1 _init: function () {
2 this._initEvents();
3
4 if ( this.options.scrollbars || this.options.indicators ) {
5 this._initIndicators();
6 }
7
8 if ( this.options.mouseWheel ) {
9 this._initWheel();
10 }
11
12 if ( this.options.snap ) {
13 this._initSnap();
14 }
15
16 if ( this.options.keyBindings ) {
17 this._initKeys();
18 }
19 // INSERT POINT: _init
20 },
代码很清晰,我现在的需求关注_initEvents与_initIndicators就好了,其它暂时可以不管,关键点便是事件绑定了
_initEvents
1 _initEvents: function (remove) {
2 var eventType = remove ? utils.removeEvent : utils.addEvent,
3 target = this.options.bindToWrapper ? this.wrapper : window;
4
5 eventType(window, 'orientationchange', this);
6 eventType(window, 'resize', this);
7
8 if ( this.options.click ) {
9 eventType(this.wrapper, 'click', this, true);
10 }
11
12 if ( !this.options.disableMouse ) {
13 eventType(this.wrapper, 'mousedown', this);
14 eventType(target, 'mousemove', this);
15 eventType(target, 'mousecancel', this);
16 eventType(target, 'mouseup', this);
17 }
18
19 if ( utils.hasPointer && !this.options.disablePointer ) {
20 eventType(this.wrapper, 'MSPointerDown', this);
21 eventType(target, 'MSPointerMove', this);
22 eventType(target, 'MSPointerCancel', this);
23 eventType(target, 'MSPointerUp', this);
24 }
25
26 if ( utils.hasTouch && !this.options.disableTouch ) {
27 eventType(this.wrapper, 'touchstart', this);
28 eventType(target, 'touchmove', this);
29 eventType(target, 'touchcancel', this);
30 eventType(target, 'touchend', this);
31 }
32
33 eventType(this.scroller, 'transitionend', this);
34 eventType(this.scroller, 'webkitTransitionEnd', this);
35 eventType(this.scroller, 'oTransitionEnd', this);
36 eventType(this.scroller, 'MSTransitionEnd', this);
37 },
这段代码,是整个iScroll的核心,整个入口函数其实在这里,我们暂时的关注点又在这里:
1 if ( utils.hasTouch && !this.options.disableTouch ) {
2 eventType(this.wrapper, 'touchstart', this);
3 eventType(target, 'touchmove', this);
4 eventType(target, 'touchcancel', this);
5 eventType(target, 'touchend', this);
6 }
PS:这里有一点让我比较疑惑的就是这里传递进去的fn是一个对象,而不是函数,看来我事件机制一块仍然不到家:
然后进入我们的touch事件,反正现在touchstart便会进入我们的start回调函数
touchStart
1 _start: function (e) {
2 // React to left mouse button only
3 if ( utils.eventType[e.type] != 1 ) {
4 if ( e.button !== 0 ) {
5 return;
6 }
7 }
8
9 if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
10 return;
11 }
12
13 if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
14 e.preventDefault();
15 }
16
17 var point = e.touches ? e.touches[0] : e,
18 pos;
19
20 this.initiated = utils.eventType[e.type];
21 this.moved = false;
22 this.distX = 0;
23 this.distY = 0;
24 this.directionX = 0;
25 this.directionY = 0;
26 this.directionLocked = 0;
27
28 this._transitionTime();
29
30 this.startTime = utils.getTime();
31
32 if ( this.options.useTransition && this.isInTransition ) {
33 this.isInTransition = false;
34 pos = this.getComputedPosition();
35 this._translate(Math.round(pos.x), Math.round(pos.y));
36 this._execEvent('scrollEnd');
37 } else if ( !this.options.useTransition && this.isAnimating ) {
38 this.isAnimating = false;
39 this._execEvent('scrollEnd');
40 }
41
42 this.startX = this.x;
43 this.startY = this.y;
44 this.absStartX = this.x;
45 this.absStartY = this.y;
46 this.pointX = point.pageX;
47 this.pointY = point.pageY;
48
49 this._execEvent('beforeScrollStart');
50 }
前面做了一系列的兼容性处理,然后记录了一些数据便结束了
结语
今天有点晚了,我也暂时结束了,明天还要上班呢,下次详细研究下touch事件的几个阶段干的事情以及滚动条的实现
本文链接:http://www.cnblogs.com/yexiaochai/p/3496369.html,转载请注明。
博客园_首页 于 2013-12-29 19:16:26
提高代码质量系列之一:尽可能少写注释. - 碎景
关于<<提高代码质量系列>>
这是我新开的一个系列,旨在记录我对整个编码规范,代码风格,语法习惯的一些思考,感悟和总结. 至于asp.net系列,目前还在学习mvc中, 暂时还不太敢动笔.所以那个系列就先停了.
前言
不知道大家会不会觉得我的标题很噱头,不是一般应该提倡写注释的么?首先我得解释下,我这句话有两个意思!
1,绝非提倡不写注释,而是不要写不必要的注释.
2,命名规范的作用大于注释
好吧,这么一说,其实还是有点噱头的感觉的,因为我这篇文其实重心更放在强调命名规范上面,良好的命名规范,让你的代码有自释性,省去了注释的步骤.
还要强调下的是:这个观点绝非我自己主观臆断,凭空瞎想出来的. 而是实实在在由项目开发里面总结出来的.
为什么我有这个想法呢?请继续看我的蛋疼经历.
正文
先上段奇葩代码
///
/// 根据产品ID获取产品列表
///
/// 关键字
public DataTable GetColumnInfoByColumnID(int columnID)
{
return DALColumn.GetColumnInfoByColumnID(columnID);
}
///
/// 根据产品名称获取产品列表
///
/// 关键字
public DataTable GetColumnInfoByColumnID(string columnName)
{
return DALColumn.GetColumnInfoByColumnID(columnName);
}
这是我现在维护的一个老项目了,经手的人比较多,代码写的比较垃圾,我们先不吐槽这种纯粹是脱裤子放屁的所谓三层和明明返回的是dataTable又扯什么info,就说这两个函数,tm功能应该是不一样的吧,为啥名字一模一样?这是在闹哪样?
其实,造成这种情况的原因,我们都知道,就是某类程序员的ctrl c+v大法,这两个函数底层逻辑比较相似,懒得重构,直接copy了改改多快!copy就copy吧,好歹名字改一下啊!也许这位前辈会说,我不是写了注释了吗,看到注释,不久知道这个函数是干嘛的了?但问题是,其他调用的人,首先看到的,肯定是函数名啊,GetColumnInfoByColumnID,多直观 通过id来查找呗.虽然这里有两个比较违和的地方,一是参数推荐名字是columnName,二是参数类型不是int而是string,但反正这项目的代码不规范,如果调用的人也够粗心,那么,一个隐晦的bug,就这么产生了.
如果改下名字,叫 GetTableByColumnName,这种错误发生的概率无疑会减少很多了,
Ps:其实现在ide功能这么强大,只要你确定没有反射调用这个函数的地方,完全可以使用全局重命名的方法,一步到位.
看到这里读者朋友们可能会说,这是命名不规范嘛,和写不写注释有什么关系呢?
我们可以假想一种这样的情况,同样是拷贝代码,拷贝者改了函数名,这个名字语义清晰,表意清楚, 但他却忘记改注释了,结果函数是新的函数签名,函数的注释却是其他一段莫名其妙的注释.
或者再假想一种情况.
原来的一个函数,名字和注释是对应的,随便举个例子,叫GetTypeByColumnId吧,注释为"通过产品id取得产品类型"
一切ok,是吧! 但是现在逻辑变了,比如说Type和产品无关了,需要通过生产批次来确定,于是一个代码维护者,将函数重写了一下功能ok,满足新需求!!同时他比上个人好一点,他记得改函数名,然后他改为了GetTypeBySerialId,这名字也很ok,一切也都看起来很好.但偏偏他漏掉了注释,但代码仍然运行的很ok,毕竟注释可不受.net的元数据的支持,ide也不可能知道你这里犯了这么一个错误是吧!
然后,接下来的场景大家很容易就可以联想到,
如果是细心的调用者:
尼玛!函数注释让我传"产品id",但函数名和参数默认名字又是"SerialId"(流水号),这尼玛闹哪样?
如果是粗心的调用者呢?两种情况呗:
1 看了函数名字和默认参数名字 ,没注意到注释,ok,算这个粗心的小伙伴幸运,
2 看了注释,然后这小伙伴不认识SerialId这个单词的意思. 然后,一个悲伤的故事就这么发生了!!
其实我以上举例的一些函数,都是功能比较具体,业务比较单纯,也容易用几个单词描述.对于这些函数,我个人的意见是,完全不需要去写注释,
有以下几个原因:
1,浪费精力去写,
2调用的人需要把一段话读两遍
3,写了还需要人去维护,
4,如果强类型的参数传递不匹配,ide或者resharper插件会马上指出你的错误,但如果注释和代码不匹配,则除了通过人力排查,没有其他任何办法去找出这种错误.
其中尤其是4,完全就是埋在项目中的地雷,除非你踩到了,不然很难排查.
当然,如果是反之,逻辑复杂,甚至有调用的前置约束,那肯定是必须写注释了,
同时,我这里还提一个观点,注释比代码更有价值,因为代码毕竟大部分还是在讲怎么做,而注释是讲做什么?抽象程度更高,所以,轻易不写注释,但如果你写了,请一定要对你的注释负责,它比代码更需要你的细心呵护!!
好吧,我会说这篇文最大的作用,其实是可以让我吐槽发泄一下么?
我感觉自己现在就是在这种地雷坑里,天天过着提醒吊胆的日子.
以上例子皆非我刻意编造,来源于工作中的真实经历,只是稍作修饰,隐去了和业务有关的信息.
然后,明年必须闪人了,再在这个项目里面写代码,我会疯的!
本文链接:http://www.cnblogs.com/suijing/p/nameNotation_is_important_than_annotation.html,转载请注明。
博客园_首页 于 2013-12-29 19:16:25
JAVA面试精选【Java算法与编程二】 - elysee
在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。算法是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。算法常常含有重复的步骤和一些比较或逻辑判断。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。
6、从类似如下的文本文件中读取出所有的姓名,并打印出重复的姓名和重复的次数,并按重复次数排序:
1,张三,28
2,李四,35
3,张三,28
4,王五,35
5,张三,28
6,李四,35
7,赵六,28
8,田七,35
程序代码如下(答题要博得用人单位的喜欢,包名用该公司,面试前就提前查好该公司的网址,如果查不到,现场问也是可以的。还要加上实现思路的注释):
package com.huawei.interview;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
publicclass GetNameTest {
/**
* @paramargs
*/
public static voidmain(String[] args) {
// TODO Auto-generated method stub
//InputStream ips =GetNameTest.class.getResourceAsStream("/com/huawei/interview/info.txt");
//用上一行注释的代码和下一行的代码都可以,因为info.txt与GetNameTest类在同一包下面,所以,可以用下面的相对路径形式
Map results = new HashMap();
InputStream ips = GetNameTest.class.getResourceAsStream("info.txt");
BufferedReader in = newBufferedReader(new InputStreamReader(ips));
String line = null;
try {
while((line=in.readLine())!=null)
{
dealLine(line,results);
}
sortResults(results);
} catch (IOException e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
static class User
{
public String name;
public Integer value;
public User(String name,Integervalue)
{
this.name = name;
this.value = value;
}
@Override
public booleanequals(Object obj) {
// TODO Auto-generated methodstub
//下面的代码没有执行,说明往treeset中增加数据时,不会使用到equals方法。
boolean result =super.equals(obj);
System.out.println(result);
return result;
}
}
private static voidsortResults(Map results) {
// TODO Auto-generated method stub
TreeSet sortedResults =newTreeSet(
new Comparator(){
public intcompare(Object o1, Object o2) {
// TODOAuto-generated method stub
User user1 = (User)o1;
User user2 = (User)o2;
/*如果compareTo返回结果0,则认为两个对象相等,新的对象不会增加到集合中去
* 所以,不能直接用下面的代码,否则,那些个数相同的其他姓名就打印不出来。
* */
//returnuser1.value-user2.value;
//returnuser1.value if(user1.value { return -1; }else if(user1.value>user2.value) { return 1; }else { returnuser1.name.compareTo(user2.name); } } } ); Iterator iterator =results.keySet().iterator(); while(iterator.hasNext()) { String name = (String)iterator.next(); Integer value =(Integer)results.get(name); if(value > 1) { sortedResults.add(newUser(name,value)); } } printResults(sortedResults); } private static voidprintResults(TreeSet sortedResults) { Iterator iterator = sortedResults.iterator(); while(iterator.hasNext()) { User user = (User)iterator.next(); System.out.println(user.name +":" + user.value); } } public static voiddealLine(String line,Map map) { if(!"".equals(line.trim())) { String [] results =line.split(","); if(results.length == 3) { String name = results[1]; Integer value =(Integer)map.get(name); if(value ==null)value = 0; map.put(name,value + 1); } } } } 7、写一个Singleton出来。 第一种:饱汉模式 public classSingleTon { private SingleTon(){ } //实例化放在静态代码块里可提高程序的执行效率,但也可能更占用空间 private final static SingleTon instance =new SingleTon(); public static SingleTon getInstance(){ return instance; } } 第二种:饥汉模式 public classSingleTon { private SingleTon(){} private static instance = null;//newSingleTon(); public static synchronized SingleTongetInstance(){ if(instance == null) instance = new SingleTon(); return instance; } } 第三种:用枚举 public enum SingleTon{ ONE; } 第三:更实际的应用(在什么情况用单例) public classSequenceGenerator{ //下面是该类自身的业务功能代码 private int count = 0; public synchronized int getSequence(){ ++count; } //下面是把该类变成单例的代码 private SequenceGenerator(){} private final static instance = newSequenceGenerator(); public static SingleTon getInstance(){ return instance; } } 第四: public class MemoryDao { private HashMap map = new HashMap(); publicvoid add(Student stu1){ map.put(SequenceGenerator.getInstance().getSequence(),stu1); } //把MemoryDao变成单例 } Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 一般Singleton模式通常有几种种形式: 第一种形式: 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 public class Singleton { private Singleton(){} //在自己内部定义自己一个实例,是不是很奇怪? //注意这是private只供内部调用 private staticSingleton instance = new Singleton(); //这里提供了一个供外部访问本class的静态方法,可以直接访问 public staticSingleton getInstance() { return instance; } } 第二种形式: public class Singleton { private static Singleton instance = null; public static synchronized Singleton getInstance() { //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 //使用时生成实例,提高了效率! if (instance==null) instance=new Singleton(); return instance; } } 其他形式: 定义一个类,它的构造函数为private的,所有方法为static的。 一般认为第一种形式要更加安全些 8、递归算法题1 一个整数,大于0,不用循环和本地变量,按照n,2n,4n,8n的顺序递增,当值大于5000时,把值按照指定顺序输出来。 例:n=1237 则输出为: 1237, 2474, 4948, 9896, 9896, 4948, 2474, 1237, 提示:写程序时,先致谢按递增方式的代码,写好递增的以后,再增加考虑递减部分。 public static void doubleNum(int n) { System.out.println(n); if(n<=5000) doubleNum(n*2); System.out.println(n); } Gaibaota(N) = Gaibaota(N-1) + n 9、递归算法题2 第1个人10,第2个比第1个人大2岁,依次递推,请用递归方式计算出第8个人多大? package cn.itcast; import java.util.Date; publicclass A1 { public static voidmain(String [] args) { System.out.println(computeAge(8)); } public static int computeAge(intn) { if(n==1)return 10; returncomputeAge(n-1) + 2; } } public static voidtoBinary(int n,StringBuffer result) { if(n/2 != 0) toBinary(n/2,result); result.append(n%2); } 10、排序都有哪几种方法?请列举。用JAVA实现一个快速排序。 本人只研究过冒泡排序、选择排序和快速排序,下面是快速排序的代码: public class QuickSort { /** * 快速排序 * @param strDate * @param left * @param right */ public void quickSort(String[] strDate,int left,int right){ String middle,tempDate; int i,j; i=left; j=right; middle=strDate[(i+j)/2]; do{ while(strDate[i].compareTo(middle)<0&& i i++; //找出左边比中间值大的数 while(strDate[j].compareTo(middle)>0&& j>left) j--; //找出右边比中间值小的数 if(i<=j){ //将左边大的数和右边小的数进行替换 tempDate=strDate[i]; strDate[i]=strDate[j]; strDate[j]=tempDate; i++; j--; } }while(i<=j); //当两者交错时停止 if(i quickSort(strDate,i,right);//从 } if(j>left){ quickSort(strDate,left,j); } } /** * @param args */ public static void main(String[] args){ String[] strVoid=newString[]{"11","66","22","0","55","22","0","32"}; QuickSort sort=new QuickSort(); sort.quickSort(strVoid,0,strVoid.length-1); for(int i=0;i System.out.println(strVoid[i]+" "); } } } 11、有数组a[n],用java代码将数组元素顺序颠倒 //用下面的也可以 //for(inti=0,int j=a.length-1;i importjava.util.Arrays; public classSwapDemo{ public static void main(String[] args){ int [] a = new int[]{ (int)(Math.random() *1000), (int)(Math.random() * 1000), (int)(Math.random() *1000), (int)(Math.random() *1000), (int)(Math.random() * 1000) }; System.out.println(a); System.out.println(Arrays.toString(a)); swap(a); System.out.println(Arrays.toString(a)); } public static void swap(int a[]){ int len = a.length; for(int i=0;i int tmp = a[i]; a[i] = a[len-1-i]; a[len-1-i] = tmp; } } } 12.金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)->(一千零一拾一元整)输出。 去零的代码: returnsb.reverse().toString().replaceAll("零[拾佰仟]","零").replaceAll("零+万","万").replaceAll("零+元","元").replaceAll("零+","零"); public class RenMingBi { /** * @param args add by zxx ,Nov 29, 2008 */ private static finalchar[] data = new char[]{ '零','壹','贰','叁','肆','伍','陆','柒','捌','玖' }; private static finalchar[] units = new char[]{ '元','拾','佰','仟','万','拾','佰','仟','亿' }; public static voidmain(String[] args) { // TODOAuto-generated method stub System.out.println( convert(135689123)); } public static Stringconvert(int money) { StringBuffersbf = new StringBuffer(); int unit = 0; while(money!=0) { sbf.insert(0,units[unit++]); intnumber = money%10; sbf.insert(0,data[number]); money/= 10; } returnsbf.toString(); } } 本文链接:http://www.cnblogs.com/hnlshzx/p/3496631.html,转载请注明。 博客园_首页 于 2013-12-29 19:16:21 让后端开发变简单:“原子移动云”基于BaaS实现App快速构建,多种功能组件一站式获取 卖水型的创业几乎让人上瘾:App统计分析领域有flurry、友盟和Talking Data,推送领域有个推和极光推送,社交分享有ShareSDK和Arrownock,在用户行为追踪上我们已经介绍过Heatma.ps、Lookback.io和邦分析。 “原子移动云”可以看做是友盟、个推、Heatma.ps三者的结合。基于BaaS(后端即服务)模式,“原子移动云”提供了一系列旨在简化后端开发的SDK,允许开发者快速添加热点信息图、推送、自动升级和用户反馈等功能。同时还搭载了一个基础的统计分析服务,方便开发者优化产品和运营。 “原子移动云”的热点信息图与Heatma.ps比较类似,可以监测用户在移动设备上的触摸、手势操作、点击等交互动作,并通过热点图可视化呈现出来。开发者可以借此分析用户行为,完善产品的UI布局。 有些开发者可能不喜欢通过应用商店升级客户端版本(因为渠道碎片化等原因),这时就可以使用“原子移动云”的自动升级服务。开发者只需提交最新版本,下次App启动时将会自动检测并提示升级。当然iOS升级还是只能通过App Store实现。 至于“用户反馈”功能,则是通过调用一个可自定义的反馈界面来实现,方便开发者搜集用户意见以完善产品。 “原子移动云”的统计分析包括了常见的用户、渠道、设备、访问路径等项目,不过其目的主要是为了辅助推送。在开发推送时,原子团队感到要有一个更精细化的用户分组,而这个分组则须在用户分析的基础上实现。“原子移动云”将统计与推送打通,开发者可以设置一定规则,系统依据规则将用户自动分组并定向推送个性化内容。而且,“原子移动云”的推送也可以支持富文本和富媒体。 统计分析与推送结合也是一种趋势,我们可以看到原本单纯做统计服务的友盟和Cobub Razor正在向“分析+推送”转型。 原子团队目前只有3人,基本都是华为出身。产品于本月早些时候上线,两周时间已有大概100位开发者在试用,下一步将推出非结构化数据的即时云存储服务。团队在商业化方面并不着急,目前所有服务都是免费提供,未来可能采用freemium模式。 在原子团队看来,BaaS的概念正在逐渐深入开发者群体:既可以快速为App添加必备功能,也可以省去搭建服务器的麻烦,降低后台维护成本的同时还可以把主要精力放在产品创意上。目前国内提供这类整合服务的公司还不太多,与“原子移动云”重合度最高的可能就是Bmob。 除非注明,本站文章均为原创或编译,转载请注明: 文章来自 36氪 36氪官方iOS应用正式上线,支持『一键下载36氪报道的移动App』和『离线阅读』 立即下载! 36氪 于 2013-12-29 19:00:09 【案例】优衣库为什么要教你做菜? 说起优衣库UNIQLO,大概没有人不知道这个品牌主要是销售服饰的吧!最近我却在优衣库UNIQLO美国的官网上,看到她们开始“不务正业”,在网站上教起妳『如何做菜』?莫非她想要多角化经营,开设餐厅? 答案当然不会这么跳跃了。其实优衣库UNIQLO美国官网跟大部分的品牌官网一样,都有分门别类介绍各系列产品的内容,用非常优衣库UNIQLO的风格展示产品。除此之外,她有一个特别的《Life Tools》单元页,上面简介是这样说的:这是『延伸我们品牌哲学的网络工具』,所以在这个单元里,全都是一些看似跟服饰没有直接关系,却都是能丰富消费者生活的内容。教你做菜或是教你绑头髮,就是在这样的理念下所发展出来的一部份。 优衣库UNIQLO找了6位新锐大厨,利用简单的食材、一般的烹饪技巧,根据LifeWear系列服饰的启发,创作了24道食谱,在Life Tools单元页裡有步骤地教妳做菜。 并且把这些内容,设计成了一个iPad版的免费App 《UNIQLO RECIPE》。个人认为,iPad版才是真的优衣库UNIQLO这一次想要消费者完整体验的东西,网站反而是配角,只不过是顺便而已。 在UNIQLO RECIPE里一共有包含繁中在内的四种语言版本,你可以先选择你感兴趣的食谱,进到食谱页内,就会告诉你完成这道菜需要哪些食材的资讯以及步骤,你只要在厨房一边做菜,一边看着大大的iPad萤幕,跟着照片及文字的描诉(有时还加码以影片来呈现),就可以完成一道视觉与美味兼具的佳肴了。 ▲对哪道菜感兴趣,点进去就对了 ▲跟着照片及文字一步一步的完成,还贴心的附上计时器的功能 ▲将~将~完成后,你做的菜应该长这样,你看跟服饰配在一起简直完美! ▲食谱旁展示的衣服,还帮你连接到官网,可以直接购物 ▲就算你食谱只是看爽的,光是使用计时器的功能,听着有着独特配乐的滴答声,就让人很有食慾 UNIQLO有技巧性的,引君入瓮,把感兴趣的消费者一下子扩大了 发现了没?优衣库UNIQLO很巧妙的,把产品型录变成了食谱,把食谱又变成了型录!她思索的是『让使用者在什麽样的时机下,看到产品』。 在这个资讯爆炸的时代,我们有太多的事情需要关注,我想我们不会闲来无事follow各个品牌的资讯。如果你刚好是品牌的爱好者,就算给你一本普普通通的型录,你也会看得津津有味;但,如果你不是呢?这,才是问题的重点。 我相信在优衣库UNIQLO的目标对象中,有一群人的生活形态是喜爱烹饪的。与其推出一个精美的型录,只服务对品牌感兴趣的消费者,不如,推出一个实用的工具,一下子就把可能下载这个App的对象,扩大为品牌爱好者或对烹饪感兴趣的人。 找出符合消费者的生活形态,提供实用的工具更实在 只要这个工具是消费者愿意留在iPad裡的App,就增加了产品被看到的机会。被留下来,是因为它实用。它实用,是因为我的生活有机会用到它。找到这样的模式,是不是开启各种品牌与消费者贴近的可能?只要找到符合消费者生活形态的服务,通通都可以考虑。就像之前UNIQLO推出的《UNIQLOCK》也是一样的道理。 只是这一次,她偷偷的,更往促成产品销售的路靠近,而你却不自知。你看,多高明! 本文来自SocialBeta 特约作者:米卡 本文链接:socialbeta 原文链接:jabamay 非商业转载须注明作者、出处,并保留文章在SocialBeta的完整链接。 您可能也喜欢: 【案例】星巴克优衣库联合营销:设计师练手之作,为何魅力不减? 【How-To】优酷海外公关传播的4个最佳实践经验 【品牌案例】IUNI定制豆瓣用户个性化LOGO—“U友,请赏脸” 【案例】2013年社交网络上最受欢迎的10大品牌,SamSung mobile居首 【案例】旅游企业利用Pinterest的最佳范例 无觅 SocialBeta 于 2013-12-29 18:45:51 主播夏子陌:有生之年,你过得好吗 主播夏子陌:有生之年,你过得好吗 悦读时光FM第八十三期预告,主播夏子陌《有生之年,你过得好吗》,2013年12月30日,晚8点悦读时光FM,我们不见不散。点击收听主播夏子陌的往期节目。 在这个急功近利的世界,每个人都带着一张面具生活,只有当夜深人静的时候才能安定下来。悦读时光FM的每位主播将用声音和文字温暖你的内心。悦读时光FM,在这里,邂逅你我最美好的时光。 阅读时间 | 阅读让自己更美好 于 2013-12-29 18:30:55 JS对表格排序(支持对序号,数字,字母,日期) - 龙恩0707 JS对表格排序(支持对序号,数字,字母,日期) 前不久看到淘宝组件有"对表格排序的插件" 如想要看 可以看这个地址 http://gallery.kissyui.com/KSortTable/1.0/demo/index.html 但是看了下他们的JS源码 有点复杂 所以自己试着写了一个简单点的,同样也能实现相应的功能,且相对于他们来讲 多添加了一个"按日期排序的功能" , 目前对日期的格式 支持两种格式:一种是2013/12/29 另外一种是:2013-12-29 。其他的日期未做处理,其实对表格排序 特别对字母排序刚开始有点纠结,看了下kissy组件源码 感觉他们那样处理有点复杂 所以自己也是google下 发下JS有一种方法可以比较本地的字母的方法 ----- localeCompare。 什么是localeCompare? 根据官方定义: 确定两个字符串在当前区域设置中是否相等。(当然字母可以转换成字符串进行比较)。 语法如下: stringVar.localeCompare(stringExp[, locales][, options]) 。 stringVar 必需。 要比较的第一个字符串。 这可能是 String 对象或字符串文本。 stringExp 必需。 要比较的第二个字符串。 locales 可选。 包含一种或多种语言或区域设置标记的区域设置字符串数组。 如果包含多个区域设置字符串,请以降序优先级对它们进行排列,确保首个条目为首选区域位置。 如果省略此参数,则使用 JavaScript 运行时的默认区域设置。 options 可选。 包含指定比较选项的一个或多个特性的对象。 下面可以先来看看效果吧! 表格排序的原理? 还是用上一篇文章的方法 用图来解释 更明白点 如下: 思路如上面所示: HTML代码 css代码 我是直接把淘宝的结构和css样式拿过来用的 其实HTML+CSS对于前端或者后端来说 没有什么 只是以什么样的显示而已 没有其他什么的 至于JS的逻辑 就是上面的图表解释的那个逻辑。我会在下面提供demo源码 大家可以下载下看看。 所以在这里 也不贴HTML代码和CSS代码了 直接贴下JS代码 不过会提供Demo源码下载 有兴趣的同学可以看看! JS代码如下: /** * JS对表格排序(支持按序号,日期,字母,数字排序)。 * @constructor {KSortTable} * @date 2013-12-26 * @author tugenhua * @email [email protected] */ function KSortTable(options) { this.config = { tableElem : '.J_table', //表格容器 sortableElem : '.sortable', // 点击对象 callback : null // 排序好后的回调函数 }; this.cache = { trArrs : [] // 存放tbody下的所有tr }; this.init(options); } KSortTable.prototype = { constructor : KSortTable, init: function(options){ this.config = $.extend(this.config,options || {}); var self = this, _config = self.config, _cache = self.cache; $(_config.sortableElem).each(function(index,item){ $(item).unbind('click'); $(item).bind('click',function(e){ var tagParent = $(this).closest(_config.tableElem), tagAttr = $(this).attr("data-sorttable"); // 判断table元素是否有属性 data-sorttable = 'true' 没有的话 不做任何事情 if($(tagParent).attr('data-sorttable') == 'true') { self._curStyle($(this)); var index = $(_config.sortableElem,tagParent).index($(this)); // 排序函数 self._sortMethod(index,tagAttr,tagParent,$(this)); } }); }); }, /* * 点击对当前添加样式 */ _curStyle: function(item) { var self = this; !$(item).hasClass('st-active') && $(item).addClass("st-active").siblings().removeClass("st-active"); if(!$(item).hasClass('data-reverse')) { $(item).addClass('data-reverse'); }else { $(item).removeClass('data-reverse'); } }, /* * 排序方法 做了type的判断 * 目前暂支持 数字 字符串 日期的比较 */ _sortMethod: function(index,tagAttr,tagParent,tagElem) { var self = this, _config = self.config, _cache = self.cache; var tbody = $(tagParent)[0].tBodies[0]; for(var i = 0, ilen = tbody.rows.length; i < ilen; i++) { _cache.trArrs[i] = tbody.rows[i]; } if(tagAttr == 'number') { _cache.trArrs.sort(function (td1, td2){ if($(tagElem).hasClass('data-reverse')) { return td1.cells[index].innerHTML - td2.cells[index].innerHTML; }else { return td2.cells[index].innerHTML - td1.cells[index].innerHTML } }); }else if(tagAttr == 'string') { _cache.trArrs.sort(function(td1,td2){ var str1 = td1.cells[index].innerHTML, str2 = td2.cells[index].innerHTML; if($(tagElem).hasClass('data-reverse')) { return str1.localeCompare(str2); }else { return str2.localeCompare(str1); } }); }else if(tagAttr == 'date') { _cache.trArrs.sort(function (td1, td2){ var str1 = td1.cells[index].innerHTML, str2 = td2.cells[index].innerHTML; if($(tagElem).hasClass('data-reverse')) { return Date.parse(str1.replace(/-/g,'/')) - Date.parse(str2.replace(/-/g,'/')); }else { return Date.parse(str2.replace(/-/g,'/')) - Date.parse(str1.replace(/-/g,'/')); } }); } //把排序后的tr 重新插入tbody for(var j =0; j < _cache.trArrs.length; j++ ) { tbody.appendChild(_cache.trArrs[j]); } // 排序完 回调 _config.callback && $.isFunction(_config.callback) && _config.callback(); } }; /* JS初始化 */ $(function(){ new KSortTable({}); }); Demo下载 本文链接:http://www.cnblogs.com/tugenhua0707/p/3496596.html,转载请注明。 博客园_首页 于 2013-12-29 18:30:47 对进度条的通用封装实现 - Johnnie Zhang 一直想写点啥对最近的工作做个总结,由于项目比较忙,可能还有自己的各种理由推脱有点懈怠,零碎的总结过一些,都没有动笔写下来过。眼看2013都要过去了,该写点啥来总结下。先从自己对进度封装的一点学习经验写出来,供大家交流,欢迎园子里的朋友不吝啬的拍砖。 首先定义对进度表示的契约,定义进度行为(IProgressor)、进度信息(IStepProgress)和中断处理(ITrackCancel)的接口如下: /// /// 进度行为接口 /// public interface IProgressor { string Message { get; set; } void Show(); void Hide(); void Step(); } /// /// 进度展示接口 /// public interface IStepProgress : IProgressor { int MaxRange { get; set; } int MinRange { get; set; } int StepValue { get; set; } i /// /// 中断接口 /// public interface ITrackCancel { void Cancel(); bool Continue(); bool CancelOnClick { get; set; } bool CancelOnKeyPress { get; set; } IProgressor Progressor { get; set; } } 实现进度信息接口,实现类(ProgressBarDialog)如下: /// /// 进度实现类 /// public class ProgressBarDialog:IStepProgress { private int maxRange; private int minRange; private int stepValue; private int position; private string message; private Task task; private ProgressBarForm progressBarForm; private CancellationTokenSource cancellationTokenSource; private ITrackCancel trackCancel; public Form ParentForm { get; set; } public ITrackCancel TrackCancel { get { return trackCancel; } set { if (value != null) { trackCancel = value; trackCancel.Progressor = this; } if(progressBarForm != null) progressBarForm.TrackCancel = value; } } public int MaxRange { get { return maxRange; } set { if (maxRange != value) { if (progressBarForm != null) progressBarForm.MaxRange = value; maxRange = value; } } } public int MinRange { get { return minRange; } set { if (minRange != value) { if(progressBarForm != null) progressBarForm.MinRange = value; minRange = value; } } } public int StepValue { get { return stepValue; } set { if (stepValue != value) { if (progressBarForm != null) progressBarForm.StepValue = value; stepValue = value; } } } public int Position { get { return position; } set { if (progressBarForm != null) progressBarForm.Position = value; position = value; } } public string Message { get { return message; } set { if (message != value) { if (progressBarForm != null) progressBarForm.Message = value; message = value; } } } private void InitDialog() { if (progressBarForm == null) progressBarForm = new ProgressBarForm(ParentForm); progressBarForm.TopMost = true; progressBarForm.MaxRange = this.MaxRange; progressBarForm.MinRange = this.MinRange; progressBarForm.StepValue = this.StepValue; progressBarForm.Position = this.Position; if (trackCancel != null) progressBarForm.TrackCancel = this.TrackCancel; if (!cancellationTokenSource.IsCancellationRequested)//此处的取消方法不是强制取消,CLR线程管理会在响应后合适的时候取消 { //这种线程取消的方式,一个目的是为了解决.NET4.0以前线程强制关闭的异常问题 if (progressBarForm.ShowDialog() == DialogResult.OK) { } } } public void Show() { ShowDialog(); } public void ShowDialog() { try { cancellationTokenSource = new CancellationTokenSource(); task = new Task(InitDialog, cancellationTokenSource.Token); task.Start(); //此处创建的延续任务,为确保线程取消的时候,出现异常情况(如:进度UI还没有展示,主方法就已经走完)的保险处理 Task cancelTask = task.ContinueWith( (cancellatinTask) => { if (progressBarForm != null && progressBarForm.Visible) progressBarForm.Hide(); }, TaskContinuationOptions.OnlyOnCanceled); } catch (Exception) { throw; } } public void HideDialog() { try { if (progressBarForm == null)//当进度窗体还没有显示出来,进度线程监视的方法已经走完,取消task线程 cancellationTokenSource.Cancel(); else { MethodInvoker invoker = () => { if (progressBarForm.Visible) progressBarForm.Hide(); }; if (progressBarForm.InvokeRequired) { progressBarForm.Invoke(invoker); } else { invoker(); } } } catch (Exception) { throw; } } public void Hide() { HideDialog(); } public void Step() { if (progressBarForm != null) progressBarForm.Step(); } } 实现进度展示的UI类(ProgressBarForm)如下: public partial class ProgressBarForm : Form { public ProgressBarForm(Form parent) { InitializeComponent(); InitLocation(parent); } private void InitLocation(Form parent) { if (parent != null) { Left = parent.Left + (parent.Width - Width) / 2; Top = parent.Top + (parent.Height - Height) / 2; } } private int maxRange; private int minRange; private int stepValue; private int position; private string message; public ITrackCancel TrackCancel { get; set; } public string Message { get { return message; } set { if (message != value) { MethodInvoker invoker = () => lab_Message.Text = value; ; if (lab_Message.InvokeRequired) { //lab_Message.Invoke(invoker); IAsyncResult asyncResult =lab_Message.BeginInvoke(invoker); lab_Message.EndInvoke(asyncResult); } else { invoker(); } message = value; } } } public int MaxRange { get { return maxRange; } set { if (maxRange != value) { progressBar1.Maximum = value; maxRange = value; } } } public int MinRange { get { return minRange; } set { if (minRange != value) { progressBar1.Minimum = value; minRange = value; } } } public int StepValue { get { return stepValue; } set { if (stepValue != value) { progressBar1.Step = value; stepValue = value; } } } public int Position { get { return position; } set { if (position != value) { progressBar1.Value = value; position = value; } } } public void Step() { MethodInvoker invoker = () => { int newValue = progressBar1.Value + this.StepValue; //当进度超过最大值,默认赋最小值 if (newValue > this.MaxRange) { progressBar1.Value = MinRange; progressBar1.Refresh(); } else { progressBar1.Value = newValue; progressBar1.Refresh(); } }; if (progressBar1.InvokeRequired) { //progressBar1.Invoke(invoker); //采用异步委托的方法,提高进度条的响应速度,不知道线程创建多会不会影响效率(PS:没有研究过异步委托创建线程的方式,不知道是从线程池那线程,还是每次来就创建一个) IAsyncResult asyncResult =progressBar1.BeginInvoke(invoker); progressBar1.EndInvoke(asyncResult); } else { invoker(); } } private void ProgressBarForm_KeyDown(object sender, KeyEventArgs e) { if( e.KeyCode == Keys.Escape && TrackCancel.CancelOnKeyPress) TrackCancel.Cancel(); } private void btn_Cancel_Click(object sender, EventArgs e) { if (TrackCancel.CancelOnClick) { TrackCancel.Cancel(); } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); //当中断处理不支持按钮取消时候,将取消按钮不显示 if (!TrackCancel.CancelOnClick) { this.Height = 65; } } } 下面完成对进度的调用进度条的简单封装类(DialogManager)如下: /// /// 管理进度条的方法类 /// public class ProgressManager { public static IStepProgress ShowProgressBarDialog(ITrackCancel trackCancel,Form pForm) { if (trackCancel == null) return null; return new ProgressBarDialog() {TrackCancel = trackCancel,ParentForm = pForm}; } public static IStepProgress ShowProgressBarDialog(ITrackCancel trackCancel, Form pForm,int maxRange,int minRange,int stepValue) { if (trackCancel == null) return null; return new ProgressBarDialog() { TrackCancel = trackCancel, ParentForm = pForm, MaxRange = maxRange, MinRange = minRange, StepValue = stepValue }; } } 最后,在自己代码中调用进度的写法如下: private void button1_Click(object sender, EventArgs e) { ITrackCancel trackCancel = new CancelTracker() {CancelOnClick = true, CancelOnKeyPress = true}; IStepProgress stepProgress = ProgressManager.ShowProgressBarDialog(trackCancel,this); stepProgress.MinRange = 0; stepProgress.MaxRange = 100; stepProgress.StepValue = 1; bool pContinue = true; stepProgress.Show(); for (int i = 0; i < 100; i++) { stepProgress.Message = String.Format("正在计算{0}...",i); pContinue = trackCancel.Continue(); if(!pContinue) break; stepProgress.Step(); Thread.Sleep(50); } stepProgress.Hide(); } 以上为对简单进度条的封装过程,接口的定义参考了ArcGIS的接口定义方式。如果想增加对其它的进度条的支持,需要实现对应的进度条接口和对应的展示UI即可。如:想增加一个转圈等待的进度条展示进度,你只需要实现IProgressor接口,想支持中断处理,实例化实现ITrackCancel接口的类即可,可以用工厂模式来管理你的进度条展示。 题外的话:最近在学习fyiReporting和SharpDeveloper的源代码,fyiReporting的源代码太多,网上没发现什么知道的资源,看起来还是一知半解。SharpDeveloper的源码,网上资源大都讲的都是关于插件框架的东西,还有SharpDeveloper的团队也出过一本书的,讲的都是很久以前的版本。在此,求园友们对此感兴趣的能指导下学习fyiReporting或者SharpDeveloper的资料,或者一起共同学习。 本文链接:http://www.cnblogs.com/Johnnie/p/3496607.html,转载请注明。 博客园_首页 于 2013-12-29 18:30:46 解读系统托盘图标隐藏(删除) - CRoot 为了在某计算机上建立一个隐蔽的Wifi,所以面对可恶的托盘图标,令我不得不考虑写个程序将其Hide掉,以免后患。 于是开始了大量的谷歌与 百度,看下前人是如何去做的。站在巨人的肩膀不是看得更远嘛。 首先我们了解到托盘区似乎是个窗口性质的东西,于是乎,操起Spy++查查到底是什么情况。 很清晰的窗口所属结构,只要我们一次通过FindWindow()和FindWindowEx()依次从窗口类为Shell_TrayWnd ->SysPager ->ToolbrWindow32,得到最终句柄,然后通过发送 TB_BUTTONCOUNT消息得到托盘窗口TBBUTTON的个数 -> 通过得到总数,遍历所有按钮,向每个BUTTON发送TB_GETBUTTON消息 获得按钮 -> 用ReadProcessMemory读取每个TBBUTTON结构 -> 再通过TBBUTTON.dwData 得到TRAYDATA结构。 这里不得不提一下TRAYDATA,据说这是一个从未公开的结构 1 struct TRAYDATA { 2 HWND hwnd; 3 UINT uID; 4 UINT uCallbackMessage; 5 DWORD Reserved[2]; 6 HICON hIcon; 7 }; 可我们怎么找到他呢?他的信息在TBBUTON结构中的dwData 1 typedef struct { 2 int iBitmap; 3 int idCommand; 4 BYTE fsState; 5 BYTE fsStyle; 6 #ifdef _WIN64 7 BYTE bReserved[6]; 8 #else 9 #if defined(_WIN32) 10 BYTE bReserved[2]; 11 #endif 12 #endif 13 DWORD_PTR dwData; 14 INT_PTR iString; 15 } TBBUTTON, *PTBBUTTON, *LPTBBUTTON; 然后我们可以根据他获得信息填充下NOTIFYICONDATA结构体 1 //系统定义结构体 2 typedef struct _NOTIFYICONDATA 3 { 4 DWORD cbSize; //以字节为单位的这个结构的大小 5 HWND hWnd; //接收托盘图标通知消息的窗口句柄 6 UINT uID; //应用程序定义的该图标的ID号 7 UINT uFlags; //设置该图标的属性 8 UINT uCallbackMessage; //应用程序定义的消息ID号,此消息传递给hWnd 9 HICON hIcon; //图标的句柄 10 char szTip[64]; //鼠标停留在图标上显示的提示信息 11 } NOTIFYICONDATA, *PNOTIFYICONDATA; 12 13 /* 14 该结构中,成员uFlags可以使下列之一或组合: 15 16 NIF_ICON 设置成员hIcon有效 17 NIF_MESSAGE 设置成员uCallbackMessage有效 18 NIF_TIP 设置成员szTip有效 19 */ 以便我们在后面使用Shell_NotifyIcon()函数,他是在托盘上增加,删掉等作用的函数。 1 WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA pnid); 2 /* 3 ---Pnid是NOTIFYICONDATA结构的指针; dwMessage是被传递的消息,可以是以下消息之一: 4 5 NIM_ADD 增加图标 6 NIM_DELETE 删除图标 7 NIM_MODIFY 修改图标 8 */ 但是有的时候我们可能不希望删除所有的托盘图标,所以我们要判断下他的文本,在TRAYDATA结构体中的szTip来确认是哪个按钮,如果是他就删掉他,这个判断加在51行后面,然后再执行Shell_NotifyIcon() 完整代码是: 1 struct TRAYDATA 2 { 3 HWND hWnd; 4 UINT uID; 5 UINT uCallbackMessage; 6 DWORD Reserved1[2]; 7 HICON hIcon; 8 DWORD Reserved2[3]; 9 TCHAR szExePath[MAX_PATH]; 10 TCHAR szTip[128]; 11 }; 12 13 //获取托盘可见区域句柄 14 HWND hWnd = NULL; 15 hWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL); 16 hWnd = ::FindWindowEx(hWnd, NULL, _T("TrayNotifyWnd"), NULL); 17 hWnd = ::FindWindowEx(hWnd, NULL, _T("SysPager"), NULL); 18 hWnd = ::FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL); 19 20 //获取进程 ID 21 DWORD dwPID = 0; 22 ::GetWindowThreadProcessId(hWnd, &dwPID); 23 DWORD dwCount = ::SendMessage(hWnd, TB_BUTTONCOUNT, NULL, NULL); 24 25 26 27 //打开进程 28 HANDLE hProc = ::OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwPID); 29 30 //申请内存 31 LPVOID pTB = ::VirtualAllocEx(hProc, NULL, sizeof(TBBUTTON), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 32 33 TBBUTTON tb; 34 TRAYDATA td; 35 NOTIFYICONDATA nid; 36 for (DWORD i = 0; i < dwCount; i++) 37 { 38 39 ::ReadProcessMemory(hProc, pTB, &tb, sizeof(TBBUTTON), NULL); 40 41 //获取 TRAYDATA 信息 42 ::ReadProcessMemory(hProc, (LPVOID)tb.dwData, &td, sizeof(TRAYDATA), NULL); 43 //填充 NOTIFYICONDATA 结构体,调用 Shell_NotifyIcon 函数 44 //在这里可以利用判断TRAYDATA结构体中的szTip来确认是哪个按钮,然后删掉他 45 nid.cbSize = sizeof(NOTIFYICONDATA); 46 nid.hWnd = td.hWnd; 47 nid.uID = td.uID; 48 nid.uCallbackMessage = td.uCallbackMessage; 49 nid.hIcon = td.hIcon; 50 memcpy(nid.szTip, td.szTip, sizeof(nid.szTip)); 51 nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; 52 ::Shell_NotifyIcon(NIM_DELETE, &nid); //删除图标(注意这里是删除而不是隐藏) 53 //::Shell_NotifyIcon(NIM_ADD, &nid); //显示图标 54 } 55 56 //释放内存 57 ::VirtualFreeEx(hProc, pTB, sizeof(TBBUTTON), MEM_FREE); 58 VirtualFreeEx(hPro,pText,len,MEM_COMMIT); 59 60 //关闭进程句柄 61 ::CloseHandle(hProc) 本文链接:http://www.cnblogs.com/croot/p/3496576.html,转载请注明。 博客园_首页 于 2013-12-29 18:00:52 xml学习总结 - nolonely xml DTD 定义元素 (1)元素类型描述:任意类型,字符串型,空元素,包含子元素,混合类型 任意类型:
]> <书籍列表> 书籍列表 书籍列表>
]> <书籍列表/> 字符串类型:
]> <书籍列表>描述书籍列表> 空类型
]> <书籍列表/> 混合类型
"mixed.dtd"> <喜欢的游戏> 游戏和编程是类似的,都是一种兴趣 <游戏> <游戏类型>动作格斗游戏类型> <游戏名称>侍魂游戏名称> 适合释放紧张的、沉闷的感觉 游戏> <游戏> 放松、舒缓心情的好游戏。 <游戏名称>雷电游戏名称> <游戏类型>飞行射击游戏类型> 游戏> <游戏> <游戏名称>雷电游戏名称> 游戏> <游戏> <游戏名称>雷电游戏名称> <游戏名称>侍魂游戏名称> 游戏> 喜欢的游戏> (2)定义子元素 有序子元素-----使用英文(,)作为子元素之间的分割符 互斥------使用(|)隔开 子元素出现的频率 +:可以出现一次货多次 *:可以出现零次或多次 ?:可以出现零次或一次 组合子元素 无序子元素 (3)定义属性 属性约束: ->#REQUIRED:必须为该元素提供该属性 ->#IMPLIED:该属性值可有可无 ->#FIXED:必须为该属性值指定默认值 属性类型: CDATA ------------------属性值只能是字符串数据 (en1|en2)--------------枚举类型 ID------------------------属性的有效标示符(唯一) IDREF--------------------引自另一个已有的ID属性值 IDREFS-------------------引自多个ID,之间用空格分隔 ENTITY-------------------一个外部实体,例如图片文件 ENTITIES-----------------多个外部实体,多实体之间用空格隔开 XML:--------------------预定义的XML值 XML Schema (1)Schema内置类型 ->字符串类型 xsi:noNamespaceSchemaLocation="string.xsd"> ->数值类型 xsi:noNamespaceSchemaLocation="number.xsd"> ->日期时间类型 xsi:noNamespaceSchemaLocation="date.xsd"> ->boolean类型 xsi:noNamespaceSchemaLocation="boolean.xsd"> ->anyURI类型 xsi:noNamespaceSchemaLocation="uri.xsd"> ->二进制 xsi:noNamespaceSchemaLocation="binary.xsd"> (2)使用限制派生新类型 方式一(base.xsd) elementFormDefault="qualified" attributeFormDefault="unqualified"> 方式二 elementFormDefault="qualified" attributeFormDefault="unqualified"> (base.xml) xsi:noNamespaceSchemaLocation="base.xsd"> 34 本文链接:http://www.cnblogs.com/nolonely/p/3496581.html,转载请注明。 http://www.blublu.me/news?max_id=229310