精华内容
下载资源
问答
  • 不同角度看机器学习的几种学习方式: 1、在线学习与离线学习 offline learning and online learning.  In offline learning, the whole training data must be available at the time of model ...

    从不同角度看机器学习的几种学习方式:


    1、在线学习与离线学习
    offline learning and online learning. 

    In offline learning, the whole training data must be available at the time of model training. Only when training is completed can the model be used for predicting. 

    In contrast, online algorithms process data sequentially. They produce a model and put it in operation without having the complete training dataset available at the beginning. The model is continuously updated during operation as more training data arrives.





    2、增量学习与减量学习
    online learning包括了incremental learning和decremental learning

    incremental learning增量学习,是指一个学习系统能不断地从新样本中学习新的知识,并能保存大部分以前已经学习到的知识。增量学习非常类似于人类自身的学习模式。因为人在成长过程中,每天学习和接收新的事物,学习是逐步进行的,而且,对已经学习到的知识,人类一般是不会遗忘的。Less restrictive than online algorithms are incremental algorithms that process input examples one by one (or batch by batch) and update the decision model after receiving each example. Incremental algorithms may have random access to previous examples or representative/selected examples. In such a case, these algorithms are called in- cremental algorithms with partial memory. Typically, in incremental algorithms, for any new presentation of data, the update operation of the model is based on the previous one. Streaming algorithms are online algorithms for processing high-speed continuous flows of data. In streaming, examples are processed sequentially as well and can be examined in only a few passes (typically just one). These algorithms use limited memory and limited processing time per item.
    一个增量学习算法应同时具有以下特点:
        1)可以从新数据中学习新知识;  
        2)以前已经处理过的数据不需要重复处理;
        3)每次只有一个训练观测样本被看到和学习;
        4)学习新知识的同时能保存以前学习到的大部分知识;
        5)—旦学习完成后训练观测样本被丢弃;
        6)学习系统没有关于整个训练样本的先验知识;
    增量式算法:就是每当新增数据时,并不需要重建所有的知识库,而是在原有知识库的基础上,仅做由于新增数据所引起的更新,这更加符合人的思维原理。


    decremental learning递减学习,即抛弃“价值最低”的保留的训练样本。这两个概念在incremental and decremental svm这篇论文里面可以看到具体的操作过程。





    3、主动学习与直推学习
    主动学习(active learning),指的是这样一种学习方法:
    有的时候,有类标的数据比较稀少而没有类标的数据是相当丰富的,但是对数据进行人工标注又非常昂贵,这时候,学习算法可以主动地提出一些标注请求,将一些经过筛选的数据提交给专家进行标注。
    这个筛选过程也就是主动学习主要研究的地方了,怎么样筛选数据才能使得请求标注的次数尽量少而最终的结果又尽量好。
    主动学习的过程大致是这样的,有一个已经标好类标的数据集K(初始时可能为空),和还没有标记的数据集U,通过K集合的信息,找出一个U的子集C,提出标注请求,待专家将数据集C标注完成后加入到K集合中,进行下一次迭代。
    按wiki上所描述的看,主动学习也属于半监督学习的范畴了,但实际上是不一样的,半监督学习和直推学习(transductive learning)以及主动学习,都属于利用未标记数据的学习技术,但基本思想还是有区别的。如上所述,主动学习的“主动”,指的是主动提出标注请求,也就是说,还是需要一个外在的能够对其请求进行标注的实体(通常就是相关领域人员),即主动学习是交互进行的。
    而半监督学习,特指的是学习算法不需要人工的干预,基于自身对未标记数据加以利用。


    直推学习,它与半监督学习一样不需要人工干预,不同的是,直推学习假设未标记的数据就是最终要用来测试的数据,学习的目的就是在这些数据上取得最佳泛化能力。相对应的,半监督学习在学习时并不知道最终的测试用例是什么。
    也就是说,直推学习其实类似于半监督学习的一个子问题,或者说是一个特殊化的半监督学习,所以也有人将其归为半监督学习。




    4、迁移学习
    Less restrictive than online algorithms are incremental algorithms that process input examples one by one (or batch by batch) and update the decision model after receiving each example. Incremental algorithms may have random access to previous examples or representative/selected examples. In such a case, these algorithms are called in- cremental algorithms with partial memory. Typically, in incremental algorithms, for any new presentation of data, the update operation of the model is based on the previous one. Streaming algorithms are online algorithms for processing high-speed continuous flows of data. In streaming, examples are processed sequentially as well and can be examined in only a few passes (typically just one). These algorithms use limited memory and limited processing time per item.
    在传统的机器学习的框架下,学习的任务就是在给定充分训练数据的基础上来学习一个分类模型;然后利用这个学习到的模型来对测试文档进行分类与预测。然而,我们看到机器学习算法在当前的Web挖掘研究中存在着一个关键的问题:一些新出现的领域中的大量训练数据非常难得到。我们看到Web应用领域的发展非常快速。大量新的领域不断涌现,从传统的新闻,到网页,到图片,再到博客、播客等等。传统的机器学习需要对每个领域都标定大量训练数据,这将会耗费大量的人力与物力。而没有大量的标注数据,会使得很多与学习相关研究与应用无法开展。其次,传统的机器学习假设训练数据与测试数据服从相同的数据分布。然而,在许多情况下,这种同分布假设并不满足。通常可能发生的情况如训练数据过期。这往往需要我们去重新标注大量的训练数据以满足我们训练的需要,但标注新数据是非常昂贵的,需要大量的人力与物力。从另外一个角度上看,如果我们有了大量的、在不同分布下的训练数据,完全丢弃这些数据也是非常浪费的。如何合理的利用这些数据就是迁移学习主要解决的问题。迁移学习可以从现有的数据中迁移知识,用来帮助将来的学习。迁移学习(Transfer Learning)的目标就是将从一个环境中学到的知识用来帮助新环境中的学习任务。因此,迁移学习不会像传统机器学习那样作同分布假设。举一个通俗的例子,一个会下象棋的人可以更容易的学会下围棋;一个认识桌子的人可以更加容易的认识椅子;
    在迁移学习方面的工作目前可以分为以下三个部分:同构空间下基于实例的迁移学习,同构空间下基于特征的迁移学习与异构空间下的迁移学习。基于实例的迁移学习有更强的知识迁移能力,基于特征的迁移学习具有更广泛的知识迁移能力,而异构空间的迁移具有广泛的学习与扩展能力。
    迁移学习即一种学习对另一种学习的影响,它广泛地存在于知识、技能、态度和行为规范的学习中。任何一种学习都要受到学习者已有知识经验、技能、态度等的影响,只要有学习,就有迁移。迁移是学习的继续和巩固,又是提高和深化学习的条件,学习与迁移不可分割。





    refs:
    http://m.blog.csdn.net/article/details?id=51942135



    展开全文
  • 目前虽然网上相关的文章也不少,但我觉得没有哪篇写得特别详细的(也许我还没有找到),多数文章只是讲了讲理论,然后配合demo运行了一下结果。而我准备带着大家从源码的角度进行分析,相信大家可以更加深刻地理解...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9097463

     

    其实我一直准备写一篇关于Android事件分发机制的文章,从我的第一篇博客开始,就零零散散在好多地方使用到了Android事件分发的知识。也有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图片使用Button而不用ImageView?等等……对于这些问题,我并没有给出非常详细的回答,因为我知道如果想要彻底搞明白这些问题,掌握Android事件分发机制是必不可少的,而Android事件分发机制绝对不是三言两语就能说得清的。

     

    在我经过较长时间的筹备之后,终于决定开始写这样一篇文章了。目前虽然网上相关的文章也不少,但我觉得没有哪篇写得特别详细的(也许我还没有找到),多数文章只是讲了讲理论,然后配合demo运行了一下结果。而我准备带着大家从源码的角度进行分析,相信大家可以更加深刻地理解Android事件分发机制。

     

    阅读源码讲究由浅入深,循序渐进,因此我们也从简单的开始,本篇先带大家探究View的事件分发,下篇再去探究难度更高的ViewGroup的事件分发。

     

    那我们现在就开始吧!比如说你当前有一个非常简单的项目,只有一个Activity,并且Activity中只有一个按钮。你可能已经知道,如果想要给这个按钮注册一个点击事件,只需要调用:

    button.setOnClickListener(new OnClickListener() {
    	@Override
    	public void onClick(View v) {
    		Log.d("TAG", "onClick execute");
    	}
    });

    这样在onClick方法里面写实现,就可以在按钮被点击的时候执行。你可能也已经知道,如果想给这个按钮再添加一个touch事件,只需要调用:

    button.setOnTouchListener(new OnTouchListener() {
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		Log.d("TAG", "onTouch execute, action " + event.getAction());
    		return false;
    	}
    });

    onTouch方法里能做的事情比onClick要多一些,比如判断手指按下、抬起、移动等事件。那么如果我两个事件都注册了,哪一个会先执行呢?我们来试一下就知道了,运行程序点击按钮,打印结果如下:

     

     

    可以看到,onTouch是优先于onClick执行的,并且onTouch执行了两次,一次是ACTION_DOWN,一次是ACTION_UP(你还可能会有多次ACTION_MOVE的执行,如果你手抖了一下)。因此事件传递的顺序是先经过onTouch,再传递到onClick。

     

    细心的朋友应该可以注意到,onTouch方法是有返回值的,这里我们返回的是false,如果我们尝试把onTouch方法里的返回值改成true,再运行一次,结果如下:

     

     

    我们发现,onClick方法不再执行了!为什么会这样呢?你可以先理解成onTouch方法返回true就认为这个事件被onTouch消费掉了,因而不会再继续向下传递。

     

    如果到现在为止,以上的所有知识点你都是清楚的,那么说明你对Android事件传递的基本用法应该是掌握了。不过别满足于现状,让我们从源码的角度分析一下,出现上述现象的原理是什么。

     

    首先你需要知道一点,只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类里的dispatchTouchEvent方法,可是你会发现Button类里并没有这个方法,那么就到它的父类TextView里去找一找,你会发现TextView里也没有这个方法,那没办法了,只好继续在TextView的父类View里找一找,这个时候你终于在View里找到了这个方法,示意图如下:

     

     

    然后我们来看一下View中dispatchTouchEvent方法的源码:

    public boolean dispatchTouchEvent(MotionEvent event) {
        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                mOnTouchListener.onTouch(this, event)) {
            return true;
        }
        return onTouchEvent(event);
    }

    这个方法非常的简洁,只有短短几行代码!我们可以看到,在这个方法内,首先是进行了一个判断,如果mOnTouchListener != null,(mViewFlags & ENABLED_MASK) == ENABLED和mOnTouchListener.onTouch(this, event)这三个条件都为真,就返回true,否则就去执行onTouchEvent(event)方法并返回。

     

    先看一下第一个条件,mOnTouchListener这个变量是在哪里赋值的呢?我们寻找之后在View里发现了如下方法:

    public void setOnTouchListener(OnTouchListener l) {
        mOnTouchListener = l;
    }

    Bingo!找到了,mOnTouchListener正是在setOnTouchListener方法里赋值的,也就是说只要我们给控件注册了touch事件,mOnTouchListener就一定被赋值了。

     

    第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true。

     

    第三个条件就比较关键了,mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。

     

    现在我们可以结合前面的例子来分析一下了,首先在dispatchTouchEvent中最先执行的就是onTouch方法,因此onTouch肯定是要优先于onClick执行的,也是印证了刚刚的打印结果。而如果在onTouch方法里返回了true,就会让dispatchTouchEvent方法直接返回true,不会再继续往下执行。而打印结果也证实了如果onTouch返回true,onClick就不会再执行了。

     

    根据以上源码的分析,从原理上解释了我们前面例子的运行结果。而上面的分析还透漏出了一个重要的信息,那就是onClick的调用肯定是在onTouchEvent(event)方法中的!那我们马上来看下onTouchEvent的源码,如下所示:

    public boolean onTouchEvent(MotionEvent event) {
        final int viewFlags = mViewFlags;
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
        }
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
                    if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }
                        if (!mHasPerformedLongPress) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();
                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }
                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }
                        if (prepressed) {
                            mPrivateFlags |= PRESSED;
                            refreshDrawableState();
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }
                        removeTapCallback();
                    }
                    break;
                case MotionEvent.ACTION_DOWN:
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    mPrivateFlags |= PREPRESSED;
                    mHasPerformedLongPress = false;
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    break;
                case MotionEvent.ACTION_CANCEL:
                    mPrivateFlags &= ~PRESSED;
                    refreshDrawableState();
                    removeTapCallback();
                    break;
                case MotionEvent.ACTION_MOVE:
                    final int x = (int) event.getX();
                    final int y = (int) event.getY();
                    // Be lenient about moving outside of buttons
                    int slop = mTouchSlop;
                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||
                            (y < 0 - slop) || (y >= getHeight() + slop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();
                            // Need to switch from pressed to not pressed
                            mPrivateFlags &= ~PRESSED;
                            refreshDrawableState();
                        }
                    }
                    break;
            }
            return true;
        }
        return false;
    }
    

    相较于刚才的dispatchTouchEvent方法,onTouchEvent方法复杂了很多,不过没关系,我们只挑重点看就可以了。

     

    首先在第14行我们可以看出,如果该控件是可以点击的就会进入到第16行的switch判断中去,而如果当前的事件是抬起手指,则会进入到MotionEvent.ACTION_UP这个case当中。在经过种种判断之后,会执行到第38行的performClick()方法,那我们进入到这个方法里瞧一瞧:

    public boolean performClick() {
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        if (mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            mOnClickListener.onClick(this);
            return true;
        }
        return false;
    }

    可以看到,只要mOnClickListener不是null,就会去调用它的onClick方法,那mOnClickListener又是在哪里赋值的呢?经过寻找后找到如下方法:

    public void setOnClickListener(OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        mOnClickListener = l;
    }

    一切都是那么清楚了!当我们通过调用setOnClickListener方法来给控件注册一个点击事件时,就会给mOnClickListener赋值。然后每当控件被点击时,都会在performClick()方法里回调被点击控件的onClick方法。

     

    这样View的整个事件分发的流程就让我们搞清楚了!不过别高兴的太早,现在还没结束,还有一个很重要的知识点需要说明,就是touch事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。

     

    说到这里,很多的朋友肯定要有巨大的疑问了。这不是在自相矛盾吗?前面的例子中,明明在onTouch事件里面返回了false,ACTION_DOWN和ACTION_UP不是都得到执行了吗?其实你只是被假象所迷惑了,让我们仔细分析一下,在前面的例子当中,我们到底返回的是什么。

    参考着我们前面分析的源码,首先在onTouch事件里返回了false,就一定会进入到onTouchEvent方法中,然后我们来看一下onTouchEvent方法的细节。由于我们点击了按钮,就会进入到第14行这个if判断的内部,然后你会发现,不管当前的action是什么,最终都一定会走到第89行,返回一个true。

     

    是不是有一种被欺骗的感觉?明明在onTouch事件里返回了false,系统还是在onTouchEvent方法中帮你返回了true。就因为这个原因,才使得前面的例子中ACTION_UP可以得到执行。

     

    那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。如下所示:

    imageView.setOnTouchListener(new OnTouchListener() {
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		Log.d("TAG", "onTouch execute, action " + event.getAction());
    		return false;
    	}
    });

    运行一下程序,点击ImageView,你会发现结果如下:

     

     

    在ACTION_DOWN执行完后,后面的一系列action都不会得到执行了。这又是为什么呢?因为ImageView和按钮不同,它是默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。

     

    好了,关于View的事件分发,我想讲的东西全都在这里了。现在我们再来回顾一下开篇时提到的那三个问题,相信每个人都会有更深一层的理解。

     

    1. onTouch和onTouchEvent有什么区别,又该如何使用?

    从源码中可以看出,这两个方法都是在View的dispatchTouchEvent中调用的,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。

     

    另外需要注意的是,onTouch能够得到执行需要两个前提条件,第一mOnTouchListener的值不能为空,第二当前点击的控件必须是enable的。因此如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实现。

     

    2. 为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?

    如果你阅读了Android滑动框架完全解析,教你如何一分钟实现滑动菜单特效这篇文章,你应该会知道滑动菜单的功能是通过给ListView注册了一个touch事件来实现的。如果你在onTouch方法里处理完了滑动逻辑后返回true,那么ListView本身的滚动事件就被屏蔽了,自然也就无法滑动(原理同前面例子中按钮不能点击),因此解决办法就是在onTouch方法里返回false。

     

    3. 为什么图片轮播器里的图片使用Button而不用ImageView?

    提这个问题的朋友是看过了Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来 这篇文章。当时我在图片轮播器里使用Button,主要就是因为Button是可点击的,而ImageView是不可点击的。如果想要使用ImageView,可以有两种改法。第一,在ImageView的onTouch方法里返回true,这样可以保证ACTION_DOWN之后的其它action都能得到执行,才能实现图片滚动的效果。第二,在布局文件里面给ImageView增加一个android:clickable="true"的属性,这样ImageView变成可点击的之后,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。

     

    今天的讲解就到这里了,相信大家现在对Android事件分发机制又有了进一步的认识,在后面的文章中我会再带大家一起探究Android中ViewGroup的事件分发机制,感兴趣的朋友请继续阅读 Android事件分发机制完全解析,带你从源码的角度彻底理解(下) 。

     

    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • Android不同手机屏幕分辨率自适应

    万次阅读 2013-11-25 00:33:55
    不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。 px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较
    有必要了解的 Android中常见的单位 dip, dp, px, sp之间的区别:

    dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。
    px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。
    pt: point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用;
    sp: scaled pixels(放大像素). 主要用于字体显示best for textsize,根据 google 的建议,TextView 的字号最好使用 sp 做单位,

    Android支持下列所有单位:
    px(像素):屏幕上的点。
    in(英寸):长度单位。
    mm(毫米):长度单位。
    pt(磅):1/72英寸。
    dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。
    dip:与dp相同,多用于Android/ophone示例中。
    sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。

    =================================================================================
    这些术语都是指屏幕的分辨率。
    VGA:Video Graphics Array,即:显示绘图矩阵,相当于640×480 像素;
    HVGA:Half-size VGA;即:VGA的一半,分辨率为480×320;
    QVGA:Quarter VGA;即:VGA的四分之一,分辨率为320×240;
    WVGA:Wide Video Graphics Array;即:扩大的VGA,分辨率为800×480像素;

    WQVGA:Wide Quarter VGA;即:扩大的QVGA,分辨率比QVGA高,比VGA低,一般是:400×240,480×272;

    在设计之初,Android系统就被设计为一个可以在多种不同分辨率的设备上运行的操作系统。对于应用程序来说,系统平台向它们提供的是一个稳定的,跨平台的运行环境,而关于如何将程序以正确的方式显示到它所运行的平台上所需要的大部分技术细节,都由系统本身进行了处理,无需程序的干预。当然,系统本身也为程序提供了一系列API,所以在目标平台的分辨率是可以完全确定的情况下,程序也可以精确的控制自身在目标平台上的界面显示方式。

      这个文档会说明系统平台究竟提供了哪些分辨率支持特性,与它们如何在程序中使用的信息。如果你遵循文档中列出的方法,就很容易让你的程序在所有支持的分辨率下都能完美显示。这样你就可以用一个单独的.apk文件,将你的程序发布到所有的平台上。

      如果你已经发布过针对Android 1.5或更早版本平台的程序,你应该仔细阅读这篇文档,然后考虑一下到底如何让自己的老程序可以在拥有各种不同分辨率,并且运行着Android 1.6或更新平台上正常显示。在绝大部分情况下,只需要对程序作出小小的修改就可以达到目的,但你仍然需要尽可能地在各种分辨率的平台上进行测试。
      特别的,如果你有一个已经完成的程序,又想让它可以在超低分辨率的设备(比如320×240)上正确运行,你需要阅读“老程序的更新策略”,那篇文档会告诉你应该怎么做。

    术语和概念
    屏幕尺寸
    屏幕的物理尺寸,以屏幕的对角线长度作为依据(比如2.8寸,3.5寸)。
    简而言之,Android把所有的屏幕尺寸简化为三大类:大,正常,和小。
    程序可以针对这三种尺寸的屏幕提供三种不同的布局方案,然后系统会负责把你的布局方案以合适的方式渲染到对应的屏幕上,这个过程是不需要程序员用代码来干预的。

    屏幕长宽比
    屏幕的物理长度与物理宽度的比例。程序可以为制定长宽比的屏幕提供制定的素材,只需要用系统提供的资源分类符long和notlong。

    分辨率
    屏幕上拥有的像素的总数。注意,虽然大部分情况下分辨率都被表示为“宽度×长度”,但分辨率并不意味着屏幕长宽比。在Android系统中,程序一般并不直接处理分辨率。

    密度
    以屏幕分辨率为基础,沿屏幕长宽方向排列的像素。
    密度较低的屏幕,在长和宽方向都只有比较少的像素,而高密度的屏幕通常则会有很多——甚至会非常非常多——像素排列在同一区域。屏幕的密度是非常重要的,举个例子,长宽以像素为单位定义的界面元素(比如一个按钮),在低密度的屏幕上会显得很大,但在高密度的屏幕上则会显得很小。

    密度无关的像素(DIP)
    指一个抽象意义上的像素,程序用它来定义界面元素。它作为一个与实际密度无关的单位,帮助程序员构建一个布局方案(界面元素的宽度,高度,位置)。
    一个与密度无关的像素,在逻辑尺寸上,与一个位于像素密度为160DPI的屏幕上的像素是一致的,这也是Android平台所假定的默认显示设备。在运行的时候,平台会以目标屏幕的密度作为基准,“透明地”处理所有需要的DIP缩放操作。要把密度无关像素转换为屏幕像素,可以用这样一个简单的公式:pixels = dips * (density / 160)。举个例子,在DPI为240的屏幕上,1个DIP等于1.5个物理像素。我们强烈推荐你用DIP来定义你程序的界面布局,因为这样可以保证你的UI在各种分辨率的屏幕上都可以正常显示。


    支持的屏幕分辨率范围
    1.5及更早版本的Android系统,在设计的时候假定系统只会运行在一种分辨率的设备上——HVGA(320×480)分辨率,尺寸为3.2寸。由于系统只能工作在一种屏幕上,开发人员就可以针对那个屏幕来编写自己的程序,而无需去考虑程序在其他屏幕上的显示问题。
    但自从Android 1.6以来,系统引入了对多种尺寸、多种分辨率屏幕的支持,以此满足拥有各种配置的新平台的运行需求。这就意味着开发人员在针对Android 1.6或更新版系统开发程序的时候,需要为自己的程序在多种分辨率的屏幕上良好显示作出额外的设计。
    为了简化程序员面在对各种分辨率时的困扰,也为了具备各种分辨率的平台都可以直接运行这些程序,Android平台将所有的屏幕以密度和分辨率为分类方式,各自分成了三类:
    ·三种主要的尺寸:大,正常,小;
    ·三种不同的密度:高(hdpi),中(mdpi)和低(ldpi)。
    如果需要的话,程序可以为各种尺寸的屏幕提供不同的资源(主要是布局),也可以为各种密度的屏幕提供不同的资源(主要是位图)。除此以外,程序不需要针对屏幕的尺寸或者密度作出任何额外的处理。在执行的时候,平台会根据屏幕本身的尺寸与密度特性,自动载入对应的资源,并把它们从逻辑像素(DIP,用于定义界面布局)转换成屏幕上的物理像素。
    下表列出了Android平台支持的屏幕中一些比较常用的型号,并显示了系统是如何把它们分类到不同的屏幕配置里的。有些屏幕分辨率并不在下面的列表上,但系统仍会把它们归入下列的某一个类型中。

      低密度(120),ldpi 中密度(160),mdpi 高密度(240),hdpi
    屏幕 ·QVGA(240×320),2.6~3.0寸    
    普通屏幕 ·WQVGA(240×400),3.2~3.5寸
    ·FWQVGA(240×432),3.5~3.8寸
    ·HVGA(320×480),3.0~3.5寸 ·WVGA(480×800),3.3~4.0寸
    ·FWVGA(480×854),3.5~4.0寸
    屏幕   ·WVGA(480×800),4.8~5.5寸
    ·FWVGA(480×854),5.0~5.8寸

     


     

    首先,一块屏幕有几个参数,屏幕的物理尺寸,分辨率,像素密度(Density, DPI)。

    其中

    • 物理尺寸,就是所说的几寸的屏幕,代表屏幕对角线的长度,比如3.5寸、3.7寸、4寸、7寸等。
    • 分辨率,是屏幕总共能显示的像素数,通常我们都说几百×几百,比如240*320,320*480,480*800等,我们一般直接说乘后的结果。
    • 像素密度(DPI),DPI的全称是dots per inch,每英寸点数,还有个词是PPI,是每英寸像素数,其实PPI感觉更准确一些,这两个比较纠结,很多时候混用,这里就不明确区分了。(本文的意思都是“每英寸像素数”)

    这三个参数,任两个确定后,第三个量就是确定了。公式为:分辨率(总像素数)= 物理尺寸 × 像素密度。

    • 比如一个3寸的屏幕,分辨率为240×320,那么密度为 开方(480x800/3.5) 约等于为160。
    • 再比如一个3.5寸的屏幕,分辨率为480×800,那么密度为 开方(480x800/3.5) 约等于为331。
    • 在比如一个3.5寸的屏幕,分辨率为960x640,那么密度为 开方(960x640/3.5) 约等于418。
    • 再比如一个4寸的屏幕,分辨率为480x800,那么密度为 开方(480x800/4) 约等于309。

    面对种类旁杂的屏幕,开发人员该怎么办,人工针对不同屏幕做相应调整,No!

    让机器调整!开发人员是天生懒惰的!

    那么要调整什么,目的该是让界面元素的物理大小在所有设备上保持一致(但是屏大的似乎天然可以显示的大一点,小屏的可以小一点。)

    过去,开发人员通常以像素为单位设计计算机用户界面。例如,定义一个宽度为300像素的表单字段,列之间的间距为5个像素,图标大小为16×16像素等。这样处理的问题在于,如果在一个每英寸点数(dpi)更高的新显示器上运行该程序,则用户界面会显得很小。在有些情况下,用户界面可能会小到难以看清内容。

    针对屏幕的三个参数,分析如下:

    • 同样物理尺寸,分辨率不同,那么如果按照像素设计,就会产生,分辨率大的那个,图像很小.物理尺寸就会很小.
    • 同样分辨率,不同物理尺寸,如果按钮找像素设计,实际看起来的物理比例是一样的.
    • 看起来物理尺寸一样,不同分辨率,分辨率大的,屏幕尺寸就要大.
    • 看起来物理尺寸一样,不同屏幕尺寸,大尺寸的,就要像素多.

    那么Android框架为自动调整尺寸做了什么呢?

    就是密度无关像素,原文如下

    The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen.

    是说,以160dpi为标准,在一个160dpi的屏幕上的1个物理像素作为设备无关像素的1个像素,也就是Android最佳实践中推荐的dip/dp(以下这两个单位表示同样含义,dip常见于Google官方示例中)这个单位。

    针对于字体,Android设计了sp这个单位,这个于dp的不同在于,字体大小在dp的基础上,可以根据用户的偏好设置,相应调整字体大小,所以是scale的。

    但是!

    Android的做法不是根据160dpi这个标准值和设备实际的dpi的比值进行缩放!而是设定了一套标准化尺寸和密度:

    • 标准化物理尺寸: small, normal, large, and xlarge
    • 标准化屏幕密度: ldpi (low), mdpi (medium), hdpi (high), and xhdpi (extra high)

    Each generalized size or density spans a range of actual screen sizes or density. For example, two devices that both report a screen size of normal might have actual screen sizes and aspect ratios that are slightly different when measured by hand. Similarly, two devices that report a screen density of hdpi might have real pixel densities that are slightly different. Android makes these differences abstract to applications, so you can provide UI designed for the generalized sizes and densities and let the system handle any final adjustments as necessary. Figure 1 illustrates how different sizes and densities are roughly categorized into the different size and density groups.(摘自官方文档)


    (我曾经以为,Android会根据实际dpi进行缩放,这也是我迷惑很久,之前写就在这个卡住了)

    为了证明Android确实不是不是根据实际dpi进行缩放,我查阅了相关的源代码。

    我们知道当显卡绘制一个图像的时候,是根据物理像素绘制的。所以,当开发人员设定dp这种单位的时候,需要一个转化过程,将sp转化为px。

    如果按我之前所想,计算公式该是:实际dpi / mdpi(也就是160dpi)然后乘上sp的数值,这样就得到了在不同设备上物理大小完全一样的的界面元素。

    但是Android不是这样设计的,正如前文所说,是根据那套标准化的密度来进行转换的。通过如下代码(这个是Android将dp转化为px值的过程)。

    public static float applyDimension(int unit, float value, DisplayMetrics metrics) { switch (unit) { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f); } return 0; }

    可以看到,如果单位是dip(dp),那么返回值则是dip的value * metrics.density。

    这里的density是

    The logical density of the display. This is a scaling factor for the Density Independent Pixel unit, where one DIP is one pixel on an approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), providing the baseline of the system's display. Thus on a 160dpi screen this density value will be 1; on a 120 dpi screen it would be .75; etc.
    This value does not exactly follow the real screen size (as given by xdpi and ydpi, but rather is used to scale the size of the overall UI in steps based on gross changes in the display dpi. For example, a 240x320 screen will have a density of 1 even if its width is 1.8", 1.3", etc. However, if the screen resolution is increased to 320x480 but the screen size remained 1.5"x2" then the density would be increased (probably to 1.5).
    (摘自Google官方文档,懒得翻译了,当然也是怕翻译坏了原来的味道,这段还是相当重要的)

    重点是This value does not exactly follow the real screen size。这也解释我之前的疑惑。

    这个density值Displaymetrics记录的,如果你想看看实际情况,可以获取Displaymetrics,通过代码:

    DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics);

    然后就能得到metrics的值。

    另外!

    这类还有xdpi和ydpi这两个值,官方文档上说:The exact physical pixels per inch of the screen in the X(Y) dimension.

    然而,当我试图获取某些机器的这两个值的时候却与我手动计算所得到的值完全不同!

    后来翻阅StackOverflow,看到也有人遇到类似问题,

    作者获得了几个设备的dip值,如下:

    • HTC Desire Z: 480x800, density : HIGH, xdpi: 254.0, ydpi: 254.0
    • Motorola Defy: 480x854, density : HIGH, xdpi: 96.0, ydpi: 96.0
    • Samsung Galaxy S2: 480x800, density : HIGH, xdpi: 217.71428, ydpi: 218.49463
    • LG Optimus 2X: 480x800, density : HIGH, xdpi: 160.0, ydpi: 160.0

    (原文地址: http://stackoverflow.com/questions/6224900/android-incorrect-size-for-views-with-mm-or-inch-dimensions)

    可以看到对于Moto和LG的dpi是明显错误的。

    再回想刚才Android转换单位的函数里面这段代码:

    case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f);

    对于这几个单位的处理都用到了xdpi,所以很可能转换后是错误的值,
    (这里应该仍然算是个疑问,难道真的没有办法得到正确的值吗?我们都知道是不推荐用pt,in,mm这种单位的,这是否也是一个方面)

    至此关于屏幕的问题大体说完,然后就是提供的资源问题,当我们设置了一个界面元素的的大小后,对于不是标准dpi的机器上就要进行缩放,那么对于绘制的矢量元素,自然是不用管,而对于图像这种位图,缩放后会导致模糊等问题,所以就要对标准化dpi的几个大小,提供相应的替换版本,Android会根据实际屏幕规格,进行相应替换,并且有相应的查找资源的规则,看Android源码,可以知道,Android的框架的默认ui使用了大量nine-patch图片。这里就不详细说了。

    好吧,这次就到这里了。

    =======================================================================================

    (一)、尺寸
    现有的Android手机主要屏幕尺寸有:2.8、3.1、3.2、3.7、4、4.2、4.3、5.0(单位/英寸)
    屏幕为2.8英寸的机型主要由HTC Tattoo(也就是我们常说的G4)、摩托罗拉FLIPOUT(行货名称为MB511)等机型,这个尺寸的Android手机通常为入门级机型,价格通常在1000元上下。
       屏幕尺寸3.1-3.5英寸的机型主要为中端机型,代表机型有HTC Hero(G3),摩托罗拉ME600(后空翻)、三星i7500等,价格在2000元上下。
       新上市的屏幕尺寸3.7英寸以上机型通常为Android高端手机,代表机型有HTC Desire S、HTC Sensation、摩托罗拉Droid X(天翼定制型号为ME811)、摩托罗拉Atrix 4G(行货型号为ME860)、三星Nexus S、三星Galaxy S2等,价格通常在3000元以上。
        屏幕尺寸越大,可视范围就越大,由于所有Android手机均为可触摸操作屏幕,所以操作区域也更大。在用手机玩游戏,观看视频方面,大尺寸手机优势明显。
       另外,手机尺寸越大,携带起来也越不方面。我使用过的最大的Android手机是Dell Mini 5(7英寸Galaxy Tab不在手机之列),这部手机屏幕尺寸超过5英寸,几乎无法塞进裤子的口袋。
    (二)、分辨率
    Android手机分辨率主要有240X320、320X480、480X800、480X854几种。
    分辨率一词在港台地区称之为解析度(个人认为解析度一词表达的更为精确),也就是屏幕图像的精密度。分辨率越大的显示屏越清晰。
        分辨率为240X320、320X480的机型通常为Android中低端机型,价格通常在1000-2000元。
        分辨率480X800、480X854的机型通常为中高端机型,价格从2500-5000不等。
    目前大部分软件开发大多以兼容分辨率480X800和480X854的手机为标准,所有有一些软件早一些分辨率的手机会被告知无法运行。
     
    2.      手机尺寸分布情况(http://developer.android.com/resources/dashboard/screens.html)
    目前主要是以分辨率为800*480和854*480的手机用户居多
     
     
    Data collected during a 7-day period ending on August 1, 2011
    ldpi mdpi hdpi xhdpi
    small 3.4%
    normal 0.9% 16.9% 74.5%
    large 3.1%
    xlarge 1.2%
     
     
    2、术语解释
    术语 说明 备注
    Screen size(屏幕尺寸) 指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸摩托罗拉milestone手机是3.7英寸
    Aspect Ratio(宽高比率) 指的是实际的物理尺寸宽高比率,分为long和nolong Milestone是16:9,属于long
    Resolution(分辨率) 和电脑的分辨率概念一样,指手机屏幕纵、横方向像素个数 Milestone是854*480
    DPI(dot per inch) 每英寸像素数,如120dpi,160dpi等,假设QVGA(320*240)分辨率的屏幕物理尺寸是(2英寸*1.5英寸),dpi=160 可以反映屏幕的清晰度,用于缩放UI的
    Density(密度) 屏幕里像素值浓度,resolution/Screen size可以反映出手机密度  
    Density-independent pixel (dip) 指的是逻辑密度计算单位,dip和具体像素值的对应公式是dip/pixel=dpi值/160  表示每英寸有多少个显示点
     
    3、手机屏幕分类和像素密度的对应关系
    VGA  :Video Graphics Array,即:显示绘图矩阵,相当于640×480 像素;
    HVGA :Half-size VGA;即:VGA的一半,分辨率为480×320; density=160
    QVGA :Quarter VGA;即:VGA的四分之一,分辨率为320×240; density=120
    WVGA:Wide Video Graphics Array;即:扩大的VGA,分辨率为800×480像素;density=240
    WQVGA:Wide Quarter VGA;即:扩大的QVGA,分辨率比QVGA高,比VGA低,一般是:400×240,480×272;density=120
    apk的资源包中,当屏幕density=240时使用hdpi标签的资源
    当屏density=160时使用mdpi标签的资源
    当屏幕density=120时使用ldpi标签的资源。
    不加任何标签的资源是各种分辨率情况共用的
    屏幕(Type) 宽度(Pixels) 高度(Pixels) 尺寸Range (inches) 大小Size 密度Group
    QVGA 240 320 2.6 - 3.0 Small screen Low density (120)  ldpi
    WQVGA 240 400 3.2 - 3.5 Normal screen Low density (120)  ldpi
    FWQVGA 240 432 3.5 - 3.8 Normal screen Low density (120)  ldpi
    HVGA 320 480 3.0 - 3.5 Normal screen Mediumdensity(160)mdpi
    WVGA 480 800 3.3 - 4.0 Normal screen High density (240), hdpi
    FWVGA 480 854 3.5 - 4.0 Normalscreen High density (240), hdpi
    WVGA 480 800 4.8 - 5.5 Large screen Medium density(160) mdpi
    FWVGA 480 854 5.0 - 5.8 Large screen Medium density(160) mdpi
     
    4、UI设计
    开发角度讲,应用程序会根据 3 类 A ndroid 手机屏幕提供3 套UI 布局文件,但是相应界面图标也需要提供3 套
    con Type Standard Asset Sizes (in Pixels), for Generalized Screen Densities
      Lowdensityscreen(ldpi) Mediumdensityscreen(mdpi) Highdensityscreen(hdpi)
    Launcher 36 x 36 px 48 x 48 px 72 x 72 px
    Menu 36 x 36 px 48 x 48 px 72 x 72 px
    StatusBar 24 x 24 px 32 x 32 px 48 x 48 px
    Tab 24 x 24 px 32 x 32 px 48 x 48 px
    Dialog 24 x 24 px 32 x 32 px 48 x 48 px
    List View 24 x 24 px 32 x 32 px 48 x 48 px

    ========================================================================================

    http://wolfgangkiefer.blog.163.com/blog/static/8626550320111120105136253/

     

    各种Android操作系统的手机简直就是琳琅满目,屏幕分辨率的差异可想而知。目前比较主流的有WVGA=800x480,HVGA=480x320,另外的还有QVGA=320x240。当然还有魅族M9的DVGA=960x640,还有蛋疼的摩托罗拉的FWVGA=854x480。

      其实,在你layout的xml文件中,编写的时候是不是用了许多的padding呢?如果是,那你就蛋疼了。因为这样的布局永远是无法适应所有手机屏幕的。

      正确的做法应该是使用的是weight属性。将你控件的layout中的width、height设置为fill-parent,不要使用wrap——content。因为wrap-content的大小是不固定的。而weight(权重)这个属性很好的解决了这个问题。

      当包裹在控件外面的Layout的width、height属性都设置为fill-parent时,可以利用weight的反比特性。即如果控件A设置weight为5,控件B设置weight为7,那么A所占的空间为5/(5+7),B所占的空间为7/(5+7)。这样的反比属性对任何分辨率下的手机都是合适的。


      当然,字体就不行了。那怎么保证字体能够跟布局一样能够自适应呢?
      呵呵,很简单,就是在你的res文件夹中创建一个文件夹,叫做values-320x240。其中320x240是你手机屏幕的分辨率,根据你手机屏幕的情况做不同的命名,例如values- 800x480。在该文件夹下创建一个dimens.xml文件,定义各种字体的大小。那么系统就会自动根据你手机屏幕的分辨率去调用响应的文件夹。


        另外,值得提醒的是,记得在你默认的values文件下的dimens.xml文件中也要写上相应的字体大小哦,因为当系统无法认识你手机屏幕大小的时候,它会自动去找你默认文件中的

    东西,没有写的话程序会崩溃。

    ************************************************************分割线************************************************************

      在看下面内容之前首先请看你SDK文档中以下这篇文章

    Android不同手机屏幕分辨率自适应

    其实google在分辨率适应性的东西已经写的很清楚了,只是我们很多人没去看而已

      以下是结论:
        屏幕分辨率:1024x600
        density:1(160)
        文件夹:values-mdpi-1024x600

        屏幕分辨率:1024x600
        density:1.5(240)
        文件夹:values-hdpi-683x400  由1024/1.5  600/1.5得到,需要四舍五入。

        屏幕分辨率:800x480
        density:1(160)
        文件夹:values-mdpi-800x480

        屏幕分辨率:800x480
        density:1.5(240)
        文件夹:values-hdpi-533x320  由800/1.5  480/1.5得到,需要四舍五入。

      以此类推
        一般情况下需要创建出values 、values-mdpi 、 values-hdpi文件夹,以备在一些没有规定的尺寸屏幕上找不到资源的情况。

        然后在里面使用不同的dimens文件,Layout中不要使用显示的数字,所有的尺寸定义全都援引dimens里面的内容。

        这样能够保证深度UI定制的情况

        另外在工程的default.properties中如果split.density=false,则分辨率适配的时候文件夹命名不需要与scale相除

      例
      屏幕分辨率:800x480
      density:1.5(240)
      文件夹:values-hdpi-800x480

    ************************************************************分割线************************************************************

      关于dimens 

        位置:res\values
        单位:px   Pixel 以画面的像素为单位;
             in     Inches以画面的多少英寸为单位;
             mm  Millimeter以画面的多少毫米为单位;
             pt     Points 一点为1/72英寸;
             dp或dip  Density-indepentdent 为160dpi屏幕的一个pixel;
             ap Scale-independent Pixels 随屏幕尺寸改变的一个pixel;

    1.drawable: 存放不同分辨率对应图片

          在2.1版本中有drawable-mdpi、drawable-ldpi、drawable-hdpi三个,这三个主要是为了支持多分辨率。

      drawable- hdpi、drawable- mdpi、drawable-ldpi的区别:

      (1)drawable-hdpi里面存放高分辨率的图片,如WVGA (480x800),FWVGA (480x854)

      (2)drawable-mdpi里面存放中等分辨率的图片,如HVGA (320x480)

      (3)drawable-ldpi里面存放低分辨率的图片,如QVGA (240x320)

      系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片。

          在2.1之前的版本可以通过drawable-800x480, drawable-480x320 等方式实现。

    2:layout:放置对应不同分辨率的布局

          创建不同的layout文件夹, layout-800x480,layout-480x320, 系统会根据屏幕的大小自己选择合适的layout进行使用。

          另外:可以在res目录下建立layout-port和layout-land两个目录,里面分别放置竖屏和横屏两种布局文件。

    下面列出主流的android机型有:

    240x320低端,国产入门级采用,例如HTC G4,G8
    320x480中端,大部分基于此分辨率,例如HTC G1,G2,G3,G6, MOTO ME600, SAMSUNG I7500
    480x800中高端,大部分基于此分辨率,例如HTC G5,G7, MOTO MT810
    480x854MOTO特有的,例如Droid, Milestone, XT800
    960x640, 魅族M9

    更为详细的见下图:

    Android不同手机屏幕分辨率自适应

    Android不同手机屏幕分辨率自适应

    Android不同手机屏幕分辨率自适应 

    参考了以下资料:
    http://topic.csdn.net/u/20101119/09/29efa19b-958e-493b-9200-38d7205a26fa.html

    http://hi.baidu.com/wudaovip/blog/item/7453084e811697c4d1c86a15.html

     

     ========================================================================

    1、屏幕相关概念
    1.1分辨率
    是指屏幕上有横竖各有多少个像素
    1.2屏幕尺寸
    指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸
    android将屏幕大小分为四个级别(small,normal,large,and extra large)。
    1.3屏幕密度
    每英寸像素数
    手机可以有相同的分辨率,但屏幕尺寸可以不相同,
    Diagonal pixel表示对角线的像素值(=),DPI=933/3.7=252
    android将实际的屏幕密度分为四个通用尺寸(low,medium,high,and extra high)
    一般情况下的普通屏幕:ldpi是120dpi,mdpi是160dpi,hdpi是240dpi,xhdpi是320dpi
    对于屏幕来说,dpi越大,屏幕的精细度越高,屏幕看起来就越清楚
    1.4密度无关的像素(Density-independent pixel——dip)
    dip是一种虚拟的像素单位
    dip和具体像素值的对应公式是dip/pixel=dpi值/160,也就是px = dp * (dpi / 160)
    当你定义应用的布局的UI时应该使用dp单位,确保UI在不同的屏幕上正确显示。

    手机屏幕分类和像素密度的对应关系如表1所示

    手机尺寸分布情况(http://developer.android.com/resources/dashboard/screens.html)如图所示,

    目前主要是以分辨率为800*480和854*480的手机用户居多
    从以上的屏幕尺寸分布情况上看,其实手机只要考虑3-4.5寸之间密度为1和1.5的手机

    2、android多屏幕支持机制
    Android的支持多屏幕机制即用为当前设备屏幕提供一种合适的方式来共同管理并解析应用资源。
    Android平台中支持一系列你所提供的指定大小(size-specific),指定密度(density-specific)的合适资源。
    指定大小(size-specific)的合适资源是指small, normal, large, and xlarge。
    指定密度(density-specific)的合适资源,是指ldpi (low), mdpi (medium), hdpi (high), and xhdpi (extra high).
    Android有个自动匹配机制去选择对应的布局和图片资源
    1)界面布局方面
    根据物理尺寸的大小准备5套布局:
    layout(放一些通用布局xml文件,比如界面顶部和底部的布局,不会随着屏幕大小变化,类似windos窗口的title bar),
    layout-small(屏幕尺寸小于3英寸左右的布局),
    layout-normal(屏幕尺寸小于4.5英寸左右),
    layout-large(4英寸-7英寸之间),
    layout-xlarge(7-10英寸之间)
    2)图片资源方面
    需要根据dpi值准备5套图片资源:
    drawable:主要放置xml配置文件或者对分辨率要求较低的图片
    drawalbe-ldpi:低分辨率的图片,如QVGA (240x320)
    drawable-mdpi:中等分辨率的图片,如HVGA (320x480)
    drawable-hdpi:高分辨率的图片,如WVGA (480x800),FWVGA (480x854)
    drawable-xhdpi:至少960dp x 720dp
    Android有个自动匹配机制去选择对应的布局和图片资源。
      系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片。
      在开发程序时为了兼容不同平台不同屏幕,建议各自文件夹根据需求均存放不同版本图片。

    3、AndroidManifest.xml 配置
    android从1.6和更高,Google为了方便开发者对于各种分辨率机型的移植而增加了自动适配的功能
    <supports-screens
    android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>
    3.1是否支持多种不同密度的屏幕
    android:anyDensity=["true" | "false"]
    如果android:anyDensity="true"
    指应用程序支持不同密度,会根据屏幕的分辨率自动去匹配。
    如果android:anyDensity="false"
    应用程序支持不同密度,系统自动缩放图片尺寸和这个图片的坐标。具体解释一下系统是如何自动缩放资源的。
    例如我们在hdpi,mdpi,ldpi文件夹下拥有同一种资源,那么应用也不会自动地去相应文件夹下寻找资源,这种情况都是出现在高密度,以及低密度的手机上,比如说一部240×320像素的手机,
    如果设置android:anyDensity="false",Android系统会将240 x 320(低密度)转换为 320×480(中密度),这样的话,应用就会在小密度手机上加载mdpi文件中的资源。
    3.2是否支持大屏幕
    android:largeScreens=["true" | "false"]
    如果在声明不支持的大屏幕,而这个屏幕尺寸是larger的话,系统使用尺寸为("normal")和密度为("medium)显示,
    不过会出现一层黑色的背景。
    3.3是否支持小屏幕
    android:smallScreens=["true" | "false"]
    如果在声明不支持的小屏幕,而当前屏幕尺寸是smaller的话,系统也使用尺寸为("normal")和密度为("medium)显示
    如果应用程序能在小屏幕上正确缩放(最低是small尺寸或最小宽度320dp),那就不需要用到本属性。否则,就应该为最小屏幕宽度标识符设置本属性
    来匹配应用程序所需的最小尺寸。


    4、Android提供3种方式处理屏幕自适应
    4.1预缩放的资源(基于尺寸和密度去寻找图片)
    1)如果找到相应的尺寸和密度,则利用这些图片进行无缩放显示。
    2)如果没法找到相应的尺寸,而找到密度,则认为该图片尺寸为 "medium",利用缩放显示这个图片。
    3)如果都无法匹配,则使用默认图片进行缩放显示。默认图片默认标配 "medium" (160)。
    4.2自动缩放的像素尺寸和坐标(密度兼容)
    1)如果应用程序不支持不同密度android:anyDensity="false",系统自动缩放图片尺寸和这个图片的坐标。
    2)对于预缩放的资源,当android:anyDensity="false",也不生效。
    3)android:anyDensity="false",只对密度兼容起作用,尺寸兼容没效果
    4.3兼容更大的屏幕和尺寸(尺寸兼容)
    1)对于你在声明不支持的大屏幕,而这个屏幕尺寸是normal的话,系统使用尺寸为 ("normal")和密度为("medium)显示。
    2.)对于你在声明不支持的大屏幕,而这个屏幕尺寸是larger的话,系统同样使用尺寸为("normal")和密度为("medium)显示,
    不过会出现一层黑色的背景。

    5、Android系统自动适配技巧
    Android系统采用下面两种方法来实现应用的自动适配:
    1)布局文件中定义长度的时候,最好使用wrap_content,fill_parent, 或者dp 进行描述,这样可以保证在屏幕上面展示的时候有合适的大小
    2)为不同屏幕密度的手机,提供不同的位图资源,可以使得界面清晰无缩放。
    对应bitmap 资源来说,自动的缩放有时会造成放大缩小后的图像变得模糊不清,这是就需要应用为不同屏幕密度配置提供不同的资源:为高密度的屏幕提供高清晰度的图像等。
    3)不要使用AbsoluteLayout
    4)像素单位都使用DIP,文本单位使用SP

    6、在代码中获取屏幕像素、屏幕密度
    DisplayMetrics metric = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metric);
    int width = metric.widthPixels; // 屏幕宽度(像素)
    int height = metric.heightPixels; // 屏幕高度(像素)
    float density = metric.density; // 屏幕密度(0.75 / 1.0 / 1.5)
    int densityDpi = metric.densityDpi; // 屏幕密度DPI(120 / 160 / 240)

    7、 一般多分辨率处理方法及其缺点
    7.1 图片缩放
    基于当前屏幕的精度,平台自动加载任何未经缩放的限定尺寸和精度的图片。如果图片不匹配,平台会加载默认资源并且在放大或者缩小之后可以满足当前界面的显示要求。例如,当前为高精度屏幕,平台会加载高精度资源(如HelloAndroid中drawable-hdpi 中的位图资源),如果没有,平台会将中精度资源缩放至高精度,导致图片显示不清晰。
    7.2 自动定义像素尺寸和位置
    如果程序不支持多种精度屏幕,平台会自动定义像素绝对位置和尺寸值等,这样就能保证元素能和精度160 的屏幕上一样能显示出同样尺寸的效果。例如,要让WVGA 高精度屏幕和传统的HVGA 屏幕一样显示同样尺寸的图片,当程序不支持时,系统会对程序慌称屏幕分辨率为320×480,在(10,10)到(100,100)的区域内绘制图形完成之后,系统会将图形放大到(15,15)到(150,150)的屏幕显示区域。
    7.3 兼容更大尺寸的屏幕
    当前屏幕超过程序所支持屏幕的上限时,定义supportsscreens元素,这样超出显示的基准线时,平台在此显示黑色的背景图。例如,WVGA 中精度屏幕上,如程序不支持这样的大屏幕,系统会谎称是一个320×480 的,多余的显示区域会被填充成黑色。
    7.4 采用OpenGL 动态绘制图片
    Android 底层提供了OpenGL 的接口和方法,可以动态绘制图片,但是这种方式对不熟悉计算机图形学的开发者来讲是一个很大的挑战。一般开发游戏,采用OpenGL 方式。
    7.5 多个apk 文件
    Symbian 和传统的J2ME 就是采用这种方式,为一款应用提供多个分辨率版本,用户根据自己的需求下载安装相应的可执行文件。针对每一种屏幕单独开发应用程序不失为一种好方法,但是目前Google Market 对一个应用程序多个分辨率版本的支持还不完善,开发者还是需要尽可能使用一个apk 文件适应多个分辨率。
    展开全文
  • Flink运行时之生产端结果分区

    千次阅读 2016-12-30 22:05:43
    生产端结果分区生产者结果分区是生产端任务所产生的结果...以一个简单的MapReduce程序为例,从静态的角度来看,生产端的算子(Map)跟消费端的算子(Reduce),两者之间交换数据通过中间结果集(IntermediateResult)。

    生产端结果分区

    生产者结果分区是生产端任务所产生的结果。以一个简单的MapReduce程序为例,从静态的角度来看,生产端的算子(Map)跟消费端的算子(Reduce),两者之间交换数据通过中间结果集(IntermediateResult)。形如下图:

    mapreduce-static-dataexchange

    而IntermediateResult只是在静态表述时的一种概念,在运行时,算子会被分布式部署、执行,我们假设两个算子的并行度都为2,那么对应的运行时模型如下图:

    IntermediateResult-at-runtime

    生产端的Map算子会产生两个子任务实例,它们各自都会产生结果分区(ResultPartition)。但ResultPartition并不会直接被处于消费端的Reduce的子任务实例消费,它会再次进行分区从而产生结果子分区(ResultSubpartition),ResultSubpartition是最终保存Buffer的地方。接下来的这一篇,我们就来详细分析生产者分区。

    结果分区

    在运行时,Flink使用结果分区(ResultPartition)来表示单一任务的子任务实例所生产的数据,这在其作业图中等价于中间结果分区(IntermediateResultPartition)。需要避免对这两个概念产生混淆,IntermediateResultPartition主要用于JobManager组织作业图的一种逻辑数据结构,ResultPartition是运行时的一种逻辑概念,两者处于不同的层面。

    每个ResultPartition拥有一个BufferPool并且是被其包含的ResultSubPartition共享的。ResultSubPartition个数主要取决于消费任务的数目以及数据的分发模式(DistributionPattern)。任何想消费ResultPartition的任务,最终都是请求ResultPartition的某个ResultSubPartition。而请求要么是同一TaskManager中的本地请求要么是来自另外一个TaskManager中的消费子任务实例发起的远程请求。

    每个ResultPartition的生命周期都有三个阶段:生产、消费和释放。

    结果分区类型(ResultPartitionType)是一个枚举类型,指定了ResultPartition的不同属性,这些属性包括是否可被持久化、是否支持管道以及是否会产生反压。ResultPartitionType有三个枚举值:

    • BLOCKING:持久化、非管道、无反压;
    • PIPELINED:非持久化、支持管道、有反压;
    • PIPELINED_PERSISTENT(当前暂不支持)

    其中管道属性会对消费端任务的消费行为产生很大的影响。如果是管道型的,那么在结果分区接收到第一个Buffer时,消费者任务就可以进行准备消费(如果还没有部署则会先部署),而如果非管道型,那么消费者任务将等到生产端任务生产完数据之后才会着手进行消费。

    结果分区编号(ResultPartitionID)用来标识ResultPartition。ResultPartitionID关联着IntermediateResultPartitionID(也即调度时的分区编号)和ExecutionAttemptID(部署时的生产者子任务实例编号)。在任务失败时,单靠IntermediateResultPartitionID无法鉴别ResultPartition,必须结合ExecutionAttemptID一起鉴别。

    一个ResultPartition有多少个ResultSubPartition,是在构建ResultPartition就确定了的。当生产端任务调用记录写入器写入一个记录时,该记录先被序列化器序列化并放入Buffer中,然后通过ResultPartitionWriter加入到ResultPartition,具体被加入哪个子分区中取决于ChannelSelector,该加入方法实现如下:

    public void add(Buffer buffer, int subpartitionIndex) throws IOException {
        boolean success = false;
    
        try {
            //确认生产状态处于未完成状态
            checkInProduceState();
    
            //获取指定索引的子分区
            final ResultSubpartition subpartition = subpartitions[subpartitionIndex];
    
            synchronized (subpartition) {
                //如果Buffer被加入子分区,则success被置为true
                success = subpartition.add(buffer);
    
                //更新统计信息
                totalNumberOfBuffers++;
                totalNumberOfBytes += buffer.getSize();
            }
        } finally {
            //如果Buffer被加入成功,且当前的模式是管道模式,则立即通知消费者任务
            if (success) {
                notifyPipelinedConsumers();
            }
            //如果加入失败,则回收Buffer
            else {
                buffer.recycle();
            }
        }
    }

    在notifyPipelinedConsumers方法中,会通过分区可消费通知器(ResultPartitionConsumableNotifier)间接通知消费者任务(经过JobManager转发通知),它会携带两个信息:

    • JobID
    • ResultPartitionID

    ResultPartition有一个标识变量hasNotifiedPipelinedConsumers,用来表示当前是否已通知过消费者,在notifyPipelinedConsumers中,一旦通知过,该标识将会被设置为true,所以该通知只会发生在第一个被成功加入的Buffer之后,后续便不再通知。

    ResultPartitionConsumableNotifier当前只有一个实现JobManagerResultPartitionConsumableNotifier(位于NetworkEnvironment中的一个静态内部类),它会通过Actor网关向JobManager发送一条请求消息(ask模式,需要应答)。

    这是针对管道模式的ResultPartition而言的,而针对阻塞模式的ResultPartition的通知时机却需要等到数据生产完成之后(ResultPartition的finish方法被调用),任务会向JobManager报告其状态变更为FINISHED。JobGraph根据执行图(ExecutionGraph)找到完成任务对应的IntermediateResultPartition的消费者任务并调度它们进行消费:

    for (IntermediateResultPartition finishedPartition : getVertex().finishAllBlockingPartitions()) {
        IntermediateResultPartition[] allPartitions = finishedPartition
            .getIntermediateResult().getPartitions();
    
        for (IntermediateResultPartition partition : allPartitions) {
            scheduleOrUpdateConsumers(partition.getConsumers());
        }
    }

    当每个子分区中的缓冲区数据被消费完后,它们会通知ResultPartition。因为一个ResultPartition包含若干个ResultSubPartition,那么ResultPartition如何判断所有的ResultSubPartition都被消费完了呢?它基于原子计数器(AtomicInteger),每个ResultSubPartition被消费完成之后都会回调ResultPartition的实例方法onConsumedSubpartition:

    void onConsumedSubpartition(int subpartitionIndex) {
        //已被释放,则直接返回
        if (isReleased.get()) {
            return;
        }
    
        //计数器减一后获得未完成的子分区计数
        int refCnt = pendingReferences.decrementAndGet();
    
        //如果全部都已完成,则通知ResultPartitionManager,它会将ResultPartition直接释放
        if (refCnt == 0) {
            partitionManager.onConsumedPartition(this);
        }
        //异常
        else if (refCnt < 0) {
            throw new IllegalStateException("All references released.");
        }
    
        LOG.debug("{}: Received release notification for subpartition {} (reference count now at: {}).",
                    this, subpartitionIndex, pendingReferences);
    }

    我们在add方法中看到,ResultPartition其实不保存Buffer,它只是起到一个分配或者转发的作用,Buffer真正会被保存到ResultSubPartition中。

    ResultPartition会被消费端任务消费,但对消费者而言,其跟待消费的ResultPartition之间不同的位置消费方式却不一样。ResultPartitionLocation对不同的位置进行了定义和封装。目前支持三种位置类型:

    • LOCAL:表示消费者任务被部署在跟生产该ResultPartition的生产者任务相同的实例上;
    • REMOTE:表示消费者任务被部署在跟生产该ResultPartition的生产者任务不同的实例上;
    • UNKNOWN:表示ResultPartition未被注册到生产者任务,当部署消费者任务时,其实例可能是确定的也可能是不确定的。

    对ResultPartition进行管理的部件是结果分区管理器(ResultPartitionManager)。一个NetworkEnvironment对应一个ResultPartitionManager。

    ResultPartitionManager会对某个TaskManager中已生产和已被消费的ResultPartition进行跟踪。具体而言,它采用Guava库里的Table这一集合类型来维护其所管理的ResultPartition。

    Table是Guava集合库提供的一个多级映射容器类型。效仿了关系型数据库中的数据表结构,支持”row”、“column”、“value”。其结构等价于Map<R, Map<C,V>>且提供了针对多个Map的实现。

    该数据结构的完整定义如下:

    public final Table<ExecutionAttemptID, IntermediateResultPartitionID, ResultPartition> 
        registeredPartitions = HashBasedTable.create();

    在NetworkEnvironment中注册Task时,会获取该Task所生产的ResultPartition数组。然后用ResultPartitionManager的registerResultPartition方法进行注册。同样,在对Task解除注册时,会调用ResultPartitionManager的releasePartitionsProducedBy方法,将相应的ExecutionAttemptID对应的信息从registeredPartitions表中移除。在releasePartitionsProducedBy方法中,所有的ResultPartition都会调用其release以释放各自占用的资源。

    结果子分区

    ResultPartition可以看作结果子分区(ResultSubpartition)的容器,而ResultSubpartition是真正存储供消费者消费Buffer的地方。ResultSubPartition是一个抽象类,针对不同类型的ResultPartition提供了两个实现:

    • PipelinedSubpartition:基于内存的管道模式的结果子分区;
    • SpillableSubpartition:基础模式下是基于内存的阻塞式的结果子分区,但数据量过大时可以将数据溢出到磁盘;

    在ResultPartition的构造器中,会根据ResultPartitionType来实例化特定的结果子分区:

    switch (partitionType) {
        case BLOCKING:
            for (int i = 0; i < subpartitions.length; i++) {
                subpartitions[i] = new SpillableSubpartition(i, this, ioManager, defaultIoMode);
            }
            break;
    
        case PIPELINED:
            for (int i = 0; i < subpartitions.length; i++) {
                subpartitions[i] = new PipelinedSubpartition(i, this);
            }
            break;
    
        default:
            throw new IllegalArgumentException("Unsupported result partition type.");
    }

    PipelinedSubpartition会将数据保存在双端队列中(ArrayDeque),在ResultPartition完成数据生产时,其finish方法会得到调用,该方法会依次触发它所包含的所有的ResultSubpartition的finish方法,作为结束的标记,一个EndOfPartitionEvent事件会作为一个特殊的Buffer加入到双端队列中去。

    SpillableSubpartition结合了内存缓存和磁盘持久化的能力。最初,Buffer被加入进来时是以一个ArrayList来缓存,当BufferPoolOwner也就是SpillableSubpartition的父容器ResultPartition因为Buffer资源紧张决定释放一定数量的Buffer时,其releaseMemory方法会间接被触发(这对于SpillableSubpartition来说意味着将没有足够的内存资源来容纳生产者的数据了)。这时,它会通过IOManager的createBufferFileWriter方法来创建一个BufferFileWriter(通过该写入器可以以阻塞的模式将Buffer写到磁盘上),这时所有ArrayList内的Buffer都将被写入磁盘。注意,BufferFileWriter的实例一旦被创建之后,所有再加入进来的Buffer都将被直接写入磁盘,而不再加入到ArrayList。维护两个“Buffer源”并不是一个明智的选择,并且当BufferFileWriter被创建,也意味着内存不再宽裕。同样,其finish方法也会加入一个EndOfPartitionEvent来标记结束。

    结果子分区视图

    ResultSubpartition负责容纳Buffer,但考虑到它对Buffer提供了不同的存储实现,所以又提供了结果子分区视图(ResultSubpartitionView)抽象出从不同的存储机制中读取Buffer的方式。因此,ResultSubpartitionView才是对接数据消费端的对象。当前的ResultSubpartitionView的实现有:

    ResultSubpartitionView-class-diagram

    其中跟SpillableSubpartition相关的就有三个,它们的差异如下:

    • SpillableSubpartitionView:通用的基于内存、磁盘的读取视图,如果数据溢出到磁盘,则借助于另外两个基于磁盘的读取视图;
    • SpilledSubpartitionViewSyncIO:溢出到磁盘以同步模式读取的视图;
    • SpilledSubpartitionViewAsyncIO:溢出到磁盘以异步模式读取的视图;

    这些视图的选择逻辑封装在SpillableSubpartition的createReadView方法中。而对于PipelinedSubpartitionView,很显然它是关联着PipelinedSubpartition的。

    ResultSubpartitionView提供了获取Buffer的接口方法getNextBuffer。因为每个ResultSubpartition存储这些Buffer的机制不一,这才是为什么需要ResultSubpartitionView的原因。

    其实,ResultSubpartitionView并不是针对PipelinedSubpartition而构建的,更主要的是针对SpillableSubpartition。

    PipelinedSubpartitionView的实现我们就不多说了,它就是从PipelinedSubpartition存储Buffer的队列出队一条记录。

    我们的重点将会放在由SpillableSubpartition所衍生出的三个视图对象上。

    对于SpilledSubpartitionViewSyncIO,其以同步的形式(SynchronousBufferFileReader)从磁盘读取,读取器读取的单位就是Buffer。SpilledSubpartitionViewSyncIO自己在内部实现了一个缓冲池SpillReadBufferPool,其缓冲池里的内存段并非池化的,而是直接申请,缓冲池被销毁时所有的内存段随即被释放。

    SpilledSubpartitionViewAsyncIO采用的是AsynchronousBufferFileReader这一异步Buffer读取器,该读取器采用的是批量读取的模式从磁盘读取,默认单批次读取数量为2。该读取器会启动一个独立的I/O线程来读取,读取完成之后会触发一个RequestDoneCallback类型的异步回调,SpilledSubpartitionViewAsyncIO内部实现了这一接口,在读取完成之后会触发returnBufferFromIOThread方法,它会把读取到的Buffer加入到ConcurrentLinkedQueue<Buffer>类型的队列中去。SpilledSubpartitionViewAsyncIO中用于填充从文件读取到数据的Buffer是从ResultPartition的BufferPool中获取到的。既然是从池中获取Buffer,那么就会存在没有Buffer可用的情况,这里还是通过事件回调的机制在有可用Buffer时触发处理逻辑。

    最后再讲一下SpillableSubpartitionView,它依赖于上面两个基于文件读取的视图。具体而言,跟SpillableSubpartition类似,它会判断其对应的SpillableSubpartition实例的spillWriter变量以及用于支持基于文件读取的视图对象spilledView是否为空,以将读取的场景划分为三种:在内存中、已溢出到磁盘、正在溢出到磁盘。在内存中,直接从集合中返回;已溢出到磁盘,则直接调用基于文件的读取视图返回;正在溢出则返回null,因为在写磁盘没有完全结束时,不会进行消费。


    微信扫码关注公众号:Apache_Flink

    apache_flink_weichat


    QQ扫码关注QQ群:Apache Flink学习交流群(123414680)

    qrcode_for_apache_flink_qq_group

    展开全文
  • JVM系列之:从汇编角度分析Volatile

    千次阅读 2020-08-05 09:57:16
    今天我们从汇编的角度来分析一下Volatile关键字到底是怎么工作的。 重排序 这个世界上有两种重排序的方式。 第一种,是在编译器级别的,你写一个java源代码,经过javac编译之后,生成的字节码顺序可
  • Android Camera旋转角度总结

    千次阅读 2019-08-18 23:56:53
    根据相机旋转角度以及屏幕显示旋转角度选择相机预览数据显示到View上的预览数据显示旋转角度,使眼睛直接看到的真实画面和手机屏幕中显示的画面效果相同。 **相机旋转角度:**相机成像相对于手机的旋转角度,若...
  • 从JDK源码角度看Integer

    万次阅读 2017-08-15 16:31:41
    digits数组用于表示数字的所有可能的字符,因为int支持从2进制到36进制,所以这里需要有36个字符才能表示所有不同进制的数字。 sizeTable数组主要用在判断一个int型数字对应字符串的长度。比如相关的方法如下,...
  • JVM系列之:从汇编角度分析NullCheck

    千次阅读 2020-08-06 07:42:39
    之前我们在讲Virtual call的时候有提到,virtual call方法会根据传递的参数实例的不同而进行优化,从而优化成为classic call,从而提升执行效率。 今天我们考虑一下,在virtual call中执行nullcheck的时候,如果已经...
  • SSD人脸检测以及FDDB检测结果分析

    千次阅读 2018-08-26 20:09:12
    前段时间使用caffe版本的SSD训练人脸检测,效果还不错,在FDDB上测试了下结果最终只有89%的准确率,但是...可以看到: 很模糊的脸没有检测到 还有些基本上算不上脸 极少数是人脸的确实没有检测到 发现FDDB中不...
  • 可以看到,这里在第10行判断如果stack是等于null的,则去创建一个HttpStack对象,这里会判断如果手机系统版本号是大于9的,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例。实际上 HurlStack的...
  • MATLAB不同时频信号处理方法介绍及效果对比

    千次阅读 多人点赞 2018-10-25 17:29:40
    目录MATLAB不同时频信号处理方法介绍及效果对比信号的频域分析Fourier(FFT)变换基本原理MATLAB 中信号FFT的基本处理方法MATLAB 中不同参数设置对FFT结果的影响信号的功率谱分析的基本原理MATLAB中信号功率谱分析的...
  • Spark ALS算法推荐结果一样?

    千次阅读 热门讨论 2016-10-05 16:41:12
    在进行Spark ALS算法进行试验的时候发现模型对所有用户其推荐结果是一样的,即针对所有用户建模得到的模型对其推荐的项目是一样的,只是评分有比较小的差异。下面就分3个部分来进行分析,分别是实验过程及结果描述,...
  • 从面试官的角度分享一些后端校招经验

    万次阅读 多人点赞 2018-07-01 16:24:46
    看到很多同学面试几场下来,只知道面试官问了什么问题,却对面试官如何从这些问题中勾勒出一个候选人的知识结构毫无概念,也不知道自己再哪些方面有缺陷,希望本文能给你提供一个不一样的视角,学会从面试官的角度...
  • 图像处理入门:如何处理不同类型的图像

    千次阅读 多人点赞 2021-01-15 18:20:26
    本节将讨论不同的图像处理函数(使用点变换和几何变换),以及如何处理不同类型的图像。 1.5.1 处理不同的文件格式和图像类型 图像可以以不同的文件格式和不同的模式(类型)保存。接下来我们将讨论如何使用...
  • 下查看的结果 超线程技术就是利用特殊的硬件指令,把一个物理芯片模拟成两个逻辑处理核心,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了 CPU 的闲置时间,提高的 CPU 的运行效率。...
  • Zsh和Bash究竟有何不同

    万次阅读 多人点赞 2018-07-12 11:03:30
    chsh -s /bin/bashchsh -s /bin/zsh已经有不少人写过类似“为什么Zsh比Bash好”“为什么Zsh比* shell好”的文章了,讲解如何配置Zsh或折腾各种oh-my-zsh主题的教程也是一搜一大箩,但是却极少看到Zsh和Bash这两个...
  • Unity 3D中不同坐标系下的旋转和平移

    千次阅读 2018-08-27 20:19:16
    同一个物体基于不同的坐标系进行旋转所得结果不一样; 本文的目的是探究游戏引擎Unity3D不同的旋转API的表现,我们都知道,在Unity3D中有多种刚体旋转的API:①有对localRotation进行赋值修改的方式达到旋转的目的...
  • OpenGL三角形的双面不同颜色的绘制

    千次阅读 2015-05-26 20:50:12
    对于一个三角形,我要给它正反面不同的颜色。然后通过旋转,看出它的效果。 我只想到了2种方法,下面我来写一下这两种方法。 第一种方法,通过角度的判断重设glColor3f的参数(这种方法局限性很大,不推荐,不喜欢...
  • CNN 可视化结果分析

    千次阅读 2016-04-28 13:12:34
    可视化结果分别从以下几个角度做分析: 1 看每个卷积层经过激活函数(relu)后的输出图像 第一个卷积层的结果(相对比较容易懂): 为了方便人眼观察,对每一幅图的像素值都做了一个放大,做法是除以这幅图的最大...
  • ADXL345 三轴加速度角度传感器

    千次阅读 2019-02-27 21:14:31
    @[topic]ADXL345 三轴加速度角度传感器目录 ADXL345 三轴加速度角度传感器 简介 这款ADXL345三轴加速度采用ADXL345芯片,具有体积小,功耗低的特点,13位数字精度分辨能够测量超过±16g的加速度变换。信号输出为16位...
  • SQL如何合并查询结果

    万次阅读 2010-06-23 14:28:00
    1.UNION的作用...另外,当我们用 UNION这个指令时,我们只会看到不同的资料值 (类似 SELECT DISTINCT)。 union只是将两个结果联结起来一起显示,并不是联结两个表………… UNION 的语法如下: [SQL 语句 1]<br
  • (5)最后的聚类效果不仅仅考虑名人的专业领域,也考虑了其生活中的情感状态、爱好兴趣等,是一个综合的结果,选取不同的专业领域可以通过选取不同topic做聚类分析而得。 参考文献: 原文链接: 微博名人那些事儿  ...
  • 从用户角度看,数据库系统的结构

    千次阅读 2019-03-21 11:49:04
    从最终用户角度来看,数据库系统分为单用户结构、主从式结构、分布式结构、客户机/服务器结构。 1. 单用户数据库系统 单用户数据库系统是一种早期的最简单的数据库系统。在这种系统中,整个数据库系统(包括应用程序...
  • 自动化套图制作,适配不同屏幕尤其是对于android开发来说,要适配不同屏幕就需要多套切图,那么。这款工具将让你脱离切图的苦海,拥抱愉快开发的怀抱。 编程之禅这次的工具制作,可谓是煞费苦心了。我尝试着让自己...
  • SPSS多元线性回归结果分析

    万次阅读 多人点赞 2018-04-11 17:42:00
    结果的统计学意义,是结果真实程度(能够代表总体)的一种估计方法。专业上,p 值为结果可信程度的一个递减指标,p 值越大,我们越不能认为样本中变量的关联是 总体中各变量关联的可靠指标。p 值是将观察结果认为...
  • jmeter生成html性能结果报告

    万次阅读 多人点赞 2018-06-11 12:51:04
    注2:Apdex 标准从用户的角度出发,将对应用响应时间的表现,转为用户对于应用性能的可量化为范围为 0-1 的满意度评价。。 Apdex (Application Performance Index) is an open standard developed by an ...
  • 之前百度大概有50-60张不同的图片,也就是说不同角度(360°)全部加入图片库的话最多21600张图(估计值)。 现在通过抓取了几百张图片观察,不同的图片大概有120多张,之前的50-60张也包括在内,也就是说百度又...
  • 不同数值微分方法的精度讨论

    千次阅读 2017-06-20 15:32:54
    在进行姿态解算的时候,有一个环节是“通过角度求角速率”,用到离散点求微分的知识,也就是“数值微分”。有人说离散点直接差分就好了,但对于对精度有一定要求的时候,直接差分可以胜任吗? 验证方法 身边恰好有...
  • 从三个角度理解知识图谱

    千次阅读 2018-08-18 12:19:58
    自己在研究大数据独角兽Palantir之后开始接触知识图谱,也算对其有了一定了解,这里从三个角度总结一下怎么去理解知识图谱。 几个基本概念:本体、实体、知识库、知识图谱 1、本体是概念的集合,是大家都公认的...
  • Stata 结果输出系列推文: Stata:毕业论文大礼包 A——实证结果输出命令大比拼 Stata:毕业论文大礼包 B——神速实证结果输出之搜狗短语   作者:王美庭 (中南民族大学经济学院) Stata连享会   ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 257,381
精华内容 102,952
关键字:

不同的角度看到不同的结果