美文网首页
安卓小知识2

安卓小知识2

作者: 晨曦诗雨 | 来源:发表于2018-12-07 11:29 被阅读0次

    一:SurfaceView

    普通的Android控件,例如TextView、Button和CheckBox等,它们都是将自己的UI绘制在宿主窗口的绘图表面之上,这意味着它们的UI是在应用程序的主线程中进行绘制的,由于应用程序的主线程除了要绘制UI之外,还需要及时地响应用户输入,否则的话,系统就会认为应用程序没有响应而出现ANR,对于一些游戏,摄像头预览、视频播放来说,它们的UI都比较复杂,而且要求能够进行高效的绘制,因此,它们的UI就不适合在应用程序的主线程中进行绘制,这时候就必须要给那些需要复杂而高效UI的视图生成一个独立的绘图表面,以及使用一个独立的线程来绘制这些视图的UI。
    在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面,由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行行绘制,又由于不占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应。

    SurfaceView的绘制方式效率非常高,因为SurfaceView的窗口刷新的时候不需要重绘应用程序的窗口(android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率非常低下)

    Thread.yield(): 与Thread.sleep(long millis):的区别:

    Thread.yield(): 是暂停当前正在执行的线程对象 ,并去执行其他线程。
    Thread.sleep(long millis):则是使当前线程暂停参数中所指定的毫秒数然后在继续执行线程。
    SurfaceView和View的区别

    总的归纳起来SurfaceView和View不同之处有:

    1. SurfaceView允许其他线程更新视图对象(执行绘制方法)而View不允许这么做,它只允许UI线程更新视图对象。

    2. SurfaceView是放在其他最底层的视图层次中,所有其他视图层都在它上面,所以在它之上可以添加一些层,而且它不能是透明的。

    3. 它执行动画的效率比View高,而且你可以控制帧数。

    4. SurfaceView在绘图时使用l了双缓冲机制,而View没有。

    二:何为句柄 (Too many open files 句柄泄露的问题)

    一个进程在初始化时,系统将为他分配一个句柄表(handle table),这个句柄表仅仅供内核对象使用,不适用于用户对象,用于创建内核对象的任何函数都会返回一个与进程相关的句柄,这个句柄可以由同一个进程中运行的所有线程使用,系统用索引来标识内核对象的信息保存在进程句柄表中的位置,要得到实际索引值,就需要得到句柄。句柄和索引之间的关系在大部分操作系统中是使句柄值右移两位。
    所以我们通常说句柄标识一个对象,这句话没有错误,说它是一个指针就有问题了,因为我们是通过使用某个对象的句柄从而得到该对象在句柄表中的索引,最终得到该对象在内存中的地址,即指针。
    查看当前句柄详情:adb shell lsof -p <pid>
    查看当前句柄总数:adbshell lsof -p <pid> |wc –l
    可查看相详情

    • 句柄是用来标识某一资源的,是用来获取资源地址的。
    • 句柄其实是一个索引

    一个应用程序在执行的时候并不是所有的代码都放进内存,即使放入了内存,也不可能在整个运行期间一直停留在内存,毕竟内存还是比较珍贵的资源,所以对于某一资源,在不同的时刻物理地址可能不一样,但是在内核对象表中,句柄其实是一个索引,根据该索引我们可以很快的找到此时资源的物理地址。而句柄比较稳定,所以window操作系统就引入了句柄的概念。

    三:Handler

    如果此时需要一个耗时的操作,例如: 联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。我遇到的一种的实际情况,就是地图数据的获取(直接放到主线程中),就会导致程序崩溃。
      我们需要把这些耗时的操作,放在一个子线程中。而主线程主要负责界面的更新。
    Handler就是解决子线程和主线程通信的问题;Handler是运行在主线程中的(UI线程中), 它与子线程是通过Message对象来传递数据的。
    Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) ,然后完成UI的更新的工作。
    Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) ,然后完成UI的更新的工作。

    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对象到队列中,等待更新。

    这里需要把整个过程理清楚:

    image

    在子线程中通过Handler向发送Message,Handler先把这些消息放到消息队列中。然后Looper通过DisopatchMessage分发消息,分发的目标就是Handler。然后Hander调用回调函数(如果设置callback的话),如果没有就调用子类的中的handleMessage来处理和更新UI

    Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列的。Looper不断地从MessageQueue中抽取Message执行。

    Android Looper和Handler

    • Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

    • Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

    • MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

    • Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

    Thread:线程,负责调度整个消息循环,即消息循环的执行场所。

    Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消 息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该 线程具有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。 如下例所示:

    class LooperThread extends Thread {
          public Handler mHandler;
    
          public void run() {
              Looper.prepare();
    
              mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                      // process incoming messages here
                  }
              };
    
              Looper.loop();
          }
      }
    

    这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。
    Runnable是一个接口,不是一个线程,一般线程会实现Runnable。所以如果使用匿名内部类实现Runnable是运行在UI主线程的,如果使用实现这个Runnable接口的线程类,则是运行在对应线程的。

    Runnable 并不一定是新开一个线程,比如下面的调用方法就是运行在UI主线程中的:

     Handler mHandler=new Handler(); 
     mHandler.post(new Runnable(){ 
        @Override public void run() 
        { // TODO Auto-generated method stub 
         } 
     });
    

    调用handler的post方法,把Runnable对象(一般是Runnable的子类)传过去;handler会在looper中调用这个Runnable的Run方法执行。

    其实主要过程就是:

    子线程通过Handler----->发送Msg或者Runnable到---->Message Queue消息队列;

    Looper从消息队列中取出消息--------->DispatchMessage然后分发消息——--->分发的目标就是Handler------>Handler调用回调函数或者Handler的子类的handleMessage来处理消息,并且更新UI

    Handler类有好几种构造函数,一种是带looper的,一种是带callback回调函数的。

    public Handler(Looper L){
    ……………………
    }
    public Handler(Looper L,Callback callback){
    …………………………
    }
    如果Handler对象使用的是带callback的Handler的构造函数,那么凡是发送到这个Handler的消息,都会被这个Callback函数处理。

    如果Handler对象使用的是不带callback的Handler的构造函数,那么发送到这个Handler的消息,会被类Handler中的handleMessage()这个函数处理。
    更多详情请看此网址

    相关文章

      网友评论

          本文标题:安卓小知识2

          本文链接:https://www.haomeiwen.com/subject/gnbzcqtx.html