Notification与Toast都可以起到通知、提醒的作用。但它们的实现原理和表现形式却完全不一样。Toast其实相当于一个组件(Widget)。有些类似于没有按钮的对话框。而Notification是显示在屏幕上方状态栏中的信息。还有就是Notification需要用NotificationManager来管理,而Toast只需要简单地创建Toast对象即可。
下面来看一下创建并显示一个Notification的步骤。创建和显示一个Notification需要如下5步:
1.通过getSystemService方法获得一个NotificationManager对象。
2.创建一个Notification对象。每一个Notification对应一个Notification对象。在这一步需要设置显示在屏幕上方状态栏的通知消息、通知消息前方的图像资源ID和发出通知的时间。一般为当前时间。
3.由于Notification可以与应用程序脱离。也就是说,即使应用程序被关闭,Notification仍然会显示在状态栏中。当应用程序再次启动后,又可以重新控制这些Notification。如清除或替换它们。因此,需要创建一个PendingIntent对象。该对象由Android系统负责维护,因此,在应用程序关闭后,该对象仍然不会被释放。
4.使用Notification类的setLatestEventInfo方法设置Notification的详细信息。
5.使用NotificationManager类的notify方法显示Notification消息。在这一步需要指定标识Notification的唯一ID。这个ID必须相对于同一个NotificationManager对象是唯一的,否则就会覆盖相同ID的Notificaiton。
心动不如行动,下面我们来演练一下如何在状态栏显示一个Notification,代码如下:
01 // 第1步
02 NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
03 // 第2步
04 Notification notification =
05 new Notification(R.drawable.icon, "您有新消息了", System.currentTimeMillis());
06 // 第3步
07 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, getIntent(), 0);
08 // 第4步
09 notification.setLatestEventInfo(this, "天气预报", "晴转多云", contentIntent);
10 // 第5步
11 notificationManager.notify(R.drawable.icon, notification);
上面的5行代码正好对应创建和显示Notification的5步。在这里要解释一下的是notify方法的第1个参数。这个参数实际上表示了Notification的ID。是一个int类型的值。为了使这个值唯一,可以使用res目录中的某些资源ID。例如,在上面的代码中使用了当前Notification显示的图像对应的资源ID(R.drawable.icon)作为Notification的ID。当然,读者也可以使用其他的值作为Notification的ID值。
由于创建和显示多个Notification的代码类似,因此,在本节的例子中编写了一个showNotification方法来显示Notification,代码如下:
下面的代码使用showNotification方法显示了3个Notification消息。
其中第2个和第3个Notification使用的是同一个ID(R.drawabgle.why),因此,第3个Notification会覆盖第2个Notification。在显示Notification时还可以设置显示通知时的默认发声、震动和Light效果。要实现这个功能需要设置Notification类的defaults属性,代码如下:
1 private void showNotification(String tickerText, String contentTitle, String contentText, int id, int resId)
2 {
3 Notification notification = notification =
4 new Notification(resId, tickerText, System.currentTimeMillis());
5 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, getIntent(), 0);
6 notification.setLatestEventInfo(this, contentTitle, contentText, contentIntent);
7 // notificationManager是在类中定义的NotificationManager变量。在 onCreate方法中已经创建
8 notificationManager.notify(id, notification);
9 }
注意:设置默认发声、震动和Light的方法是setDefaults。该方法与showNotification方法的实现代码基本相同,只是在调用notify方法之前需要设置defaults属性(defaults属性必须在调用notify方法之前调用,否则不起作用)。在设置默认震动效果时还需要在AndroidManifest.xml文件中通过<uses-permission>标签设置android.permission.VIBRATE权限。 如果要清除某个消息,可以使用NotificationManager类的cancel方法,该方法只有一个参数,表示要清除的Notification的ID。使用cancelAll可以清除当前NotificationManager对象中的所有Notification。
运行本节的例子,单击屏幕上显示Notification的按钮,会显示如图1所示的消息。每一个消息会显示一会,然后就只显示整个Android系统(也包括其他应用程序)的Notification(只显示图像部分)。如图2所示。如果将状态栏拖下来,可以看到Notification的详细信息和发出通知的时间(也就是Notification类的构造方法的第3个参数值),如图3所示。当单击【清除通知】按钮,会清除本应用程序显示的所有Notification,清除后的效果如图4所示。
图1
图2
图3
经常在公司中出现的这样一批人:忙时忙得四脚朝天,天天加班,到项目或产品开发后期,感到筋疲力尽。而当项目或产品开发完毕后,闲下来,就想补补觉,好好放松一下,上班时就松松垮垮,聊天上网;如果闲的时间较长,依然保持十分松垮的工作状态。
仔细观察周围的同事和朋友,就会发现,很多技术开发人员就是在这种状态中工作和生活。当然,这种工作状态本无可厚非,因为每个人都有权利选择自己喜欢的工作方式。但是,我认为长期以这种方式工作,会破坏了员工的身体健康;较长时间的过分放松,上进心降低,惰性增强;而且,这种工作状态会使员工一直处于被动地对工作反应的状态;同时,侧面反映出保持这种状态的员工缺乏清晰的个人发展方向和个人职业发展规划。
我认为,合理而上进的工作节奏应该是:忙中偷闲,闲里不放松。现分别进行介绍:
a. 忙中偷闲:这种偷闲不是偷懒,而是通过偷闲,保持一个好的身体状态和精神状态,使忙中保持良好的身体状态;其次,大家都明白,健康的身心才有高的工作效率,不能在忙时就一直加班加班,透支自己的身体和心智,即使按时完成工作,必然是一个较低的完成效率;而且,长期紧张导致工作压力与日俱增,急躁的工作氛围会在团队中弥漫,必然影响团队内部的团结。因此,我建议项目经理们,在工作进度紧时,要适当给员工放松的机会和时间,使员工有短暂的身心休息机会。我想,员工会回报高效的工作效率和团结和谐的工作氛围。
b. 闲里不放松:这种不放松不是让员工一直保持紧张的工作状态,而是指员工不能放纵自己的空闲,而是将这种空闲变成自己进步的前奏。首先,利用工作较闲时,进行反思和总结忙时的经验和教训,形成自己的心得。其次,可以结合忙时的需要,抓紧时间充实一些必要的知识和技能,不至于又是到新工作任务到来时,知识和技能准备不足,导致得手忙脚乱。另外,做一份适合自己的个人职业发展规划,按照这个规划的要求,在闲时补充自己忙时耽误的部分,如掌握那些知识点、技能,以及管理的思路等。按照这个思路,闲时每天进步一点点,长期坚持,就积累出巨大的个人进步来。
Update: 这个方法现在已经不行了。现在美国和加拿大国内电话、短信全部免费。美国和中国间的国际长途费用为0.02美元/分钟。
我们知道使用 Gmail 给美国和加拿大拨打电话是完全免费的,现在 Google 则再一次震撼到了我们!只要使用 Gmail 拨打国内电话,通话双方均全部免费!
操作的步骤如下:
1、确保登录 Gmail 后可以看到在 Chat 列表中看到 "Call phone"按钮。这有2个方法:
其一、使用美国或加拿大 IP 地址登录 Gmail 肯定能看到该按钮;
其二、把 Gmail 语言设置成 US English ,也是有可能使用到呼叫功能的。
2、安装 Gmail voice and video chat 浏览器插件。
3、点击"Chat"列表中的"Call phone"按钮,拨打你自己的 Google Voice 号码,如:(111) 222-3333 。如果你还没有 Google Voice 号码的话,可以参考这篇文章注册一个。
4、听到 Google Voice 语音提示:“您当前没有新的消息,按“2”健拨打电话,按“4”健更改设置”。我们按“2”健。
5、再次听到 Google Voice 语音提示:“请按键输入您要拨打的电话号码,按“#”号健确认。如果是国际长途,则请先按键“011”,再输入国家号,最后输入电话号码”。
举2个例子:
如果我要拨打的中国移动手机号为:13866667777,则我应按键输入:0118613866667777#
如果我要拨打的国内固定电话号码为:021-77778888,则我应按键输入:011862177778888#(注:21为上海区号,大家不要输入021,这是错误的。)
6、再次听到 Google Vocie 语音提示:“这是一个免费电话!”,接下来电话就直接接通了呢。
这么大的好事,大家赶紧自己动手试试吧!
开放性 SQL Server
只能在Windows 上运行,没有丝毫的开放性,操作系统的系统的稳定对数据库是十分重要的。Windows9X系列产品是偏重于桌面应用,NT server只适合中小型企业。而且Windows平台的可靠性,安全性和伸缩性是非常有限的。它不象Unix那样久经考验,尤其是在处理大数据量的关键业务时。
Oracle
能在所有主流平台上运行(包括 Windows)。完全支持所有的工业标准。采用完全开放策略。可以使客户选择最适合的解决方案。对开发商全力支持。
DB2
能在所有主流平台上运行(包括Windows)。最适于海量数据。DB2在企业级的应用最为广泛,在全球的500家最大的企业中,几乎85%以上用DB2数据库服务器,而国内到97年约占5%。
可伸缩性,并行性 SQL Server
并行实施和共存模型并不成熟。很难处理日益增多的用户数和数据卷。伸缩性有限。Oracle
平行服务器通过使一组结点共享同一簇中的工作来扩展Window NT的能力,提供高可用性和高伸缩性的簇的解决方案。如果WindowsNT不能满足需要, 用户可以把数据库移到UNIX中。
DB2
DB2具有很好的并行性。DB2把数据库管理扩充到了并行的、多节点的环境。数据库分区是数据库的一部分,包含自己的数据、索引、配置文件、和事务日志。数据库分区有时被称为节点或数据库节点。
安全性
SQL Server
没有获得任何安全证书。
Oracle Server
获得最高认证级别的ISO标准认证。
DB2
获得最高认证级别的ISO标准认证。
性能
SQL Server
多用户时性能不佳
Oracle
性能最高, 保持WindowsNT下的TPC-D和TPC-C的世界记录。
DB2
适用于数据仓库和在线事物处理,性能较高。
客户端支持及应用模式
SQL Server
C/S结构,只支持Windows客户,可以用ADO,DAO,OLEDB,ODBC连接。
Oracle
多层次网络计算,支持多种工业标准,可以用ODBC,JDBC,OCI等网络客户连接。
DB2
跨平台,多层结构,支持ODBC,JDBC等客户。
操作简便
SQL Server
操作简单,但只有图形界面。
Oracle
较复杂, 同时提供GUI和命令行,在Windows NT和Unix下操作相同。
DB2
在巨型企业得到广泛的应用,向下兼容性好。风险小。
silverlight 不怎么回事他不显示了

