??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
]]>
]]>
特别注意Q按javal出?/span>Focus实现规范Q?/span>jre1.7保证实现如下几点Q?/span>
1. Focus_Gained,Focus_Lost事g的序列。即如果?/span>EDT中,应用E序查询currentFocusOwnerQ?/span>=AQ此?/span>=BQ则中间一定按序收到A LostQ?/span>B Gain.q主要通过DefaultKeyboardFocusManager.dispatchEventӞ其是处?/span>Gained(PreparedFocusGained)Ӟ如果发现currentFocusOwner!=nullQ说明尚未得?/span>LostQ则会主动生成一?/span>Lost发布Q处理后l箋处理Gained?/span>
2. 如果是在同一个重量lg间做轻量U组仉切换Q每一?/span>focus event实例上可以获取到准确?/span>oppositeQ?/span>source.q主要通过DefaultKeyboardFocusManager.dispatchEvent?/span>KeyboardFocusManager.retargetEventӞd控制U正可能的不准确?/span>source,opposite?/span>
3. Focus Event支持temporary应用Q尤其体现在当整?/span>window失去焦点Q当再次获取焦点时将恢复?/span>window的最后聚焦组件。应用程序查?/span>focus event.isTemporary==trueӞ则可以确信该lg按既有逻辑很快恢复焦炏V?/span>q主要通过DefaultKeyboardFocusManager.dispatchEvent?/span>KeyboardFocusManager.retargetEventӞ?/span>temporary做了充分的考虑控制?/span>
4. KeyEvent的处理符合规范流E:
a. Ҏrequest旉戳缓存?/span>
b. 解除~存旉?/span>KeyboardFocusManager.isProxyActiveQ?/span>KEYEVENTQ?/span>Q考虑是否交给本地pȝ代理处理?/span>
c. 否则KeyboardFocusManager.注册keyEventDispatchers接手处理?/span>
d. 发布?/span>keyeventl对?/span>componentq行处理Q其中分为如下阶D:
1. Allow the Toolkit to pass this to AWTEventListeners.注册?/span>toolkit下的AWTEventListeners得到处理?/span>
2. KeyboardFocusManager.processKeyEvent用来处理焦点遍历(TAB{?/span>)?/span>
3. Pre-process any special events before delivery。对KEY_PRESSEDQ?/span>KEY_RELEASEDQ组件的容器有机?/span>preProcessKeyEvent?/span>
4. lg的对?/span>Key(Press,Typed,~~)Listenerq行处理?/span>
e. KeyboardFocusManager.?/span>?/span>keyEventPostProcessors接手处理?/span>
f. lgpeer得到Z处理?/span>
在其?/span>1Q?/span>2Q?/span>3点的实现中,Jre1.7版本充分考虑了跨q_性:比如WindowspȝQ如果仅仅是拖动滚动条是不会发出FocusEventQ而其他系l就会发?/span>temporary?/span>FocusEventQ有些系l在重量U对{体间切换可以提供准的oppositeQ但是有些系l则不提供。?/span>jre1.7在实C融合了各个系l的情况Q给Z全面判断qҎ地处理Q保证了跨^台的Focus规范表现?/span>
1. ?/span>Jre1.7版本中,KeyboardFocusManagerQ?/span>DefaultKeyboardFocusManagerq两个类,?/span>Component, WComponentPeercM起完成了focus的主要逻辑实现?/span>
DefaultKeyboardFocusManager是前者的pȝ默认实现。其单例注册?/span>appcontext?/span>,如果需要,E序员可以替代它Q以扩展focus的逻辑实现?/span>
appcontext.put(KeyboardFocusManager.class, new SelfKeyboardFocusManager());
2. Component中提供了requestFocusҎ。而各个组件在初始化时都会安装默认?/span>Listener。当q些Listener收到适当的事仉知后(比如mouse_pressQ即会调用这个方法。该Ҏ首先判断该组件是?/span>focusableQ组件所依托H口是否focusableQ当前聚焦组件的InputVerifier是否验收输入{等Q判断通过后请求重量lg容器?/span>peer.requestFocus?/span>WComponentPeer中提供该requestFocusҎ。该Ҏ首先调用native processSynchronousLightweightTransferQ其会调?/span>KeyboardFocusManager .processSynchronousLightweightTransferrQ作用是如果当前requestlg的重量lg容器正对应当前底层系l的聚焦lgQ而且当前没有M切换焦点?/span>heavyweightRequestsQ这时将直接切换focus变量KeyboardFocusManager.focusOwner?/span>
如果上述调用没有利完成q返?/span>trueQ则会调?/span>native _requestFoucs。该Ҏ会调?/span>KeyboardFocusManager .shouldNativelyFocusHeavyweightQ其作用是完成request登记Qƈ?/span>登记旉戳以正确~存处理后箋q入EDT?/span>Keyevent处理?/span>
Request登记的结构ؓKeyboardFocusManager.heavyweightRequests=
LinkedList< HeavyweightFocusRequest >
-- HeavyweightFocusRequest{
Component heavyweight;
LinkedList<LightweightFocusRequest> lightweightRequestsQ?/span>登记方式分ؓ3U?/span>:
a. 如果发出requestFocus的组件的重量U组件容器正对应当前底层pȝ的聚焦组Ӟ而且当前没有M切换焦点?/span>heavyweightRequest,则增加一?/span>heavyweightRequestq向Post-Qqeue post focus-event?/span>
b. 如果发出requestFocuslg的重量lg容器不对应当前底层系l的聚焦lgQ而且当前没有M切换焦点?/span>heavyweightRequestQ或?/span>当前存在切换焦点?/span>heavyweightRequestQ而最后一?/span>heavyweightRequest. Heavyweight!=当前requestlg的重量lg容器Q则?/span>增加一?/span>heavyweightRequestQƈ同步通知底层pȝq行重量U对{组件的focus切换?/span>
c. 如果当前存在切换焦点?/span>heavyweightRequestQ而且最后一?/span>heavyweightRequest. Heavyweight==当前requestFocus的组件的重量U组件容器,则直接在request.lightweightRequestsq加一?/span>LightweightFocusRequest?/span>
3. EDT在逐个处理AWTEventӞ委托l?/span>EventQueue.dispatchEventQ而委托给Component. dispatchEventImpl,该方法顺序执行下面的代码片段Q?/span>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·
/* focusManagerIsDispatching标志了该event;如果==true意味着?/span>event不会交由KeyboardManagerq行retarget?/span>dispatch。而这两个动作主要完成的功能就是刷?/span>java的全局focus变量。因此可以想?/span>focusManagerIsDispatching==true?/span>Focus_event?/span>focus发生切换后的eventQ?/span>focusManagerIsDispatching==false的是PrepareFocusEvent?/span>*/
if (!e.focusManagerIsDispatching) {//----------PrepareFocusEvent
// Invoke the private focus retargeting method which provides
// lightweight Component supportF
/*通过retargetFocusQ处理之前注册的requesthQ最l激发出合适的CausedFocusEventQ交l下面的dispatch.
*/
if (e.isPosted) {
e = KeyboardFocusManager.retargetFocusEvent(e);
e.isPosted = true;
}
// Now, with the event properly targeted to a lightweight
// descendant if necessary, invoke the public focus retargeting
// and dispatching function
/*通过dispatchl注册的DefaultKeyboardFocusManager,最l更Cjava的全局focus变量
*/
if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
dispatchEvent(e))
{
return;
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·
4. MQ?/span>
a. 轻量U组件的Mouse_Press Listener?/span>requestFocus?/span>request通过必要条g查后会在KeyboardFocusManager.heavyweightRequests~存列表登记Q同时在一个列表中登记一个时间戳marker=当前pȝ旉?/span>
b. 每一?/span>KeyEvent都有一个发生时?/span>whenQ这个发生时间认为是AWT-Windows loop底层event形成KeyEvent的时间?/span>EDT在调?/span>KeyboardFocusManager ?/span>dispatchEvent处理一?/span>KeyEvent时只要发?/span>when晚于旉戳缓存列表中登记的第一个时间戳Q就充分说明q是在某焦点切换h发出后发生的键盘事gQ则不应该将q个KeyEvent target到当前全局focus变量Q因而这时暂此KeyEvent~存在另一个列表?/span>
c. Ҏ前面的分析,?/span>requestFocus时有3U情况,一U是新增heavyweightRequestQ同?/span>postl?/span>post-queue一?/span>FocusEventQ一U是新增heavyweightRequestQ同时因求底层系l切换重量对等体?/span>awt-loopC?/span>FocusEventQ这两种情况?/span>request都在列表中期待对?/span>FocusEvent到来再切换焦炏V从登记旉戛_始,?/span>awt-loop到的KeyEventq入EDT旉缓存下来,而一D|间后当期待的FocusEvent?/span>post-queueq入event-queueq要?/span>EDT中处理时Q有充分的条件可以判断出此后再进?/span>EDT?/span>KeyEventQ都臛_?/span>request登记旉戛_发生的,则这时可以完成此request-删除?/span>heavyweightRequest~存条目Q做focus实际切换Qƈ缓存的KeyEvent 及时间戳记录处理掉。而第3U情冉|?/span>requestFocus时可以在最末一?/span>heavyweightRequest上直接追?/span>LightWeightReuquestQ那么当?/span>heavyweightRequest期待?/span>FocusEvent到来Ӟ按前面所q处理完?/span>request,再将后箋LightWeightRequest保存引用C个全局变量KeyboardFocusManager.currentLightweightRequests,再将此刻为止awt-loop至的post-queue的所?/span>event完全flush?/span>event-queue,再把一个要求@环处理所?/span>currentLightweightRequests指向?/span>LightWeightRequests?/span>InvocationEvent post ?/span>event-queue之后。这样当EDT开始处理该InvocationEventӞ有充分的条g可以判断出此后再q入EDT?/span>KeyEventQ都臛_是最后一个后l?/span>LightWeightRequest登记旉戛_发生的,则这时只需按该InvocationEvent执行卛_Q及逐个清理LightWeightRequest完成focus切换及处理时间戳和缓?/span>KeyEvent。如果在循环处理q程中发生对某一个组?/span>requestFocus调用Q这时会Ҏ处理之初currentLightweightRequests中是否只有单?/span>1?/span>request来确定能?/span>processSynchronousLightweightTransferrQ?/span>卛_果有多个Q则q时止processSynchronousLightweightTransferr以防止破坏了切换焦点的顺序?/span>
d. 重量U组件不需要在Mouse_Press Listener request FocusQ当被进?/span>Mouse PressӞ底层pȝ分发一?/span>Focus EventQ当q入EDT处理Ӟ?/span>jre1.7中通过KeyboardFocusManager.retargetUnexpectedFocusEventretargetQ?/span>首先逐个剔除request后进行期待匹?/span>(针对可能的底层分?/span>-post-queue-event-queue中间环节Event的遗漏等例外情Ş)Q如果最后没有一?/span>request匚wQ则直接形成CausedFocusEvent交给后dispatch完成焦点切换。更切地说Q对?/span>jre1.7而言lg聚焦应该都通过requestFocus完成切换Q不通过该方式的聚焦切换?/span>retarget时将归属?/span>Unexpected被处理,而重量lg的这U聚焦正好通过unexpected完成?/span>
5. 最后,个h认ؓjre1.7中存在一个可能的问题Q每?/span>dispatchEvent旉会在retargetFocusEvent?/span>processCurrentLightweightRequestsQ?/span>q样不久破坏?/span>4-c分析的时机逻辑了么Qؓ什么要q样呢?
Java Focus实现U要一
H口pȝ一般包含一个桌?/span>GUI+若干应用E序GUI。每?/span>GUI都由lg构成Q每个组仉可以获得focusQ获?/span>focus的组件将获得之后的键盘事Ӟ而Q意时d有一个组件能获得focus。这个设计适用在当前所有的H口pȝQ而跨各种pȝ?/span>JAVA应用Q其focus的表C要遵循这个设计目标?/span>
JAVA的组件分为重量和轻量lgQ区别在于重量lg实例的成?/span>peer-对等体,其行为紧密依托本地系l的GUI行ؓ函数库来q行实现。比如一?/span>JFRAMEQ当setvisibleӞ会依?/span>peer.showq行屏幕l制行ؓQ该行ؓ会通过本地pȝGUI行ؓ函数库完成;q样一来,当其被点LQ本地系l会依据最初调用本?/span>GUI函数l制时留下的信息Q从而能够经底层处理后(比如该鼠标事g附加peer标记信息Q同时可能经底层分析需要构造出一个可能的focus_gain事gQ则在操作系l层面登记当前聚?/span>GUIlg{)准确底?/span>GUI事gzN给?/span>JVMq程Q该事g因而在jvmq程中的AWT-WindowsU程loop获取刎ͼq过事g提供?/span>peer标记最l确定目标ؓ重量U组?/span>JFRAMEQ因此一?/span>source==JFRAME?/span>AWTEvent被构造出来ƈ最l分zEDTq行后箋处理?/span>
事g机制是程序中家喻h的设计模式了。但是,?/span>java?/span>focus实现中对q个机制g多少有些不那么绝对的清晰J?/span>
个h理解Q事件的含义是某种定义的情况发生了。比如点击鼠标这个动作可以说触发了多个事Ӟ?/span>press,release,click{,分别指发生了鼠标button1按下Q放开Q完成点ȝ情况?/span>button1按下q个事g比v完成点击p更基一些,因ؓ完成点击指的是一个由按下Q放开动作序列l合的情况发生了?/span>
那么对于focusQ?/span>focus_gained,focus_lostq两个事件应该是指某lg获得焦点或失ȝ点的情况发生了,反映在机器里Q应该是某种指向当前聚焦lg的全局变量发生了更新?/span>
然而在Java awt实现里,概念混ؕ出现啦?/span>
如果awt_windows loop Cfocus事gQ一Q这个事件一定是目标向重量lg的;二,此时Q这个事件对于底层系l的对等lgQ?/span>focus_gainded是发生了(底层pȝ标记当前聚焦lg的全局变量已经更新Q底层操作系l没?/span>mess,L在真?/span>focus改变后才分发focus事g)Q然而在java层面Q截臛_awt_windows loop 到底?/span>focus事gq包装成FocusEvent攄?/span>EVENT QUEUEӞjava层面q没有更?/span>jvm里的全局变量。所以我个h认ؓq个时候就不应该包装成FocusEventQ至不应该叫这个名字,应该?/span>PrepareFocusEvent,嘿嘿?/span>
澄清事g机制的概念后Q回头看java focus 要实现的目标?/span>
1. 最单的设计思\是提供一?/span>setfocus调用APIQ该API来更C个全局变量?/span>EDT每次处理一?/span>keyevent根据当前全局变量q行target。最后给各类lg注册合适的事g监听Q比?/span>mouse press listenerQ在listen响应处理中调?/span>setfocus?/span>
要提?/span>setfocus指定某组件聚焦?/span>Setfocus一旦成功返回,该组件将接受后发生的所有的键盘事gQ直到再ơ失ȝ炏V?/span>
然而问题是轻量U组件的容器是一个重量lgQ而在对轻量lg调用setfocus时它的本地对{组件在pȝ中很可能q没有获得焦炏V若实现上只是简单的?/span>java的全局变量更新了,那系l就会出C个聚焦组Ӟ一个是底层pȝ承认的原来的某底层对{组Ӟ一个是java里认为的现在?/span>jtextfield。而本地系l始l把键盘事gz֏到它认可的聚焦组件上Q如果这个聚焦组件属于另外一?/span>C++q程Q那么这些键盘事件就会分发给C++q程Q而不会被JVM?/span>awt-windows loop到。也是_虽然setfocus成功q回了,但ƈ不代表随后的键盘事g?/span>target到这个组件上。所以不能采用这L设计思\?/span>
管如此Q实际上我们的组件的监听一般是?/span>mouse_press上。而这个鼠标按下动作各cd层操作系l处理时一般首先分?/span>mouse_press底层事gQ然后切换焦点,再分?/span>focus事g。随后的键盘事g会在底层切换焦点后分发出厅R假如我们确定下来所?/span>GUI应用只在EDTU程?/span>mouse_press监听处理?/span>setfocusQ实际上不会丢失键盘事g。但是如果我们要在其他情况,比如?/span>worker U程?/span>setfocusQ那?/span>setfocus׃再可靠了?/span>
那么Q根据前面的分析Q现在更改设计,?/span>setfocus处理中调用底?/span>API要求光量容器对应的本地对{组件聚焦ƈ{到它确实聚焦完成了再更?/span>JAVA的全局变量。但q样也有问题。即使底层系l根据底层调用通知更新?/span>focus,马上q会l箋对可能的焦点切换操作响应Q可以认为有一个系l进E在处理外设的响应)Q很有可能别?/span>C++应用在此时再要?/span>focusQ于是接着更C底层?/span>focus登记Q而我们的setfocus调用却是?/span>jvmq程的某U程中,昄q就是个q发的情景,q样Q很有可能我们的Ҏ地对{组件的通知发过dƈq回了,那边底层pȝ马上切换到?/span>C++的某个组?/span>focusQ而我们的U程l箋更新JAVA的全局focus变量Q于是虽?/span>setfocus成功q回了,但ƈ不代表随后的键盘事g?/span>target到这个组件上?/span>
现在看来Q除非我们同步这两个q程Q让pȝq程{待我们的调?/span>setfocus的线E返回,昄那样是不合理的?/span>(JAVA只能服从OSQ不能让OS服从JAVA?/span>---《英雄ؕ语?/span>J)?/span>
鉴于以上的分析,Ҏ无法实现一?/span>setfocus来完成一个切换焦点的原子性操作?/span>jre1.7的实Cؓ不存?/span>setfocusQ而只?/span>requestfocusQ意思是只是这个切换焦点的h登记上但q不q行实际切换focusQ随后等收到相应的事仉知后再处理requestq彻底完成一?/span>focus切换?/span>
2. 聚焦lg后马上获得随后的键盘事g?/span>
隄是按用户的实际想法,mouse_press后,马上p键盘拼写Q键盘的输入应该target?/span>mouse_press?/span>jtextfield。根据前面的分析Q?/span>mouse_press响应?/span>requestfocus/setfocus后ƈ没有意味着切换焦点已经完成。若实现上对于后l的键盘事g只是单地ҎJAVA的那个全局focus变量targetQ则q些键盘事g不?/span>target到期待的lg上?/span>
鉴于以上的分析,jre1.7的实现是requestfocusӞ只要q个h满必要条gQ那么在其返回前qC个时间戳Q在q个旉戳之后在下一?/span>requestfocus旉戳之前,EDT 逐个取的keyevent都将target到该lgq登讎ͼ直到该组件彻底聚焦完成后Q马上把q些keyevent dispatch?/span>
3. 需要支?/span>TAB键等焦点遍历操作?/span>
q一?/span>JAVA有一个遍历模型,如下Q?/span>
具体参照http://java.sun.com/javase/6/docs/api/java/awt/doc-files/FocusSpec.html
该要 求ƈ没有隄。实C只要?/span>keyevent监听QƈҎ规则q行合适处理即可?/span>
Java dnd拖拽实现分析U要
既有?/span>Swinglg都内|了拖拽的支持,是怎么h持呢Q?/span>
首先Q在Windows环境?/span>jvmq程中,一?/span>guiE序启动两个线E:AWT-WINDOWSQ?/span>AWTQ和Event-Dispatch-Thread(EDT)?/span>AWT-WINDOWSU程不断?/span>windows操作pȝ中获?/span>GUI事gq进行初步的底层处理Q其中一些事件会被包装成高?/span>AWTEvent|入一个地方,?/span>EDTU程的处理过E就包括不断在的适当时机从这个地方获取这?/span>AWTEventq进行高U处理?/span>
然后Q拖拽的效果是׃下几?/span>GUI操作事g及相应程序处理完成的?/span>
1. 拖拽开?/span>,拖拽l束?/span>
2. 拖拽q入Q源lg/目的lgQ,拖拽l过Q源lg/目的lgQ,拖拽dQ源lg/目的lgQ,拖拽中鼠标的Ud?/span>
3. 拖拽操作式样变换。即随键盘操作,可以指示3U操作式P剪切式,复制式,链接式?/span>
q些事g发生后,AWTU程即会获取C仉知q处理,底层处理后会包装交给EDTl箋处理。而处理的E序逻辑一般设计ؓQ对于鼠标图标,会被设计为随拖拽的v始及Ud的整个过E中不同事g的发生而发生变化(比如?/span>dragover事g中可能根据当时情况将图标变ؓ一个叉表示不能拖入Q;同时Q对于拖拽源lg和目的组Ӟ随不同的事g通知也会被程序设计ؓ产生不同的变化响应(比如Q拖拽结束的事g处理中可能指令目的组件复制源lg的文字内容)Q从而最l实C拖拽的效果?/span>
最后,看一?/span>JRE7中拖拽的实现cR?/span>
Swing?/span>JComponentlg一些功能委托给其成?/span>ComponentUI代理。比?/span>JTextField在构造方法中即会通过UIManager获得合适的TextUI实例Q?/span>UIManager根据当?/span>look and feel讄获取Q(此后JTextField?/span>paintҎ会调用该UI实例?/span>updateҎ从而完成组件绘画)Qƈ调用?/span>UI实例?/span>installUIҎQ在installUI中则包括l?/span>JTextField对象安装一些监听器Q安?/span>TransferHandlerq个支持拖拽的关键成员,它包?/span>DragHandler,DropHandler两个内部c,而这两个内部cL实现拖拽效果的核心逻辑Q安?/span>droptargetQ?/span>
Swing?/span>uicd分在几个包中Q其?/span>javax.swing.plaf中存放一些接口;javax.swing.plaf.basic中存攑֯接口的基本实玎ͼ卛_U?/span>LAF的通用实现Q?/span>javax.swing.plaf.metal中存?/span>java默认LAF实现Q另外还?/span>javax.swing.plaf.multi用来实现多个LAF的综合效果实玎ͼjavax.swing.plaf.synth用来实现可通过配置xml文g更换颜色{皮肤的实现?/span>
对于拖拽斚wQ?/span>BasicUI?/span>installUI中一般会对组件安?/span>mouseListenerQ?/span>
editor.addMouseListener(dragListener);
editor.addMouseMotionListener(dragListener);
?/span>dragListener监听发生在lg上的鼠标事gQ当发现可能是新启动的拖拽鼠标动作ƈ且组?/span>dragEnableӞ则立刻通知DragRecognitionSupport单例q行lg拖拽识别。该support单例L别鼠标动作本w,认是组件拖拽开始,再通知lg?/span>TransferHandler成员对象q行拖拽初始化,?/span>Q?/span>l其辨别headless环境?/span>action支持后将初始化徏立ƈ委托调用TransferHandler.SwingDragGestureRecognizer的全局单例(成员包括全局单例dragsource?/span>draghandler对象)Q该实例注册拖拽识别listener及设|?/span>sourceAction,最l将通知TransferHandler.DragHandler对象?/span>dragGestureRecognized。在该方法中Q将创徏transferable及初始化autoscrollQƈ通过dragSource全局单例完成创徏DragContext,q获取及初始?/span>DragContextPeer全局单例Q给该单例注册上?/span>component作ؓ拖拽 triggerQ以?/span>native code可以在处理底层事件时Q可以通过x,y判定是否containsQ从而羃事件处理范_Qƈ通知DragContextPeer单例拖拽开始,?/span>DragContextPeer单例则会调用底层native codeq行q一步的处理?/span>
此后AWTU程通过windows-api获取到系l底层的各种拖拽事gq进行底层处理,处理q程会随时引用DragContextPeer单例Q处理逻辑包括ҎtriggerqoQ,q最l通知该单例合适的事g通知?/span>Peer会将q些事g装成合适的DragEventq提交给EDT处理Q提交后促?/span>AWTU程模拟{待EDT处理完该事g?/span>EDT的处理逻辑是将事g交给拖拽开始时l组件创建的DragContext处理Q而该context对象的处理则会调用其dragHandler成员的对应方法进行事件处理,以及l?/span>dragSource单例相应的监听通知Q最?/span>updateCurrentCursor{,最?/span>EDTq回?/span>AWT,peer处理q回l?/span>native codel箋处理?/span>
当拖拽开始后Q鼠标图标在一?/span>swinglg上游晃时Q首?/span>windows会对光层容器(?/span>jwindowsQ视作拖拽源Q经native codeqo后,如果?/span>swinglg是此ơ拖?/span>triggerQ源Q,?/span>DragContextPeer能得?/span>dragover的通知Q进而进行后l处理;同时从另一个方面,q个也被视作一个拖拽目的,?/span>AWTU程q会Ҏơ拖拽启动徏立一?/span>DropContextPeerQؓ来支持q发Q,q调用该peer?/span>handle事gҎQ该Ҏ会将此底层事件包装后提交l?/span>EDTq促?/span>AWT{待Q?/span>EDT处理该事件时?/span>track到该事g发生时的层lgQ以及事件发生的坐标位置Q由层容器lg?/span>LightweightDispatcherq行初步处理。这个处理将p事g产生CӞ?/span>event source从containerQ比?/span>JFRAMEQ变为精的jcomponent(比如JTextField),同时对于eventId=dragover的事Ӟ有可能根据具体情况再增加dragExitQ?/span>dragEnter两个事gQ比如对jframeH体?/span>dragoverQ但鼠标实际是从一?/span>jtextfield到另外一?/span>jtextfieldQ,q些_的事件的处理会再ơ回?/span>DropContextPeer中得到对应的process。这时的process会处理本w的一些成员数据(当前context,当前droptarget{)Q再事件委托给source jcomponent?/span>droptargetq行处理Q如果已安装Q,此时的处理将是传递给对应lg?/span>drophandlerq行处理Q同时会通知droptarget的注册监听,最?/span>initializeAutoscrolling{?/span>drophandler在处理过E中?/span>event可以q行accept?/span>rejectQ这两个动作会再去调?/span>dropcontextq行处理Qƈ最l{?/span>peer处理成员数据。最?/span>edtq回?/span>AWT,peer handle处理l束q回native code当前drop action|native codel箋处理?/span>
通过以上的分析,如果需要定?/span>swinglg的拖拽逻辑Q一个比较基的入口是transferhandlerQ因为所有事件的处理都将l过其两个内部类逻辑处理(DragHandler?/span>DropHandler)Q?/span>swing包中?/span>TransferHandler的具体实玎ͼ是这两个内部cȝҎ都把一些控制划分给?/span>componet的一些属性设|或TransferHandler本n的回调方法,所以只需对组件设|合适的属性,或承ƈoverride TransferHandler合适的Ҏq给?/span>swinglg重新setTransferHandlerQ即可以定制新的逻辑。如果需要更深层ơ的定制Q则需要细致考虑上述分析Q选择合适的定制炏V?/span>
1.重用HTTPCLIENT实例?/font>
一般说来,一个通讯lgQ甚臌一个应用Y件就始终l持一个HttpClient对象实例存在。但是如果你的应用很E|才用到它,而且q不允许q么一个实例一直存在,那么Q这里强烈徏议,每次在disposing 它之前,一定要昑ּ地shut down 它的 multithreaded connection manager 。这样做是确保连接池里的connection得到释放?/span>
2.持箋q接不关闭?/span>
httpclientL量重用q接。它不要求Q何配|,默认情况下就是这栗某些情况下Q这可能Dq接泄漏Q而耗尽资源。禁用持l连接最单的Ҏ是提供或扩展一个connection managerQ在releaseConnection被调用的时候,q个connection manager把connection真正关闭?/span>?/span>
3.http method的ƈ发执行。(实际上是在对1.延Q?br /> 如果应用E序逻辑允许q发执行多个HTTPhQ(例如对多个服务器的多个ƈ发请求,或对同一个服务器代表不同用户w䆾的多个请求) Q应用程序可以ؓ每一个HTTP session开启一个专门的U程Q这L设计自然带来显著的性能提升?而当使用一个线E安全的q接理器,如multithreadedhttpconnectionmanagerӞHttpClient能保证线E安全。这P多个U程可以׃nq么一个线E安全的HttpClient实例。请注意Q应用程序的每个各自执行的线E必M用各自的httpmethod实例Qƈ且可配置各自的httpstate实例?或hostconfiguration实例Q代表一个特定的会话状态和L配置Q。这个共享的HttpClient和其标配的multithreadedhttpconnectionmanagerؓ各线E带来最高的性能?/span> HTTP提供HTTP 100 (Continue) 状态,是指客户端发送一?#8220;预期HTTP100”的请求headerQ如果服务器响应100说明接受该客LhQ则客户端l发送body。是啊,如果服务器通过headerp判断是否响应100q是拒绝Q那么不通过q条路就直接发送bodyQ尤其是大bodyQ真的太费资源了。授权拒l是最典型的情形了。因此强烈徏议对于那些有HTTP认证要求的服务器使用“预期HTTP100”q种通讯方式。注意如果通讯要通过代理Q程序员要处理可能的警告Q因Z些老的HTTP1.0代理服务器不能正处?#8220;预期HTTP100”通讯方式?br />
6. 钝态连接的?/span>
HTTP协议规范允许客户端或服务器端不知会对方,在Q意时刻都可以l止一个连接(指底层网l连接)Q因此这个connection变成了钝态或者说陈旧了,反正是不能用了。在默认情况下,HttpClient 会在执行HTTPҎ前检查要用的q接Q看看是否是钝态。这个检查需耗时15-30msQ根据用的jre不同而不同。关闭这个检查能带来部分的性能提升Q尤其对于小负蝲responseq种通讯。但是关闭后可能面底层q接已关闭的异常Q服务器d关闭但没通知客户端)?/span>
7.Cookie 处理
如果一个应用程序,例如web spiderQ不需要和服务器保持会话状态,则禁用cookie会带来轻微的性能提升?br />