面前app当完成测试,没问题,以完成整个老龄化阶段包含数据收发器,关键在 adb shell top -m 5 我发现我的 app pid 占用
CPU是最多的,事实上我想说写一个app是不难,你又没有全面的分析app的内存占用?避免一些OOM之类的问题,和其它可
能带来的一些偶发性问题。这些预计非常多小伙伴都没考虑,没事,今天就给大伙说说这方面的东西。虽说不是什么高难度的
知识点,但最重要的是养成这种习惯,才干在兴许的开发中降低不必要的时间浪费。以下我就带大家怎么发现而且解决问
题。一步一步分析
首先看看 我们的app cpu 占用情况:
我们能够看到 com.digissin.twelve 这个进程是一直排在第一位的,这个就是我们測试的进程,以下我带小伙伴们怎么发现问
题,而且及时纠正
首先我们要分析。为什么CPU 占用会那么高?是不是在主线程或者子线程做了耗时操作。网络操作,new 的实例对象过多?
带着这个疑问。我们看看DDMS而且分析下:
查看 com.digissin.twelve.RSUDPProtocol&PostBytesThread 134 行代码:
死循环读取状态导致的,但又不能去掉这个死循环,由于app须要这个死循环来给服务端进行通信,仅仅要非意外情况,app是
一直和后台保持通信的!当有数据传过来,isPause 会被设成true,代码流程就会走到if里面,一旦发完一条数据报。
isPause false while 就用进入了空死循环。不干不论什么事情,且频率非常快的循环运行。假设我们在这个死循环里面调用sleep()
尽管能成功。可是非常显然它是与app需求背道而驰的。所以必须排除,由于一旦进入sleep() 线程就不干活了,来自主线成的
协议分发的数据报发送就没不论什么意义了!所以这种方法就不可取了
所以我非常快想到了一个办法,就是当isPause false 的时候,我们就不须要子线程工作。那非常easy,我仅仅须要让他休眠,一旦
有来自协议分发过来的数据报。我们就wakeup 让子线程继续工作。那就非 wait() 和 notify() 莫属了 首先区分 Thread 和
Object 的 这两个东西里面的 wait() 和 notify() ,源代码分析太笼统了,我给大家举样例分析
在Thread 里直接调用这2两个函数是不会起作用的,我们须要创建一个Object对象来管理子线程的暂停和继续,意思就是说
子线程相当于一个普通员工。被new 出来的Object对象相当于一个管理者,员工要做什么须要管理者来通知和告知,即使员
工知道自己下一步该干什么想干什么,都须要管理者的同意才行!
员工也没法自己独立出来。就是不能自己做自己的事情,
否则整个管理模式会乱套,所以我们必须创建Object对象来对子线程做这个暂停和继续的控制着
所以我给这个内部类线程加 synchronized 字段。而且加入实例化静态方法,来创建这个Object(PostBytesThread)实例对象
别且给出暂停和继续函数:
private static PostBytesThread mThreadInstance = null; public synchronized static PostBytesThread getThreadInstance() { if (mThreadInstance == null) { mThreadInstance = new PostBytesThread(); } return mThreadInstance; } public synchronized boolean isPause() { return isPause; } public synchronized void setPause(boolean isPause) { this.isPause = isPause; } public byte[] getPost_bytes() { return post_bytes; } public void setPost_bytes(byte[] post_bytes) { this.post_bytes = post_bytes; } public synchronized void onThreadPause(){ try { Log.e(TAG, TAG+" onThreadPause() ----"); this.wait(); } catch (InterruptedException e) { Log.i(TAG, e.toString()); } } public synchronized void onThreadResume(){ Log.e(TAG, TAG+" onThreadResume() ----"); this.notify(); } @Override public void run() { if(udpSocket == null){ Log.i(TAG, TAG+" udpSocket is null"); return; } while(true){ Log.i(TAG, TAG+" isPause() state:"+isPause()); if(isPause()){ try { sendPacket.setData(getPost_bytes()); sendPacket.setLength(getPost_bytes().length); sendPacket.setAddress(serverAddress); sendPacket.setPort(DEFAULT_POTR); udpSocket.send(sendPacket); Thread.sleep(1000); setPause(false); } catch (InterruptedException e) { Log.i(TAG, "Exception:"+e.toString()); } catch (IOException e) { Log.i(TAG, "Exception:"+e.toString()); } }else{ onThreadPause(); } } } }
调用方式,回调接口收到来自主线程的协议消息数据包分发,并開始工作,当然仅仅是为了方便大家观看,事实上start()方法不用发在这里,由于这个同步对象仅仅有在子线程消亡才会被回收,所以相当于每次都多推断了一次这个同步对象的实例情况了
public void setPostBytesData(byte[] data){ PostBytesThread.getThreadInstance().start(); PostBytesThread.getThreadInstance().onThreadResume(); PostBytesThread.getThreadInstance().setPause(true); PostBytesThread.getThreadInstance().setPost_bytes(data); boolean isPause = PostBytesThread.getThreadInstance().isPause(); Log.d("PostBytesThread", "PostBytesThread isPause() state:"+isPause); }
处理完这段代码后我们继续查看 cpu的占用情况:
能够看到com.digissin.twelve的CPU占用大幅减少了。从而达到了我们的目的。在解决问题的同一时候,我也给大家说一个
常犯的错误,而且以代码和凝视的形式给大家看清楚
创建不必要的新实例:
在一些进度条更新或者上传下载数据等情况,我们通常须要对UI进行跟新之类的,这就涉及子线程跟Handler的交互。须要
我们不停地向Handler发送Message 对象,这时候就易犯这个错误。例如以下:
@Override public void run() { while(true){ try { SettingLocationTime(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void SettingLocationTime() throws InterruptedException{ if(handler!=null){ SendMessage(post_data); time = setting_time>0?setting_time:default_time; // Log.i(TAG, TAG+" SettingLocationTime() time:"+time); Thread.sleep(time*1000); } } /** * 这个函数会在run while(true)里面一直跑 * MessageBundle会被不停的创建新实例对象 * 所以这是个极低的错误!也是致命的! * */ private void SendMessage(byte[]data){ byte[]_data=ByteParseBeanTools.PostProtocolByte( ByteProtocolSessionType.LOCATION_STATE_SEND, data); Message msg = new Message(); // 不必要的 Message 新实例对象 msg.what=MainSessionUtil.SEND_POST_BYETS_DATA; Bundle bundle = new Bundle(); // 不必要的 Bundle 新实例对象 bundle.putByteArray(MainSessionUtil.BYTES_DATA_KEY, _data); msg.setData(bundle); handler.sendMessage(msg); }
解决方式:
@Override public void run() { while(true){ try { SettingLocationTime(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void SettingLocationTime() throws InterruptedException{ if(handler!=null){ SendMessage(post_data); time = setting_time>0?setting_time:default_time; // Log.i(TAG, TAG+" SettingLocationTime() time:"+time); Thread.sleep(time*1000); } } /** * 能够把Bundle放在class被载入的地方。实例化这个对象 * 装载完一次数据之后,下次调用之前运行clear()函数就可以。此时的bundle对象就相当于一个铁碗 * 每次装不同的水而已,就避免了每次开辟新的内存空间来存放Bundle对象 * Message 对象就更简单了,由于我这类回调了一个Handler对象过来,我们能够直接 * 调用Handler对象的obtainMessage()函数,这个函数当Handler被创建时。无论你用不用。它都在那里 * 随Handler消亡而消亡,不须要实例化。不须要创建,能够直接取出来用。这又避免了每次开辟新的内存空间 * 来装载Message对象,obtainMessage() 函数 来自 MessagePool * **/ private void SendMessage(byte[]data){ bundle.clear();// 倒掉碗里的老水(清空之前的缓存),装新来的水(填充来自回调函数的新数据) byte[]_data=ByteParseBeanTools.PostProtocolByte( ByteProtocolSessionType.LOCATION_STATE_SEND, data); Message msg = handler.obtainMessage(); // 来自 MessagePool msg.what=MainSessionUtil.SEND_POST_BYETS_DATA; bundle.putByteArray(MainSessionUtil.BYTES_DATA_KEY, _data);// 装新的水(填充新的数据源) msg.setData(bundle); handler.sendMessage(msg); }
这样CPU占用问题就能大幅减少,从而问题也能得到解决。
版权声明:本文博主原创文章,博客,未经同意不得转载。