图片旋转控件(.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Media.Imaging;
/*
* A 3D Image Space Demonstration in C#
*/
namespace ImageSpace3D
{
public partial class ImageSpace3D : UserControl
{
private static String[] IMAGES = {"image1.png", "image2.png", "image3.png", "image4.png", "image5.png", "image6.png", "image7.png", "image8.png", "image9.png", "image10.png",
"image11.png", "image12.png", "image13.png", "image14.png", "image15.png", "image16.png", "image17.png", "image18.png", "image19.png" };
// images
private static string IMAGE_PATH = "./images/";
private static double IMAGE_WIDTH = 128; // Image Width
private static double IMAGE_HEIGHT = 128; // Image Height
private static double SPACE_LENGTH = 2000; // dimension of the 3d space
private static double X_MUL = 10; // springness toward x axis
private static double Y_MUL = 10; // springness toward y axis
private static double Z_MUL = 20; // springness toward z axis
private static double Z_MOVEMENT = 8; // speed toward z axis
private static double NEW_SCALE = 0.6; // additional scale on the images (Use this if your image is too large)
private static double EFFECT_FACTOR = 0.3; // Control the feeling of the 3D Space (Change it to differnt to see the differences)
// Must set less than 0.5
private List<Image> _images= new List<Image>();
private Dictionary<Image, Point3D> _imagePoint3Ds = new Dictionary<Image, Point3D>();
private Canvas _holder = new Canvas();
public Point3D _camera = new Point3D(); // _camera
public Point3D _destination = new Point3D(0, 0, -500); // move desination
// on enter frame simulator
private DispatcherTimer _timer;
private static int FPS = 24;
public ImageSpace3D()
{
InitializeComponent();
// add the holder to the canvas
_holder.SetValue(Canvas.LeftProperty, Width/ 2);
_holder.SetValue(Canvas.TopProperty, Height / 2);
LayoutRoot.Children.Add(_holder);
// add all the texts to the stage
addImages();
// not enough? create more
addImages();
// start the enter frame event
_timer = new DispatcherTimer();
_timer.Interval = new TimeSpan(0, 0, 0, 0, 1000 / FPS);
_timer.Tick +=new EventHandler(_timer_Tick);
_timer.Start();
}
/////////////////////////////////////////////////////
// Handlers
/////////////////////////////////////////////////////
void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Image image = sender as Image;
Point3D point3D = _imagePoint3Ds[image];
// set the _camera to the position of the text
_destination.z = point3D.z + SPACE_LENGTH;
_destination.y = point3D.y + IMAGE_WIDTH / 2 * NEW_SCALE;
_destination.x = point3D.x + IMAGE_HEIGHT / 2 * NEW_SCALE;
}
void _timer_Tick(object sender, EventArgs e)
{
// move the _camera automatically
_destination.z += Z_MOVEMENT;
_camera.z += (_destination.z - _camera.z) / Z_MUL;
_camera.x += (_destination.x - _camera.x) / X_MUL;
_camera.y += (_destination.y - _camera.y) / Y_MUL;
// rearrange the position of the texts
posImage();
}
/////////////////////////////////////////////////////
// Private Methods
/////////////////////////////////////////////////////
private void addImages(){
int seed = (int)DateTime.Now.Ticks;
for (int i = 0; i < IMAGES.Length; i++)
{
// create a random object
seed += (int)DateTime.Now.Ticks;
Random r = new Random(seed);
// load the image
String url = IMAGE_PATH + IMAGES[i];
Image image = new Image();
image.Source = new BitmapImage(new Uri(url, UriKind.Relative));
// randomly assign the position
Point3D point3D = new Point3D();
point3D.x = r.NextDouble() * Width - Width / 2;
point3D.y = r.NextDouble() * Height - Height / 2;
point3D.z = r.NextDouble() * SPACE_LENGTH * 2 - SPACE_LENGTH;
// update the image property
image.SetValue(Canvas.LeftProperty, point3D.x);
image.SetValue(Canvas.TopProperty, point3D.y);
image.MouseLeftButtonDown += new MouseButtonEventHandler(image_MouseLeftButtonDown);
image.Cursor = Cursors.Hand;
_imagePoint3Ds.Add(image, point3D);
_holder.Children.Add(image);
_images.Add(image);
}
}
private void posImage()
{
for( int i = 0; i < _images.Count; i++){
Image image = _images[i];
Point3D point3D = _imagePoint3Ds[image];
double zActual = SPACE_LENGTH + (point3D.z - _camera.z);
double scale = SPACE_LENGTH / zActual - EFFECT_FACTOR;
// update the image position and scale
if(scale > 0){
image.SetValue(Canvas.LeftProperty, (point3D.x - _camera.x) * scale);
image.SetValue(Canvas.TopProperty, (point3D.y - _camera.y) * scale);
ScaleTransform scaleTransform = new ScaleTransform();
scaleTransform.ScaleX = scale * NEW_SCALE;
scaleTransform.ScaleY = scale * NEW_SCALE;
image.RenderTransform = scaleTransform;
image.Opacity = 1 - 0.99 * zActual / SPACE_LENGTH * 0.5;
// sort the children according to the scale
image.SetValue(Canvas.ZIndexProperty, (int)(SPACE_LENGTH - point3D.z));
}else{
// if text move over the screen, place it at the back
point3D.z += SPACE_LENGTH * 2;
}
}
}
}
}
xaml
<UserControl x:Class="ImageSpace3D.ImageSpace3D"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="550" Height="400">
<Canvas x:Name="LayoutRoot" Background="White">
</Canvas>
</UserControl>
point 3D
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
/*
* A 3D Image Space Demonstration in C#
*/
namespace ImageSpace3D
{
public class Point3D
{
public double x;
public double y;
public double z;
public Point3D()
{
}
public Point3D(double x, double y, double z){
this.x = x;
this.y = y;
this.z = z;
}
}
}
Google在7月发布了新的Android应用程序许可验证服务, 以防止Android应用可被轻松破解的尴尬局面。但是这一新的版权保护形式依然很脆弱,脆弱到依然可以被轻松破解。只要用一个叫做 smali/baksmali的程序将.apk文件解包,把里面的授权验证库给移除掉,再重新打包成.apk文件就完成了破解。因为.apk文件里的授权 验证库是独立的一个部分,所以很好分离出来,完成破解。目前这一方式还存在于理论上,不过估计很快就会有人做出自动工具来实现批量.apk文件的破解。

Google迅速通过Android Developers Blog对此事做出了回应,他们对此有6点看法:
1. 目前的Android授权服务还很年轻,不成熟
2. 用现在这种Android授权服务目的是简化开发者的工作,在以前的应用软件上很轻易的可以使用上新的授权服务,而其目的并不在加强被破解的安全性上
3. 有些开发者自己做的应用本来就太容易被攻击,他们最好看看Google的建议
4. 目前迁移到新的授权服务的应用还很少,不过会逐渐增加,因为它是未来的趋势
5. 只要运行第三方代码,100%的版权保护在任何系统里都是绝对不存在的,Google的方法已经增加了破解的难度
6. 过于严格的正版验证方式会损害用户的购买和使用体验
尽管Google说的句句在理(尤其最后一条,你不希望运行一个Android应用还要在手机上插一个USB狗来做正版验证吧……),而且iOS应 用一样也很好破解,不过还是希望Google能进一步增强Android授权服务,来保护开发者的利益和整个应用开发圈的生态平衡吧。

