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

来源:互联网 发布:谌洪果 知无知2017 编辑:程序博客网 时间:2024/05/08 07:09

         在之前的文章中总结了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队列。

@Overridepublic void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {Log.i(TAG,String.valueOf(progress));mHandler.removeMessages(0);//清除MessageQueue中所有的MessagemHandler.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;@Overrideprotected 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;}}};}@Overridepublic void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {Log.i(TAG,String.valueOf(progress));mHandler.removeMessages(0);//清除MessageQueue中所有的MessagemHandler.sendMessage(mHandler.obtainMessage(0,progress,0,0));}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {//这里是开始滑动时的回调}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {//这里是滑动结束时的回调}}

      

0 0
原创粉丝点击