很多初入Android开发的网友可能发现ANR的字样,到底Android ANR是什么呢? 其实ANR就是Application Not Responding的全称,当某个应用处于长期假死状态时Android系统会弹出一个窗口上面写道,XXX is not responding给出两个按钮一个为force close一个为wait。
可能触发ANR的情况
1. 长时间的I/O处理,比如读写大文件,网络访问时造成的阻塞。
2. 执行耗时的运算,Android默认为超过5000ms即5秒开始弹出ANR窗口,某些应用可能首次执行时没有缓存十分耗时,可以通过Splash播放闪屏Logo等方式来延缓加载
3. Service和appWidget中也要注意多线程的使用,除非它和Activity工作在不同的进程。
避免ANR的方法
1. 单独开工作者线程,通过独立的Thread或使用类似AsyncTask的方式来处理耗时的内容。
2. 耗时的操作尽量分段处理,使用类似状态机的方法,类似Symbian的活动对象将一个复杂的事情,分段执行。
3. UI线程中不要处理过多的内容,比如将一个5MB的文本,让TextView去setText,要知道这种UI操作,没有什么好方法去解决的,所以,遇到UI中需要执行复杂的操作,可以参考上面2提到的分段处理方式。
通常我们喜欢把很多控件层(UILabel,UIButton,UIView等)一起放到一个大的UIView容器来显示我们的内容,这个方法一般是可以的,但是如果要经常重新刷新内容的大区域界面,多数发生在iPad的应用中,这个方法会带来过多的内存使用和动画的延迟(比较卡),例如,scrollview的动画比较卡,又或者,经常收到内存警告。其中一个重要原因是每个控件,特别是透明底的,会多次重新绘制(drawRect)过多。其解决办法是,尽量将几个控件合并到一个层上来显示,这样系统会减少系统调用drawRect,从而带来性能上的提升。
很简单的一个例子,就是iNotes提供手写功能,用户可以在iPad屏幕上写出不同的笔画,开始的设计是,用户每写一划,iNotes就会生成一个新的透明底UIView来保持这个笔画,用户写了10笔,系统就生产了10个UIView,每个view的大小都是整个屏幕的,以便用户的undo操作。这个方案带来严重的内存问题,因为系统将每个层都保持一个bitmap图,一个像素需要4bit来算,一个层的大小就是 4x1024x768 ~ 3M, 10个层就是 10x3M = 30M,很明显,iPad很快爆出内存警告。
这个例子最后的方案是,所有笔画都画在同一个层,iNotes可以保存笔画的点进行undo操作。这样的方案就是无论用户画多少笔画,界面重画需要的资源都是一样的。
很多程序员比较懒,网络上拿下来的图片,直接就用UIImageView将其显示给用户,这样的后果就是,程序需要一直保存着大尺寸的图片到内存。解决办法应该是先将图片缩小到需要显示的尺寸,释放大尺寸图片的内存,然后再显示到界面给用户。
例如,很多界面设计者喜欢在界面上放一个大底图,但这个底图是老是占用着内存的,最佳方案是,设计出一个小的pattern图,然后用这个方案显示成底图。
UIImage *smallImage = [[UIImage alloc] initWithContentsOfFile:path];
backgroundView.backgroundColor = [UIColor colorWithPatternImage:smallImage];
[smallImage release];
一般objective-c的习惯是,用完的资源要立即释放,因为明白什么时候用完某个资源的是程序员你自己。例如,我们要读较大的图片,把它缩小后,显示到界面去。当大图片使用完成后,应该立即释放。代码如下:
UIImage *fullscreenImage = [[UIImage alloc] initWithContentOfFile:path];
UIImage *smallImage = [self resizeImage:fullscreenImage];
[fullscreenImage release];
imageView.image = smallImage;
......
for(UIView *subview in bigView.subviews) {
// 使用autorelease pool自动释放对象池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImageView *imageView = (UIImageView *)subview;
// subview处理代码
.......
// 销毁自动释放对象
[pool drain];
}
自动释放对象池把每个循环内生成的临时对象使用完后立即释放
以上的意见是本人多年来编写iPad/iPhone程序的经验,另外iOS4.0的multi-tasking特性发布后,程序可以被调入后台运行,苹果 工程师的意见是,进入后台运行时,你的应用应该释放掉能释放的对象,尽量保持在16M左右,这样别的程序运行时才不容易把你的应用挤掉。
经典游戏,猜数字。各种不解释,因为最近发生了一些事情,所以好几天没有发了,两年来在学校经历的事情确实很丰富,当然丰富并不是你们想象的,很多都不是我自己去选择的,而是只能忍受。分享自己的感受:为什么那么多人要追求财富和权力,因为他们遭遇到了太多需要钱和权的事情了。学生是弱势群体,因为他们没钱也没权,在家里,他们的保障来自于父母,在学校,他们的保障来自于学校,当学校不对学生负责时,他们要找谁?学校两年来都在告诉我们,我们谁都不能靠,只能靠自己。只能靠自己。因为只有自己才不会在足够的利益面前抛弃自己。不想说原因,在这个和谐的社会里。
下面给出效果图和原代码:
package com.wjh.demon_15;
import java.util.Random; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class Demon_15 extends Activity implements OnClickListener { public static final int INPUT = 1; public EditText m_TFInput; //文本编辑框 public TextView m_strItem; //文本显示组件 public Button inputB ; public int m_aNum[]; //存储目标数 public int m_nTimes = 0; //输入的次数 public boolean m_bEndGame = false; //游戏结束的标志 @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); this.setTitle("猜数字"); //产生目标数 setContentView(R.layout.main); m_TFInput = (EditText)findViewById(R.id.ed); m_strItem = (TextView)findViewById(R.id.tv); inputB = (Button)findViewById(R.id.input_b); inputB.setOnClickListener(this); m_aNum = new int[4]; InitNum(); } public void InitNum(){ //产生随机的目标数 Random random = new Random(); int k = random.nextInt(); int m = 0; for( int n = 0; n < 4; n ++ ){ k = random.nextInt(); m_aNum[n] = Math.abs(k % 10); //for循环语句,确保四个数位上的数字无重复 for( m = 0; m < n; m ++ ){ //若与前面的数字重复,则用加1的办法保证不重复 if( m_aNum[n] == m_aNum[m] ) m_aNum[n] ++; //若加1后大于9则回到0 if( m_aNum[n] > 9 ) m_aNum[n] = 0; } } } public boolean CheckNumber( char cNum[] ){ //检测数据是否合法 if( cNum.length != 4 ) return false; for( int i = 0; i < 4; i ++ ){ for( int j = 0; j < i; j ++ ){ //有两个数字相同了,确定是非法输入 if( cNum[i] == cNum[j] ){ return false; } } } return true; } public void FeedBack(char cNum[]){ //根据输入数反馈信息 if( cNum.length != 4 ) return; int nB = 0; //记录B的个数,即m的值 int nA = 0; //记录A的个数,即n的值 for( int i = 0; i < 4; i ++ ){ for( int j = 0; j < 4; j ++ ){ //cNum[i]是字符,需先转换成对应的数字,再与m_aNum[j]进行比较 if( (int)(cNum[i] - '0') == m_aNum[j] ){ if( i == j ) nA ++; //如果位置也相同,则m的值加1 else nB ++; //如果位置不相同,则n的值加1 } } } if( nA == 4 ){ //猜数成功 m_strItem.setText("成功"); m_bEndGame = true; return; } else m_nTimes ++; if( m_nTimes > 5 ){ //猜数失败 m_strItem.setText("失败"); m_bEndGame = true; return; } //输出反馈信息 StringBuffer temp = new StringBuffer(); temp.append(nB); temp.append("B"); temp.append(nA); temp.append("A"); m_strItem.setText(temp.toString()); m_TFInput.setText(""); } @Override public void onClick(View v) { // TODO Auto-generated method stub if( m_bEndGame ) return; if (v == inputB){ //指令被触发 char cNum[] = new char[4]; m_TFInput.getText().toString().getChars(0,cNum.length,cNum,0); Log.d("ddd", m_TFInput.getText().toString()); if( !CheckNumber( cNum ) ) {//如果输入数不合法 m_strItem.setText(""); m_TFInput.setText(""); m_strItem.setText("非法数字"); return; } FeedBack(cNum); //反馈信息 } } }
apk文件(将后缀改为apk):Demon_15.zip
源代码:Demon_15.rar