深入解析android log的分析方法(2)

 三,如何通过Handler或者多线程来解决某操作执行时间过程的问题 。(update on Jan.19)

结合上面的分析,我们知道问题似乎是线程队列中某个操作presentRegionMedia(view, (RegionMediaModel) model, dataChanged);执行时间太长所导致的界面无响应 。 因此比较典型的做法当然是控制线程队列 。 在这里我们不得不提一下Handler  . 

Handler在Android中是什么样的作用和地位呢?

1, 线程之间消息传递 , 通过sendMessage方法 。 我们通常用来后台子线程向主线程传递消息,主线程接到通知之后做更新界面等操作 。

2, 通过管理消息队列(MessageQueue)来安排计划任务 。 这个常常会被人忽略,很多书上也没有提到这个作用 。

Handler这个单词中文意思是管理者,处理者的意思 。 通过这个意思顾名思义,我们知道这个对象就是个操作对象。那么要操作谁呢 ?

当然是消息队列(MessageQueue) 。Android消息队列类似于Win32队列设计 。 都是采用线性结构,先进先出 。 其实在智能手机平台很久以前就用这种消息结构了 。 比如Palm , 只不过Palm是整个进程共享一个消息队列,而Android是线程为单位的队列罢了 。

那么是否每个线程或者子线程都有消息队列呢 ?

很遗憾,不是的,也没有必要 。 在Android中,只有使用了Looper的线程才有消息队列 。 当然如果你要简单建立一个有消息队列的线程也很方便,直接使用HandlerThread即可,这个类继承于Thread类 。怎么用我就不多说了吧 。你懂的 !

Handler有两种方式来操作消息队列 。

一种是通过sendMessage(Message)方法 ,发送消息体

另一种是通过post(Runnable) 方法 , 发送Runnable对象  。

注意:这点请注意 ,虽然发送方法含参不同 , 但他们使用的是同一个消息队列 。 我记得Mars的视频教程上说有两个队列,一个是消息队列,一个是线程队列 。 这种说法是错误的 。事实上只有一个消息队列,没有所谓的线程队列 。  当然了 , post(Runnable)也没有启动新的线程,仍然是在当前线程 。

注意:还有一种说法 ,说Handler对象在主线程,这种说法也是错误的 , 准确的说是在产生他的线程中 。 虽然常常我们是在主线程产生他的 。

那么我们要在Android建立多线程程序该如何做呢?很简单,就是Java的多线程方式。要么实现Runnable接口,要么继承Thread类 。

关于线程同步,线程锁定,线程异步 ,线程池 这些概念也是一样的 。 我就不累述了。

 

好了,经过一点儿简单的介绍,我们有了一些Handler的基础,现在开始回到我们的问题开始来分析 :

mHandler.post(new Runnable() {
                    public void run() {
                        try {
                            presentRegionMedia(view, (RegionMediaModel) model, dataChanged);
                        } catch (OMADRMException e) {
                            Log.e(TAG, e.getMessage(), e);
                            Toast.makeText(mContext,
                                    mContext.getString(R.string.insufficient_drm_rights),
                                    Toast.LENGTH_SHORT).show();
                        } catch (IOException e){
                            Log.e(TAG, e.getMessage(), e);
                            Toast.makeText(mContext,
                                    mContext.getString(R.string.insufficient_drm_rights),
                                    Toast.LENGTH_SHORT).show();
                        

                        }
                    }

从上面这段代码中,我们可以看出,在做播放器控制按钮(比如播放,暂停,停止)等操作的时候 , 是通过Handler.post(Runnable)来放到消息队列中 , 排序来处理 。 那么之所以这里出现了无响应,很有可能是因为某一项控制操作太耗时或者耗资源 。 这时候又接收到新的要处理的消息,就会处理不过来了 。 因此我试图让队列中同时只有一个控制播放器按钮的任务在 。 我对代码做了如下改动:

Runnable r = new Runnable(){
                    public void run() {
                        try {
                            presentRegionMedia(view, (RegionMediaModel) model, dataChanged);
                        } catch (OMADRMException e) {
                            Log.e(TAG, e.getMessage(), e);
                            Toast.makeText(mContext,
                                    mContext.getString(R.string.insufficient_drm_rights),
                                    Toast.LENGTH_SHORT).show();
                        } catch (IOException e){
                            Log.e(TAG, e.getMessage(), e);
                            Toast.makeText(mContext,
                                    mContext.getString(R.string.insufficient_drm_rights),
                                    Toast.LENGTH_SHORT).show();
                        

                        }
                    }

mHandler.removeCallbacks(r) ;

mHandler.post(r) ;

 代码慢慢看,思路很简单 :其实就是在post Runnable之前先清除队列中已存的相同Runnable实例 。 这样可以保证同时队列中只有一个操作在处理 。

 

很遗憾,不生效 。:(  ,改动之后,问题依然存在 ,欲哭无泪 。

 

再来,我将整个模式改为message再试试 ,核心代码如下 :

  if(mHandler.hasMessages(MEDIA_PLAY_WHAT_MESSAGEFLAG))

                {

                    return  ;

                }

                Message msg = mHandler.obtainMessage()  ;

                msg.what = this.MEDIA_PLAY_WHAT_MESSAGEFLAG  ;

                msg.obj = mMeidaPlayMessageObj  ;

                mHandler.sendMessageDelayed(msg, 1000)  ;

 

代码慢慢看,思路也很简单 ,通过发消息的方式, 先检测如果有相关消息队列,就直接跳出函数,不做任何处理, 否则延迟一秒后再向队列发送一条消息  。

 

为何我用了1秒这个这么长的时间呢 ,因为这么长时间如果都处理不了,那就不是压力测试的问题了,而是方法本身的问题 了,这也是通过排除法来试图排除是因为点击屏幕过快产生的问题 。

编译,再试 , 很不辛,又不生效,不幸被我猜中了 。 仰望苍天 !

 

现在问题很明显了 :不是压力测试时候点击过快导致的ANR,而是某些方法本身有问题。

通过之前我们的日志

----- pid 2922 at 2011-01-13 13:51:07 -----
Cmd line: com.android.mms

DALVIK THREADS:
"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 s=N obj=0x4001d8d0 self=0xccc8
  | sysTid=2922 nice=0 sched=0/0 cgrp=default handle=-1345017808
  | schedstat=( 3497492306 15312897923 10358 )
  at android.media.MediaPlayer._release(Native Method)
  at android.media.MediaPlayer.release(MediaPlayer.java:1206)
  at android.widget.VideoView.stopPlayback(VideoView.java:196)
  at com.android.mms.ui.SlideView.stopVideo(SlideView.java:640)

很容易就知道了问题出在每次执行完了MediaPlayer.stop()方法调用之后会调用release()来释放播放器资源 。 而这个方法中又死在了_release()方法上 。 这是一个Native方法 。

因此,真相大白 ,问题是在Framework 层的MediaPlayer调用的Native方法_release()上 。

阅读更多
个人分类: android
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