对Thread中MessageQueue进行操作来优化性能

         在之前的文章中总结了Handler的使用,Handler将Message添加到MessageQueue中后一个一个处理,假如发送的消息Message过多,每个都去处理的话会很耗时,这时如果从MessageQueue中筛选出去一部分,那么处理时间会减少很多,也不会有卡顿的感觉。

       我们来解决一个可能在项目工程上遇到的问题,我们每个人都有过这种操作:使用SeekBar(拖动条)来调节系统的音量,比如在电脑上改变音量大小的操作。在实现这一功能之前,我们先把要达到的效果列出来:

      1.我们要使这个整个划动过程尽可能的流畅,不能出现划动时卡顿的情况;

      2.我们在划动SeekBar的过程中,系统音量的变化过程要实时反应出来;  

      3.不能因为过快的划动,导致系统的崩溃。

      系统的音量大小设置很容易实现,但是要注意的是:更改音量大小是一个耗时操作,就像我们的Http请求,那么我们就需要创建新的线程来异步完成请求,才不会对UI界面造成堵塞,这样就满足了第一个效果。我们假设整个音量大小设置请求完成时间是50ms,而且我们在拖动SeekBar的可快可慢的,下面来分析过快和过慢会产生的情况。

      我在程序中设置SeekBar(拖动条)的值的范围是0~100,如果我们只用1s的时间从0划动到了100,中间会产生100个值(此处这么说是有问题的,后面我会加以纠正,先这样假设),我们可以算一下理想情况下每个值都请求音量的更改,那么我们会在之后100*0.5s-1s=49s的时间内听到系统音量的渐变过程。只用1s完成音量大小的拖动,花了近50s的时间才反应出音量的变化过程,这是多么不可忍受。如果划动过慢,那么有足够的时间来完成音量的设置并实时反应出来,这是没问题的,那么我们来分析过快划动出现的问题该如何优化。

      1s的时间完成整个划动过程时,系统还在处理前面的值,为了将系统的音量大小实时的反应出来(满足效果2),这时应该优先处理当前的值。解决方法是:既要向系统不断发送调整值,保证用户能实时听到声音变化,然而也不能发送的值过多,以保证有足够的时间来处理。我们在程序中的方法是:对用户在单位时间内产生的值进行筛选,也就是每次Handler往MessageQueue里添加Message时,要对MessageQueue中的Message进行取舍。

       那么该如何取舍呢?其实有个很简单的方法:每次Handler往MessageQueue中添加Message时,将MessageQueue进行清空操作,原因是在最新产生的Message未添加MessageQueue中之前,正在处理的那个Message已经给了它处理时间了,至于它后面排队的那些Message没必要给他们处理时间了,直接清除,要不然系统就无法实时反应出当前最新的音量值了,只会导致消息队列中的Message越来越多。

       下面来看一下代码的实现以及后面会讨论一些实际的问题。

        xml布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/volume_value" 
        android:layout_marginTop="100dp"/>
    
    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:max="100"
        android:progress="0"/>

</LinearLayout>    
从android:max=100可以看出我设置Seekbar的范围是0~100。

每次SeekBar在被拖动其值改变时会回调下面的函数,通过Handler发送到消息队列中,在发送前清空MessageQueue队列。

@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
			boolean fromUser) {
		Log.i(TAG,String.valueOf(progress));
		mHandler.removeMessages(0);//清除MessageQueue中所有的Message
		mHandler.sendMessage(mHandler.obtainMessage(0,progress,0,0));
	}

其实我们上面的假设1s内产生100个值是有问题的,Android内部就对SeekBar进行了优化,当拖动速度过快时,回调给onProgressChanged的值就是被筛选的,可能是0,20,30,40,50,60,70,80,90,100,这些数值被处理时,可能也会导致不及时。但总之,我在这里举的这个实例是为了锻炼Handler在Thread中的使用。是为解决与之类似的问题提高思路。

下面是项目源代码:

package com.example.testvolumecontrol;

import java.util.Calendar;
import java.util.Date;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity implements OnSeekBarChangeListener{
	
	private static final String TAG="MainActivity";
	public Date src,dist;
	public Calendar cal_src,cal_dist;
	public AudioManager audioManager;
	public int maxValue;
	private TextView text;
	private SeekBar seekBar;
	private HandlerThread handlerThread;
	private Handler mHandler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		text=(TextView)findViewById(R.id.text);
		seekBar=(SeekBar)findViewById(R.id.seekbar);
		seekBar.setOnSeekBarChangeListener(this);
		audioManager=(AudioManager)getSystemService(Context.AUDIO_SERVICE);
		maxValue=audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
		Log.i(TAG,"系统最大音量:"+String.valueOf(audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)));
		
		handlerThread=new HandlerThread("change_volume");
		handlerThread.start();
		mHandler=new Handler(handlerThread.getLooper()){
			
			@Override 
			public void handleMessage(final Message msg){
				super.handleMessage(msg);
				switch(msg.what){
				
				case 0:
					src=new Date(System.currentTimeMillis());
					cal_src = Calendar.getInstance();
					cal_src.setTime(src);
					float f=msg.arg1/100F;
					int value=(int)(f*maxValue);
					//Log.i(TAG,"音量值:"+String.valueOf(value));
					audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, value,AudioManager.FLAG_PLAY_SOUND);
					dist=new Date(System.currentTimeMillis());
					cal_dist = Calendar.getInstance();
					cal_dist.setTime(dist);
					long i=cal_dist.getTimeInMillis()-cal_src.getTimeInMillis();
					Log.i(TAG, "时间差值为"+String.valueOf(i));
					break;
					
				}
			}
		};
	}

	@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
			boolean fromUser) {
		Log.i(TAG,String.valueOf(progress));
		mHandler.removeMessages(0);//清除MessageQueue中所有的Message
		mHandler.sendMessage(mHandler.obtainMessage(0,progress,0,0));
	}

	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {
		//这里是开始滑动时的回调
	}

	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {
		//这里是滑动结束时的回调
	}

}

      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值