`

android postInvalidateDelayed 方法

 
阅读更多

多初入 Android  Java 开发的新手对 Thread  Looper  Handler  Message 仍然比较迷惑,衍生的有HandlerThread  java.util.concurrent  Task  AsyncTask 由于目前市面上的书籍等资料都没有谈到这些问题,今天 Android123 就这一问题做更系统性的总结。

     Android 开发过程中为什么要多线程

我们创建的 Service  Activity 以及 Broadcast 均是一个主线程处理,这里我们可以理解为 UI 线程。但是在操作一些耗时操作时,比如 I/O 读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现 ANR 的响应提示窗口,这个时候我们可以考虑使用 Thread 线程来解决。

  Android 中使用 Thread 线程会遇到哪些问题

  对于从事过 J2ME 开发的程序员来说 Thread 比较简单,直接匿名创建重写 run 方法,调用 start 方法执行即可。或者从 Runnable 接口继承,但对于 Android 平台来说 UI 控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点 Google 在设计 Android 时倒是参考了下 Win32 的消息处理机制。

postInvalidate() 方法

对于线程中的刷新一个 View 为基类的界面,可以使用 postInvalidate() 方法在线程中来处理,其中还提供了一些重写方法比如 postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)  postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒,如下:

void

postInvalidate ()

void

postInvalidate (int left, int top, int right, int bottom)

void

postInvalidateDelayed (long delayMilliseconds)

void

postInvalidateDelayed (long delayMilliseconds, int left, int top, int right, int bottom)

Handler 

当然推荐的方法是通过一个 Handler 来处理这些,可以在一个线程的 run 方法中调用 handler 对象的postMessage  sendMessage 方法来实现, Android 程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32 程序员可以很好理解这些消息处理,不过相对于 Android 来说没有提供 PreTranslateMessage 这些干涉内部的方法。

消息的处理者, handler 负责将需要传递的信息封装成 Message ,通过调用 handler 对象的 obtainMessage()来实现。将消息传递给 Looper ,这是通过 handler 对象的 sendMessage() 来实现的。继而由 Looper  Message放入 MessageQueue 中。    Looper 对象看到 MessageQueue 中含有 Message ,就将其广播出去。该 handler对象收到该消息后,调用相应的 handler 对象的 handleMessage() 方法对其进行处理。  

Handler 主要接受子线程发送的数据 并用此数据配合主线程更新 UI.
      
 当应用程序启动时, Android 首先会开启一个主线程 ( 也就是 UI 线程 ) , 主线程为管理界面中的 UI 控件,进行事件分发 比如说 你要是点击一个 Button ,Android 会分发事件到 Button 上,来响应你的操作。    如果此时需要一个耗时的操作,例如 联网读取数据,   或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会出现假死现象 如果 5 秒钟还没有完成的话,,会收到 Android 系统的一个错误提示   " 强制关闭 ".   这个时候我们需要把这些耗时的操作,放在一个子线程中 , 因为子线程涉及到 UI 更新,,Android 主线程是线程不安全的,也就是说,更新 UI 只能在主线程中更新,子线程中操作是危险的 这个时候,Handler 就出现了 , 来解决这个复杂的问题  由于 Handler 运行在主线程中 (UI 线程中 ),   它与子线程可以通过Message 对象来传递数据 这个时候, Handler 就承担着接受子线程传过来的 ( 子线程用 sedMessage() 方法传弟)Message 对象, ( 里面包含数据 )  , 把这些消息放入主线程队列中,配合主线程进行更新 UI  
     Handler
 一些特点: handler 可以分发 Message 对象和 Runnable 对象到主线程中 每个 Handler 实例 , 都会绑定到创建他的线程中 ( 一般是位于主线程 ),
      
 它有两个作用 : (1):   安排消息或 Runnable 在某个主线程中某个地方执行 , (2) 安排一个动作在不同的线程中执行 
        Handler
 中分发消息的一些方法 
        post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)
      
 以上 post 类方法允许你排列一个 Runnable 对象到主线程队列中 ,
      sendMessage
 类方法 允许你安排一个带数据的 Message 对象到队列中,等待更新 .
Handler
 实例 
     //  子类需要继承 Hendler 类,并重写 handleMessage(Message msg) 方法 用于接受线程数据 
     // 
 以下为一个实例,它实现的 功能  通过线程修改界面 Button 的内容

 

Java代码  收藏代码
  1. public class MyHandlerActivity extends Activity {  
  2.   
  3.     Button button;  
  4.   
  5.     MyHandler myHandler;  
  6.   
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.   
  9.         super.onCreate(savedInstanceState);  
  10.   
  11.         setContentView(R.layout.handlertest);  
  12.   
  13.         button = (Button) findViewById(R.id.button);  
  14.   
  15.         myHandler = new MyHandler();  
  16.   
  17.         // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据  
  18.   
  19.         // Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象  
  20.   
  21.         // (2): 让一个动作,在不同的线程中执行.  
  22.   
  23.         // 它安排消息,用以下方法  
  24.   
  25.         // post(Runnable)  
  26.   
  27.         // postAtTime(Runnable,long)  
  28.   
  29.         // postDelayed(Runnable,long)  
  30.   
  31.         // sendEmptyMessage(int)  
  32.   
  33.         // sendMessage(Message);  
  34.   
  35.         // sendMessageAtTime(Message,long)  
  36.   
  37.         // sendMessageDelayed(Message,long)  
  38.   
  39.         // 以上方法以 post开头的允许你处理Runnable对象  
  40.   
  41.         //sendMessage()允许你处理Message对象(Message里可以包含数据,)  
  42.   
  43.         MyThread m = new MyThread();  
  44.   
  45.         new Thread(m).start();  
  46.   
  47.     }  
  48.   
  49.     /** 
  50.  
  51.      * 接受消息,处理消息 ,此Handler会与当前主线程一块运行 
  52.  
  53.      * */  
  54.   
  55.     class MyHandler extends Handler {  
  56.   
  57.         public MyHandler() {  
  58.   
  59.         }  
  60.   
  61.         public MyHandler(Looper L) {  
  62.   
  63.             super(L);  
  64.   
  65.         }  
  66.   
  67.         // 子类必须重写此方法,接受数据  
  68.   
  69.         @Override  
  70.   
  71.         public void handleMessage(Message msg) {  
  72.   
  73.             // TODO Auto-generated method stub  
  74.   
  75.             Log.d("MyHandler""handleMessage......");  
  76.   
  77.             super.handleMessage(msg);  
  78.   
  79.             // 此处可以更新UI  
  80.   
  81.             Bundle b = msg.getData();  
  82.   
  83.             String color = b.getString("color");  
  84.   
  85.             MyHandlerActivity.this.button.append(color);  
  86.   
  87.         }  
  88.   
  89.     }  
  90.   
  91.     class MyThread implements Runnable {  
  92.   
  93.         public void run() {  
  94.   
  95.             try {  
  96.   
  97.                 Thread.sleep(10000);  
  98.   
  99.             } catch (InterruptedException e) {  
  100.   
  101.                 // TODO Auto-generated catch block  
  102.   
  103.                 e.printStackTrace();  
  104.   
  105.             }  
  106.   
  107.             Log.d("thread.......""mThread........");  
  108.   
  109.             Message msg = new Message();  
  110.   
  111.             Bundle b = new Bundle();// 存放数据  
  112.   
  113.             b.putString("color""我的");  
  114.   
  115.             msg.setData(b);  
  116.   
  117.             MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI  
  118.   
  119.         }  
  120.   
  121.     }  
  122. }  
 

  Looper

其实 Android 中每一个 Thread 都跟着一个 Looper  Looper 可以帮助 Thread 维护一个消息队列,昨天的问题   Can't create handler inside thread 错误   一文中提到这一概念,但是 Looper  Handler 没有什么关系,我们从开源的代码可以看到 Android 还提供了一个 Thread 继承类 HanderThread 可以帮助我们处理,在 HandlerThread对象中可以通过 getLooper 方法获取一个 Looper 对象控制句柄,我们可以将其这个 Looper 对象映射到一个Handler 中去来实现一个线程同步机制, Looper 对象的执行需要初始化 Looper.prepare 方法就是昨天我们看到的问题,同时推出时还要释放资源,使用 Looper.release 方法。

Looper  MessageQueue 的管理者。每一个 MessageQueue 都不能脱离 Looper 而存在, Looper 对象的创建是通过 prepare 函数来实现的。同时每一个 Looper 对象和一个线程关联。通过调用 Looper.myLooper() 可以获得当前线程的 Looper 对象  
创建一个 Looper 对象时,会同时创建一个 MessageQueue 对象。除了主线程有默认的 Looper ,其他线程默认是没有 MessageQueue 对象的,所以,不能接受 Message 。如需要接受,自己定义 一个 Looper 对象 ( 通过prepare 函数 ), 这样该线程就有了自己的 Looper 对象和 MessageQueue 数据结构了。  
Looper
  MessageQueue 中取出 Message 然后,交由 Handler  handleMessage 进行处理。处理完成后,调用Message.recycle() 将其放入 Message Pool 中。  

Message

对于 Android  Handler 可以传递一些内容,通过 Bundle 对象可以封装 String  Integer 以及 Blob 二进制对象,我们通过在线程中使用 Handler 对象的     sendEmptyMessage  sendMessage 方法来传递一个 Bundle 对象到 Handler 处理器。对于 Handler 类提供了重写方法 handleMessage(Message msg)  来判断,通过 msg.what 来区分每条信息。将 Bundle 解包来实现 Handler 类更新 UI 线程中的内容实现控件的刷新操作。相关的 Handler 对象有关消息发送 sendXXXX 相关方法如下,同时还有 postXXXX 相关方法,这些和 Win32 中的道理基本一致,一个为发送后直接返回,一个为处理后才返回。

Message :消息对象, Message Queue 中的存放的对象。一个 Message Queue 中包含多个 Message  Message 实例对象的取得,通常使用 Message 类里的静态方法 obtain(), 该方法有多个重载版本可供选择;它的创建并不一定是直接创建一个新的实例,而是先从 Message Pool( 消息池 ) 中看有没有可用的 Message 实例,存在则直接取出返回这个实例。如果 Message Pool 中没有可用的 Message 实例,则才用给定的参数创建一个 Message对象。调用 removeMessages() 时,将 Message  Message Queue 中删除,同时放入到 Message Pool 中。除了上面这种方式,也可以通过 Handler 对象的 obtainMessage() 获取 一个 Message 实例。  

final boolean

sendEmptyMessage (int what)

final boolean

sendEmptyMessageAtTime (int what, long uptimeMillis)

final boolean

sendEmptyMessageDelayed (int what, long delayMillis)

final boolean

sendMessage ( Message  msg)

final boolean

sendMessageAtFrontOfQueue ( Message  msg)

boolean

sendMessageAtTime ( Message  msg, long uptimeMillis)

final boolean

sendMessageDelayed ( Message  msg, long delayMillis)

MessageQueue

是一种 数据 结构,见名知义,就是一个消息队列,存放消息的地方。每一个线程最多只可以拥有一个MessageQueue 数据结构。  
    
创建一个线程的时候,并不会自动 创建其 MessageQueue 。通常使用一个 Looper 对象对该线程的MessageQueue 进行管理。主线程创建时,会创建一个默认的 Looper 对象,而 Looper 对象的创建,将自动创建一个 Message Queue 。其他非主线程,不会自动创建 Looper ,要需要的时候,通过调用 prepare 函数来实现。  
   
  java.util.concurrent 对象分析

对于过去从事 Java 开发的程序员不会对 Concurrent 对象感到陌生吧,他是 JDK 1.5 以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到 Android 为我们已经设计好的 Task 机制,我们这里 Android 开发网对其不做过多的赘述。

Task 以及 AsyncTask

 Android 中还提供了一种有别于线程的处理方式,就是 Task 以及 AsyncTask ,从开源代码中可以看到是针对 Concurrent 的封装,开发人员可以方便的处理这些异步任务。   当然涉及到同步机制的方法和技巧还有很多,考虑时间和篇幅问题不再做过多的描述。

分享到:
评论

相关推荐

    android三种自定义Loading加载框

    然后,通过重写`onMeasure()`来设置合适的大小,并在`postInvalidateDelayed()`或`postInvalidateOnAnimation()`中实现动画逻辑。 ```java public class CustomLoadingView extends View { private int current...

    Android星体动效实现

    - 使用`postInvalidateDelayed()`来延迟绘制,避免过度绘制,节省CPU资源。 - 如果星星数量巨大,还可以考虑使用OpenGL ES通过`SurfaceView`或`GLSurfaceView`来提高渲染效率。 5. **交互性** - 让用户能够与...

    android欢迎界面简单实现

    3. **实现动画效果**:在`onDraw()`方法中,利用`postInvalidateDelayed()`或`postDelayed()`来定时重绘视图,实现动画帧的连续播放。例如,可以创建一系列的位图,然后按照一定顺序和间隔显示出来,形成动画效果。 ...

    android水波纹效果

    要实现动态效果,我们可以使用`postInvalidateDelayed()`方法来定期重新绘制View。通过调整延迟时间,可以控制水波纹扩散的速度。另外,还可以使用`ValueAnimator`或`ObjectAnimator`来创建更复杂的动画效果,比如...

    android 立方体旋转效果

    可以使用`postInvalidateDelayed()`来控制绘制频率,并确保只在必要时重绘。 7. **注意事项**: 在处理3D效果时,理解坐标系统和投影变换至关重要。Android使用右手坐标系统,X轴向右,Y轴向上,Z轴指向屏幕外。此外...

    android 动画 碰撞矩形

    同时,利用`postInvalidateDelayed()`或`ValueAnimator`设置动画的时间周期和变化,以控制矩形的移动速度和反弹行为。 在实际开发中,可以创建一个名为`MoveInScreen`的Java类,它继承自`View`,并在其中实现上述...

    android 天女散花

    - 创建自定义View类,继承自Android的`View`或`ViewGroup`,并在其中实现绘图逻辑,覆盖`onDraw()`方法,每次重绘时调用`invalidate()`方法触发重绘。 4. **动画原理**: - Android提供了多种动画机制,如属性...

    Android自定义一个属于自己的时间钟表

    例如,可以使用`postInvalidateDelayed()`方法来定时更新钟表,而不是在每个`onDraw()`调用后都重新计算角度。此外,利用硬件加速(`setLayerType(View.LAYER_TYPE_HARDWARE, null)`)和避免使用抗锯齿(除非必要)...

    Android Animation相关

    在这个主题中,我们将深入探讨Android补间动画的原理、使用方法以及如何将其应用到自定义View中。 首先,补间动画主要包括旋转( Rotate)、缩放(Scale)、平移(Translate)和透明度变化(Alpha)四种基本类型。...

    Android 自定义View(五)实现跑马灯垂直滚动效果

    在这个类中,我们需要重写`onDraw()`方法,这是Android系统绘制View的主要入口。在这里,我们将绘制跑马灯内容,即文字或图片。为了实现垂直滚动,我们还需要计算文本的起始位置,随着每次绘制,这个位置会逐渐改变...

    android 雷达扫描两种实现方式

    4. **动画处理**:为了实现扫描效果,我们需要使用`postInvalidateDelayed()`或`ObjectAnimator`来定期重新绘制View,这将使扫描线不断移动,从而产生雷达扫描的感觉。 5. **参数控制**:添加公共方法来控制扫描...

    android 入门学习框架 贪吃蛇小游戏

    - 为了实现平滑的动画效果,可以使用`postInvalidateDelayed`来设置重绘间隔。 6. **游戏状态管理** - 设计游戏状态机,包括开始、运行、暂停和结束状态。根据状态处理用户输入和游戏逻辑。 - 当蛇碰到边界或...

    Android-Android下雪效果

    可以使用`postInvalidateDelayed()`方法来设置重绘间隔,调整动画的帧率。 6. 循环动画:雪花飘落的过程应该是一个无限循环的动画,因此我们需要在动画结束时重新开始,这可以通过重写`onAnimationEnd()`方法来实现...

    android 仪表盘效果实现

    确保只在必要时调用`invalidate()`,并考虑使用`postInvalidateDelayed()`来延迟重绘。 10. **代码结构和可维护性**: 为了使代码易于理解和维护,可以将各个部分(如指针、弧形进度、动画)封装成单独的方法,保持...

    android自定义时钟控件

    - 为了避免频繁的`onDraw`调用导致性能问题,可以使用`postInvalidateDelayed()`来延迟重绘,只在需要更新指针位置时执行。 - 考虑使用`硬件加速`,通过在AndroidManifest.xml中设置`android:hardwareAccelerated=...

    android自定义圆形进度

    自定义View通常涉及继承Android的基础View或 ViewGroup类,然后重写其onDraw()方法以实现自定义的绘制逻辑。在这个案例中,我们可能会继承`ProgressBar`或者`View`,并实现圆形的绘制。 2. **属性定制** 自定义...

    Android 扩展ImageView来播放gif动画

    postInvalidateDelayed(decoder.getNextDelay()); } } ``` 步骤4:应用`PowerImageView` 现在,你可以在布局文件中使用`PowerImageView`,并传入GIF数据源来播放动画。例如,如果你的GIF文件在assets目录下,你...

    在android上实现模仿windows桌面的鼠标残影效果

    可以使用ValueAnimator的setDuration()方法设置动画持续时间,以及postInvalidateDelayed()来延迟重绘,确保在合适的时机更新画面。 五、优化与性能 由于频繁的绘制会消耗大量资源,所以需要进行优化。一种可能的...

    Android自定义View-动态的文字闪动效果demo

    可以使用`postInvalidateDelayed()`替代`invalidate()`来延迟重绘,减少不必要的计算。 7. **布局使用**:将自定义的闪动文字View添加到XML布局文件中,指定其宽高和位置。通过属性绑定或代码设置动态效果的参数,...

    android textview特效

    同时,我们可以结合`postInvalidateDelayed()`来定期重新绘制,以实现动画效果。 3. **使用动画库**: Android社区有许多优秀的第三方动画库,如Lottie、NineOldAndroids等,它们提供了丰富的动画效果。虽然这些库...

Global site tag (gtag.js) - Google Analytics