7.1.1推出以后我们该对自己的程序做下小升级以应对低端手机,
对有可能报错的地方做下处理,
对可以节省内存的地方尽量采用一些可以节省内存的方法替换原来的方法。
下面列出一些小技巧仅供参考。
1.下面是判断机器内存的helper用来区分256的机型
public static class LowMemoryHelper { public static bool IsLowMemoryDevice { get; set; } static LowMemoryHelper() { try { // Int64 result = DeviceStatus.ApplicationMemoryUsageLimit; Int64 result = (Int64)DeviceExtendedProperties.GetValue("ApplicationWorkingSetLimit"); IsLowMemoryDevice = result < 94371840L; } catch (ArgumentOutOfRangeException) { IsLowMemoryDevice = false; } } }
上面注释的部分和下一行的意思是一样的,按照微软msdn所说是推荐使用注释掉的那行,
但是它取值比GetValue的值要大,原因不清楚,
用getvalue的方式判断最大程序可用内存小于90M是256的机型(更确切的说是94371840 bytes)。
2.不支持PeriodicTask and ResourceIntensiveTask
用到这两个类的地方应该加入判断避免出现异常影响用户体验。
为什么不支持呢,看一下如果执行后台线程内存的分布情况就知道了,很接近crash!:
| 100MB | + | 60MB | + | 60MB | + | 15MB | ≈ | 256MB |
| (Operating system) | (Foreground app) | (ResourceIntensiveTask) | (AudioStreamingAgent) |
这里可以参考 Background Agents Overview for Windows Phone;
3.使用WebBrowserTask替代<WebBrowser />控件
4.用 BingMapsTask替代 <Map />控件
5.降低图像质量
6.长列表使用数据虚拟化
7.禁用跳转页面动画
8.减少使用SoundEffect的次数
9.压缩XNA资源
10.仔细排查内存泄露
11.另外如果不想针对7.1.1进行优化又担心256的用户下载后使用出现什么问题,然后给了差评,或者直接放弃这部分用户,还有个办法,O(∩_∩)O哈哈~
在WMAppManifest.xml文件的Capabilities标记下加入Requirements标记,方法如下:
</Capabilities> <Requirements> <Requirement Name="ID_REQ_MEMORY_90" /> </Requirements>
这样在256用户的市场里就看不到你的应用了。
在上一篇准备工作完成之后,我们对开发邮箱客户端的原理有了基本的认识。那么来看看我们在wp7上有哪些资源来供我们开发吧,也就是说看看wp7对开发邮箱提供了哪些API支持。wp7没有像android和。net framework上面的那种封装好的imap类也没有mail类,我们要自己做这些工作。
因为开发邮箱最基本的需求是在imap、stmp协议上的通信,他们属于tcp家族中的一员,原理很简单就是我对服务器发送一个指令,服务器针对指令做出响应,所以在这过程中需要用到通信的socket类,编码解码的encoding类,和多线程的控制,这些在wp7中都有了,好,我们可以开工了。
建一个基类tcpclientBase.cs 用来实现收发消息的基本功能
abstract public class TCPClientBase : IDisposable
{
/// <summary>
/// 端口
/// </summary>
const int IMAP_PORT = 143;
/// <summary>
/// 缓冲区大小
/// </summary>
const int MAX_BUFFER_READ_SIZE = 2048;
/// <summary>
/// 多线程通知超时时间
/// </summary>
const int TIMEOUT_MILLISECONDS = 5000;
/// <summary>
/// socket客户端
/// </summary>
Socket _socket;
protected static int IMAP_COMMAND_VAL = 0;
static ManualResetEvent _clientDone = new ManualResetEvent(false);
/// <summary>
/// 是否连接
/// </summary>
public Boolean IsConnect
{
get
{
if (_socket != null)
{
return _socket.Connected;
}
else
return false;
}
}
/// <summary>
/// 访问终端
/// </summary>
EndPoint _dnsEndPoint;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="HostName">主机名</param>
public TCPClientBase(string HostName)
{
Initialization(new DnsEndPoint(HostName, IMAP_PORT));
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ipaddress">IP地址</param>
public TCPClientBase(IPAddress ipaddress)
{
Initialization(new IPEndPoint(ipaddress, IMAP_PORT));
}
/// <summary>
/// 实例化连接
/// </summary>
/// <param name="endPorint"></param>
void Initialization(EndPoint endPorint)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_dnsEndPoint = endPorint;
SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs()
{
RemoteEndPoint = _dnsEndPoint,
};
socketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>((o, e) =>
{
_clientDone.Set();
});
_clientDone.Reset();
_socket.ConnectAsync(socketAsyncEventArgs);
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
ResponseReceivedResult result = this.Receive();
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="data"></param>
/// <param name="onComplete"></param>
protected void Send(string data, Action<ResponseReceivedEventArgs> onComplete)
{
if (_socket == null)
{
throw new Exception("连接已关闭");
}
SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs() { RemoteEndPoint = _dnsEndPoint };
byte[] byteDate = Encoding.UTF8.GetBytes(data);
socketAsyncEventArgs.SetBuffer(byteDate, 0, byteDate.Length);
socketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>((o, e) =>
{
ResponseReceivedEventArgs arg = new ResponseReceivedEventArgs();
arg.isError = e.SocketError == SocketError.Success ? true : false;
arg.response = string.Empty;
if (onComplete != null)
{
postUI(() =>
{
onComplete(arg);
});
}
_clientDone.Set();
});
_clientDone.Reset();
_socket.SendAsync(socketAsyncEventArgs);
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
}
/// <summary>
/// 抛到UI主线程
/// </summary>
/// <param name="onComplete"></param>
private static void postUI(Action onComplete)
{
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
onComplete();
});
}
/// <summary>
/// 接收
/// </summary>
/// <returns></returns>
protected ResponseReceivedResult Receive()
{
ResponseReceivedResult args = new ResponseReceivedResult();
string response = "Time Out!";
if (_socket == null)
{
throw new Exception("连接已关闭");
}
SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs() { RemoteEndPoint = _dnsEndPoint };
socketArgs.SetBuffer(new byte[MAX_BUFFER_READ_SIZE], 0, MAX_BUFFER_READ_SIZE);
socketArgs.Completed += new EventHandler<SocketAsyncEventArgs>((o, e) =>
{
if (e.SocketError == SocketError.Success)
{
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
args.isError = true;
}
else
args.isError = false;
_clientDone.Set();
});
_clientDone.Reset();
_socket.ReceiveAsync(socketArgs);
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
args.response = response;
return args;
}
protected virtual void Disconnect()
{
if (IsDisposed)
{
throw new ObjectDisposedException("TCPClientBase");
}
if (!IsConnect)
{
throw new InvalidOperationException("TCPClient is not connected");
}
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
_socket.Dispose();
_clientDone.Dispose();
_dnsEndPoint = null;
}
/// <summary>
/// 实现dispose接口
/// </summary>
#region Dispose
private bool IsDisposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
lock (this)
{
if (!this.IsDisposed)
{
if (disposing)
{
#warning 要做异常捕获
Disconnect();
}
IsDisposed = true;
}
}
}
~TCPClientBase()
{
Dispose(false);
}
#endregion
}
然后继承TCP类实现imap和smtp的功能……
待续……
很长时间没有发博文了,今天为了记录下这几天研究的成果特意发一篇博文,因为很多事情当时记得很清楚过几个月甚至几年可能忘得一干二净,到下次再用的时候还得重新查阅一遍资料,为了给下次自己或者新人减轻点痛苦留下这篇文档。
首先编写邮件的imap4、stmp协议必须要熟悉他们,知道他们怎么样对服务器发送指令,服务器在什么情况下回复什么内容。详细imap4文档看这里
我们还需要用到一个测试工具telnet用来向服务器发送指令并查看返回结果,配置Telnet的方法(在win7中):
Windows系列不知不自觉就发展到了Win7 这一个版本,Win7颠覆了很多内容,在一些设置方面也有所改动。那么在Win 7中 Telnet该如何启动并设置呢。
Win 7 Telnet的启动
Telnet是系统管理员常用的远程登录和管理工具,在Windows 2000/XP/2003/Vista系统中它作为标准的系统组件集成到系统中供用户使用。
不过默认情况下Telnet服务是被禁止,通常情况下我们只需运行services.msc打开服务管理,找到Telnet服务项设置其启动类型为“手动”或者“自动”,然后启动该服务即可使用了。
不过在Windows7中,你按照上述方法是不能找到并启用Telnet服务的。那如何找回Win 7 Telnet功能呢?
1、隐藏原因
其实,在服务管理器中找不到Telnet并不是Win7抛弃了Telnet,而是默认状态下Win7并没有安装Telnet服务。
这也是微软第一次从个人系统中将Telnet剔出了系统默认组件之外,这和Windows Server 2008类似。
我想微软这么做,应该是出于安全性考虑,毕竟Telnet的数据是以明文传输的,攻击者很容易通过嗅探获取敏感信息。
基于安全性考虑,建议大家还是使用安全性更高的、加密的SSH远程管理方式。
2、安装Telnet
不过,我们也没有必要因噎废食,作为个人用户Telnet还是非常方便的。安装Win 7 Telnet和Windows Server 2008略有不同,大家可以通过下面的操作方法实现。
依次点击“开始”→“控制面板”→“程序”,“在程序和功能”找到并点击“打开或关闭Windows功能”进入Windows 功能设置对话框。
找到并勾选“Telnet客户端”和“Telnet服务器”,最后“确定”稍等片刻即可完成安装。除此之外,我们也可通过该向导安装“TFTP客户端”、“Internet 信息服务”等。
安装完成后,Telnet服务默认情况下是禁用的。还需执行“开始”→“运行”,输入servcies.msc打开服务管理器。
找到并双击Telnet服务项,设置其启动方式为“手动”(更安全,只在需要的时候才启用),最后“启动”该服务“确定”退出即可。
一些相关资料:
最近移动平台方面的竞争真是风云涌动,时时刻刻牵动着千万程序员的心,无论ms,苹果,google谁胜谁负都会影响着我们苦逼程序员对未来的规划。我只能默默的期盼ms给点力!不要让我在silverlight上花的功夫白费!废话不多说回归正题。
今天重写Equals方法顺手做个总结,都说好记性不如烂笔头,让自己记忆深刻的同时也分享给大家。重写的时候有几点需要注意的地方先上一段代码,然后我再逐一解释。
Class MyRefType:BaseType
{
RefTyoe refobj; //该字段是一个引用类型
ValType valobj; //该字段是一个值类型
public olverride Boolean Equals(Object obj)
{
if(obj==nulll)return false;
if(this.GetType()!=obj.GetType())return false;
//比较其中的引用类型字段
MyRefType other=(MyRefTyppe)obj;
//比较其中的值类型字段
if(!Object.Equals(refobj,other.refobj)) return false;
if(!valobj.Equals(other.valobj)) return false;
return true;
}
public static Boolean operator==(MyRefType o1,MyRefType o2)
{
return Object.Equals(o1,o2);
}
public static Boolean operator!=(MyRefType o1,MyRefType o2)
{
return !(o1==o2);
}
}
(代码是手敲的 可能有错误)
1、比较引用类型的字段
要比较引用类型的字段,我们应该调用Object的静态方法Equals方法,Object的静态Equals方法是一个比较两个应用类型的辅助方法。下面展示下Object的静态Equals方法的内部实现:
public static Boolean Equals(Object objA,Object objB)
{
//如果objA和objB指向的是同一个对象
if(objA==objB)return true;
if(objA==null || objB==null) return false;
return objA.Equals(objB);
}
所以我们采用这方式即使比较的两个字段出现值为null的情况也不影响代码的正常运行。如果refobj为null,调用refobj.Equalls(other.refobj)将会抛出NullReferenceException异常。Object的静态Equeals这一辅助方法会为我们对出现null的情况做检测。
2.比较值类型的字段
比较两个值类型的字段我们就应该用字段的Equalls方法来进行比较,因为值类型永远不可能为null,并且值类型调用Object.Equals静态辅助方法会对值类型对象执行一次装箱操作,损耗系统的性能。
此篇文章只是拾人牙慧而已,顺带总结过程避免浪费脑细胞。过程比较曲折,也希望遇到的各种问题和错误解决过程能给后来的同学们省时间。
周末拿到HD7后一直在摆弄,发现了一个可以使用对焦摄像头的第三方程序,还有大名鼎鼎的ZXing Barcode的WP7移植版。当时觉得奇怪,因为按照之前看过的教程来看,微软开放出来的摄像头接口只有一个PhotoTask,作用是切换到内置摄像头程序,等用户拍照完毕后返回照片数据,无法直接在程序中打开摄像头查看图像。于是祭出神器Reflector查看,它用到了Microsoft.Phone.Media.Extended.PhotoCamera和VideoCamera类。然后通过ZXing的代码验证,确实如此。
打开VS2010 Express创建工程,却发现根本没有这类——连Microsoft.Phone.Media.Extended这个程序集都没有。顺藤摸瓜找到了Den Delimarsky写的两篇文章Not your regular photo and video camera on Windows Phone 7和How VideoCamera/PhotoCamera content is saved on Windows Phone 7,前一篇介绍了如何使用PhotoCamera和VideoCamera进行摄像头图像实时显示,后一篇则是介绍怎么取到拍照后的数据,写得很棒,通俗易懂。文章中提到:
通过反射调用摄像头比较慢而且增加多余的代码。所以我转而使用GAC程序集“GAC_Microsoft.Phone.Media.Extended_v7_0_0_0_cneutral_1.dll”,由于是托管代码,所以就不用加上WMInteropManifest.xml文件(或者<Capability Name=”ID_CAP_INTEROPSERVICES”/>)了。
并且提供了GAC Dump(可以看作未开放API程序集的集合)下载。不过没有详细教程,于是搜了一下实现GAC Dump的牛人Thomas Hounsell的Blog,找到了方法 Avoiding Reflection: Adding the InteropServices library to the WP7 SDK。总结起来很简单:
首先下载Hounsell那篇文章里面的7z文件。
捡自己需要的放到C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone,记得改名,把前后下划线前的内容都去掉。比如对于原生摄像头,我们需要Microsoft.Phone.Media.Extended.dll。
打开RedistList文件夹里面的FrameworkList.xml文件,记得用管理员权限。新增一个File标签(复制之前的就行),程序集名改成你刚刚加入的那个dll的,去掉publicKeyToken属性。保存。
于是我欣然照做(后来尝试发现,对于InteropServices需要这么做,但Media.Extended似乎不用,dll扔过去就可以了),结果……
搞屁啊……连设计器都打不开了!只好再搜,找到个帖子[Q] Usage of Microsoft.Phone.Media.Extended,问题类似,都是“强名称验证失败”,不过帖子中还能打开设计器而运行时出现COMException:
解决方法很简单,利用Window SDK的工具sn.exe执行下面的命令跳过验证,看到后面的信息即可(也要用管理员权限,或者从VS命令行执行):
sn.exe -Vr [dll path]
Verification entry added for assembly ‘Microsoft.Phone.Media.Extended,24EEC0D8C86CDA1E’
有关强名称和sn.exe的信息可以参看具有强名称的程序集和强名称工具 (Sn.exe)。简单说来强名称就是带有唯一公钥信息的程序集,好处是可以验证程序集的可靠性以及dll名字可以随便改。但坏处就是现在遇到的问题了:每台机器的.net环境都需要强名称程序集验证后才能使用。
其实这个地方还是衰了一下,因为我发现Windows Phone Developer Tools中的Visual C# Express并没有传说中的sn.exe(靠……),于是又跑到微软网站上下了个Visual C++ Express 2010,这才彻底搞定。如果你直接装了Pro版那就没这个困扰了。
最后再说一下有关PhotoCamera的使用。首先建议详读Den Delimarsky那两篇文章,基本覆盖了所有使用和可能碰到的问题。除了里面提到的API外,还有个比较有用的是GetCurrentFrame()。这个函数接受一个WritableBitmap参数,尺寸一定要是640 x 480,不然会有异常抛出来。另外拿到的WritableBitmap最好不要尝试通过ImageBrush画出来,不然会慢死(模拟器上大概200ms处理一帧,真机也就500ms)。下面截图就是模拟器的效果,大的白色区域(加那个黑方块)是原生的View Finder,左上角的是通过GetCurrentFrame()后重新画的。