美文网首页
金三银四那些事(二)

金三银四那些事(二)

作者: 的一幕 | 来源:发表于2018-04-23 18:37 被阅读13次

    消息分发流程:

    android事件分发的图例

    总的来说分为:
    (dispatchTouchEvent)activity——>
    (dispatchTouchEvent) viewgrouop——>
    (onInterceptTouchEvent)viewgroup——>
    (dispatchTouchEvent) view——>
    (onTouchEvent)view——>
    (onTouchEvent) viewgroup——>
    (onTouchEvent) activity
    正常的走法是这样的,如果dispatchTouchEvent返回false或true就直接当做消费了,事件不会继续往下传递了,只有返回super的时候才会正常往下传,onInterceptTouchEvent是返回true是拦截,其他返回值是不拦截的,拦截后,事件不会传递到子view,onTouchEvent返回true才会进行消费,如果返回false或super那么会将事件交给上一层了。

    说说线程池怎么工作的:

    corePoolSize: 核心线程数,能够同时执行的任务数量;
    maximumPoolSize:除去缓冲队列中等待的任务,最大能容纳的任务数(其实是包括了核心线程池数量);
    keepAliveTime:超出workQueue的等待任务的存活时间,就是指maximumPoolSize里面的等待任务的存活时间;
    unit:时间单位;
    workQueue:阻塞等待线程的队列,一般使用new LinkedBlockingQueue()这个,如果不指定容量,会一直往里边添加,没有限制,workQueue永远不会满;
    threadFactory:创建线程的工厂,使用系统默认的类;
    handler:当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加;

    当线程数小于corePoolSize时,每添加一个任务,则立即开启线程执
    行;当corePoolSize满的时候,后面添加的任务将放入缓冲队列
    workQueue等待;当workQueue也满的时候,看是否超过
    maximumPoolSize线程数,如果超过,默认拒绝执行。 
    下面我们看个例子:假如corePoolSize=2,maximumPoolSize=3,
    workQueue容量为8;最开始,执行的任务A,B,此时corePoolSize已
    用完,再次执行任务C,则C将被放入缓冲队列workQueue中等待
    着,如果后来又添加了7个任务,此时workQueue已满,则后面再来
    的任务将会和maximumPoolSize比较,由于maximumPoolSize为3,
    所以只能容纳1个了,因为有2个在corePoolSize中运行了,所以后面
    来的任务默认都会被拒绝。
    

    corePoolSize=cpu核数+1
    maximumPoolSize=cpu核数*2+1
    这里会涉及到自定义线程池,以及会用到线程池下载的问题,大致思路是,断点下载,咋们可以将一个文件,按照多少个线程去平均分配文件的大小,假如我们的线程个数是4个,文件大小是100M,那么此时分得的每个线程是25M,那么第一个线程的终点是25M,第二个线程的终点是50M,第三个线程的终点是75M,第四个线程的终点是100M,每一个线程的起点那又是多少呢。起点肯定是活的,因为起点咋们需要先从本地取一次,咋们每一个线程起点是接着上一次结束的位置

    关于更多线程池介绍点击

    说说android中应用的启动流程:

    当我们点击手机桌面上的图标的时候,App就由Launcher开始启动了。但是,你有没有思考过Launcher到底是一个什么东西?

    Launcher本质上也是一个应用程序,和我们的App一样,也是继承自Activity

    public final class Launcher extends Activity
            implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
                       View.OnTouchListener {
                       }
    

    Launcher实现了点击、长按等回调接口,来接收用户的输入。既然是普通的App,那么我们的开发经验在这里就仍然适用,比如,我们点击图标的时候,是怎么开启的应用呢?如果让你,你怎么做这个功能呢?捕捉图标点击事件,然后startActivity()发送对应的Intent请求呗!是的,Launcher也是这么做的,就是这么easy!
    这里直接看Launcher点击事件:

     /**
         * Launches the intent referred by the clicked shortcut
         */
        public void onClick(View v) {
    
              ...ignore some code...
    
             Object tag = v.getTag();
            if (tag instanceof ShortcutInfo) {
                // Open shortcut
                final Intent intent = ((ShortcutInfo) tag).intent;
                int[] pos = new int[2];
                v.getLocationOnScreen(pos);
                intent.setSourceBounds(new Rect(pos[0], pos[1],
                        pos[0] + v.getWidth(), pos[1] + v.getHeight()));
            //开始开启Activity咯~
                boolean success = startActivitySafely(v, intent, tag);
    
                if (success && v instanceof BubbleTextView) {
                    mWaitingForResume = (BubbleTextView) v;
                    mWaitingForResume.setStayPressed(true);
                }
            } else if (tag instanceof FolderInfo) {
                //如果点击的是图标文件夹,就打开文件夹
                if (v instanceof FolderIcon) {
                    FolderIcon fi = (FolderIcon) v;
                    handleFolderClick(fi);
                }
            } else if (v == mAllAppsButton) {
            ...ignore some code...
            }
        }
    

    从上面的代码我们可以看到,在桌面上点击快捷图标的时候,会调用

    startActivitySafely(v, intent, tag);
    

    那么从程序列表界面,点击图标的时候会发生什么呢?实际上,程序列表界面使用的是AppsCustomizePagedView对象,所以我在这个类里面找到了onClick(View v)。

    com.android.launcher2.AppsCustomizePagedView.java
    
    /**
     * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
     */
    public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
            View.OnClickListener, View.OnKeyListener, DragSource,
            PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
            LauncherTransitionable {
    
         @Override
        public void onClick(View v) {
    
             ...ignore some code...
    
            if (v instanceof PagedViewIcon) {
                mLauncher.updateWallpaperVisibility(true);
                mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
            } else if (v instanceof PagedViewWidget) {
                     ...ignore some code..
             }
         }      
       }
    

    可以看到,调用的是

    mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
    

    所以咱们现在又明白了一件事情:不管从哪里点击图标,调用的都是Launcher.startActivitySafely()。

    下面我们就可以一步步的来看一下Launcher.startActivitySafely()到底做了什么事情。

     boolean startActivitySafely(View v, Intent intent, Object tag) {
            boolean success = false;
            try {
                success = startActivity(v, intent, tag);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
                Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
            }
            return success;
        }
    

    调用了startActivity(v, intent, tag)

     boolean startActivity(View v, Intent intent, Object tag) {
    
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            try {
                boolean useLaunchAnimation = (v != null) &&
                        !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
    
                if (useLaunchAnimation) {
                    if (user == null || user.equals(android.os.Process.myUserHandle())) {
                        startActivity(intent, opts.toBundle());
                    } else {
                        launcherApps.startMainActivity(intent.getComponent(), user,
                                intent.getSourceBounds(),
                                opts.toBundle());
                    }
                } else {
                    if (user == null || user.equals(android.os.Process.myUserHandle())) {
                        startActivity(intent);
                    } else {
                        launcherApps.startMainActivity(intent.getComponent(), user,
                                intent.getSourceBounds(), null);
                    }
                }
                return true;
            } catch (SecurityException e) {
            ...
            }
            return false;
        }
    

    这里会调用Activity.startActivity(intent, opts.toBundle()),这个方法熟悉吗?这就是我们经常用到的Activity.startActivity(Intent)的重载函数。而且由于设置了

     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    

    所以这个Activity会添加到一个新的Task栈中,而且,startActivity()调用的其实是startActivityForResult()这个方法。

    @Override
        public void startActivity(Intent intent, @Nullable Bundle options) {
            if (options != null) {
                startActivityForResult(intent, -1, options);
            } else {
                // Note we want to go through this call for compatibility with
                // applications that may have overridden the method.
                startActivityForResult(intent, -1);
            }
        }
    

    所以我们现在明确了,Launcher中开启一个App,其实和我们在Activity中直接startActivity()基本一样,都是调用了Activity.startActivityForResult()。

    binder机制的理解:

    binder机制运行图
    每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的,Client端与Server端进程往往采用ioctl等方法跟内核空间的驱动进行交互。

    binder通道运行图:

    binder通道运行图.png

    Binder的C/S架构
    在Android开发中,我们大量使用到了系统Service,比如媒体播放、各种传感器以及WindowManagerService等等等等(太多了~)。那么Android是怎么管理这些服务,并且让用户跨进程调用这些服务呢?首先我们看看调用系统服务的过程。在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,然后通过这个引用向具体的服务端发送请求,服务端执行完成后就返回

    架构图

    Binder驱动实现原理

    服务端跨进程的类都要继承Binder类。我们所持有的Binder引用(即服务端的类引用)并不是实际真实的远程Binder对象,我们的引用在Binder驱动里还要做一次映射。也就是说,设备驱动根据我们的引用对象找到对应的远程进程。客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数,transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存,把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。

    Binder机制运用

    好了,现在对Binder机制已经理解了,我们再看看Android是怎么运用Binder的。再现前面代码:

    //获取WindowManager服务引用
    WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
    //布局参数layoutParams相关设置略...
    View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
    //添加view
    wm.addView(view, layoutParams);
    

    这段代码前面已经出现过。getSystemService(getApplication().WINDOW_SERVICE);函数内部原理就是向ServiceManager查询标识符为getApplication().WINDOW_SERVICE的远程对象的引用。即WindowManager对象的引用,这个引用的真正实现是WindowManager的某个代理。得到这个引用后,在调用addView时,真正的实现是在代理里面,代理把参数打包到Parcel对象中,然后调用transact函数(该函数继承自Binder),再触发Binder驱动的一系列调用过程。

    相关文章

      网友评论

          本文标题:金三银四那些事(二)

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