Android Application Thread CPU GC Operatiing and OOM Question 0603-随手笔记

Android Application Thread CPU GC Operatiing and OOM Question 0603-随手笔记

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

面前app当完成测试,没问题,以完成整个老龄化阶段包含数据收发器,关键在 adb shell top -m 5  我发现我的 app pid 占用 

CPU是最多的,事实上我想说写一个app是不难,你又没有全面的分析app的内存占用?避免一些OOM之类的问题,和其它可

能带来的一些偶发性问题。这些预计非常多小伙伴都没考虑,没事,今天就给大伙说说这方面的东西。虽说不是什么高难度的

知识点,但最重要的是养成这种习惯,才干在兴许的开发中降低不必要的时间浪费。以下我就带大家怎么发现而且解决问

题。一步一步分析


首先看看 我们的app cpu 占用情况:

Android Application Thread CPU GC Operatiing and OOM Question 0603-随手笔记

我们能够看到 com.digissin.twelve 这个进程是一直排在第一位的,这个就是我们測试的进程,以下我带小伙伴们怎么发现问

题,而且及时纠正


首先我们要分析。为什么CPU 占用会那么高?是不是在主线程或者子线程做了耗时操作。网络操作,new 的实例对象过多?

带着这个疑问。我们看看DDMS而且分析下:

Android Application Thread CPU GC Operatiing and OOM Question 0603-随手笔记


查看 com.digissin.twelve.RSUDPProtocol&PostBytesThread 134 行代码:

Android Application Thread CPU GC Operatiing and OOM Question 0603-随手笔记


死循环读取状态导致的,但又不能去掉这个死循环,由于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的占用情况:

Android Application Thread CPU GC Operatiing and OOM Question 0603-随手笔记

 

能够看到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)里面一直跑
	 * Message\Bundle会被不停的创建新实例对象
	 * 所以这是个极低的错误!也是致命的!
	 * */
	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占用问题就能大幅减少,从而问题也能得到解决。


版权声明:本文博主原创文章,博客,未经同意不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116909.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Oracle 更改表名称的几种方式

    Oracle 更改表名称的几种方式1:使用命令直接修改(推荐)ALTERTABLEold_table_nameRENAMETOnew_table_name;(大写为系统命令)2:使用rename修改 SQL>selecttnamefromtab;TNAME——————————TESTSQL>renametesttotemp;T

  • Spatial Transformer Networks(STN)理解

    Spatial Transformer Networks(STN)理解文章目录STN的作用STN的基本结构前向过程Tensorflow部分实现代码实验结果DistortedMNISTGermanTrafficSignRecognitionBenchmark(GTSRB)datasetSTN的作用之前参加过一个点云数据分类的比赛,主要借鉴了PointNet的网络结构,在PointNet中使用到了两次STN。点云数据存在两个主要问题:1、无序性:点云本…

    2022年10月19日
  • Android面试题集锦(2019最新总结)

    一、20182018年的年假休完了,正式进入2019的工作中。也该规划一下自己的职业生涯了;是选择继续从事Android(android的话已经火了几年了,现在算是进入寒冬了,需要考虑清楚)?还是从事Java方面?还是改管理方面?如果继续从事Android方面,那么就要往资深的发展(需要搞FrameWork层,需要拿起C/C++),2019年了需要换个新的环境或者需要换个更好的平台;那还得…

  • Linux安装Android Sdk「建议收藏」

    Linux安装Android Sdk「建议收藏」在使用Jenkins+Gitlab集成自动化打包时,遇到Linux缺少AndroidSdk环境的问题,单独记录一下安装过程。sdk安装方式常规思路,下载sdk,安装之后修改环境。但是发现,网络上已经没有了sdk的下载资源,有的也只是很老的版本。查看Android开发文档——sdkmanager的使用指南,发现可以使用sdkmanager这个命令行工具进行下载。下载sdkmanager工具包官网下载页最底部-命令行工具下载,找到Linux平台的工具包使用wget下载到服务器wget-P/h

  • nginx 400 报错,故障排查

    nginx 400 报错,故障排查nginx的400错误比较难查找原因,因为此错误并不是每次都会出现的,另外,出现错误的时候,通常在浏览器和日志里看不到任何有关提示。经长时间观察和大量试验查明,此乃requestheader过大所引起,request过大,通常是由于cookie中写入了较大的值所引起。解决办法这:在nginx.conf中,将client_header_buffer_size和large_cl…

  • jquerycdn国内地址_jquery 官网

    jquerycdn国内地址_jquery 官网JqueryCDN如果您不希望下载并存放jQuery,那么也可以通过CDN(内容分发网络)引用它。StaticfileCDN、百度、又拍云、新浪、谷歌和微软的服务器都存有jQuery。如果你的站点用户是国内的,建议使用百度、又拍云、新浪等国内CDN地址,如果你站点用户是国外的可以使用谷歌和微软。如需从StaticfileCDN、又拍云、新浪、谷歌或微软引用jQuery,请使用以下代码之一:StaticfileCDN:<head><script.

    2022年10月22日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号