首页 文章 精选 留言 我的

精选列表

搜索[记忆机制],共10000篇文章
优秀的个人博客,低调大师

Android应用程序消息处理机制(Looper、Handler)分析(4)

2. 消息的发送 应用程序的主线程准备就好消息队列并且进入到消息循环后,其它地方就可以往这个消息队列中发送消息了。我们继续以文章开始介绍的Android应用程序启动过程源代码分析一文中的应用程序启动过为例,说明应用程序是如何把消息加入到应用程序的消息队列中去的。 在Android应用程序启动过程源代码分析这篇文章的Step 30中,ActivityManagerService通过调用ApplicationThread类的scheduleLaunchActivity函数通知应用程序,它可以加载应用程序的默认Activity了,这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: publicfinalclassActivityThread{ ...... privatefinalclassApplicationThreadextendsApplicationThreadNative{ ...... //weusetokentoidentifythisactivitywithouthavingtosendthe //activityitselfbacktotheactivitymanager.(mattersmorewithipc) publicfinalvoidscheduleLaunchActivity(Intentintent,IBindertoken,intident, ActivityInfoinfo,Bundlestate,List<ResultInfo>pendingResults, List<Intent>pendingNewIntents,booleannotResumed,booleanisForward){ ActivityClientRecordr=newActivityClientRecord(); r.token=token; r.ident=ident; r.intent=intent; r.activityInfo=info; r.state=state; r.pendingResults=pendingResults; r.pendingIntents=pendingNewIntents; r.startsNotResumed=notResumed; r.isForward=isForward; queueOrSendMessage(H.LAUNCH_ACTIVITY,r); } ...... } ...... } 这里把相关的参数都封装成一个ActivityClientRecord对象r,然后调用queueOrSendMessage函数来往应用程序的消息队列中加入一个新的消息(H.LAUNCH_ACTIVITY),这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: publicfinalclassActivityThread{ ...... privatefinalclassApplicationThreadextendsApplicationThreadNative{ ...... //ifthethreadhasn'tstartedyet,wedon'thavethehandler,sojust //savethemessagesuntilwe'reready. privatefinalvoidqueueOrSendMessage(intwhat,Objectobj){ queueOrSendMessage(what,obj,0,0); } ...... privatefinalvoidqueueOrSendMessage(intwhat,Objectobj,intarg1,intarg2){ synchronized(this){ ...... Messagemsg=Message.obtain(); msg.what=what; msg.obj=obj; msg.arg1=arg1; msg.arg2=arg2; mH.sendMessage(msg); } } ...... } ...... } 在queueOrSendMessage函数中,又进一步把上面传进来的参数封装成一个Message对象msg,然后通过mH.sendMessage函数把这个消息对象msg加入到应用程序的消息队列中去。这里的mH是ActivityThread类的成员变量,它的类型为H,继承于Handler类,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: publicfinalclassActivityThread{ ...... privatefinalclassHextendsHandler{ ...... publicvoidhandleMessage(Messagemsg){ ...... switch(msg.what){ ...... } ...... } ...... } 这个H类就是通过其成员函数handleMessage函数来处理消息的了,后面我们分析消息的处理过程时会看到。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966600,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(18)

Step 16.InputDispatcher.prepareDispatchCycleLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::prepareDispatchCycleLocked(nsecs_tcurrentTime, constsp<Connection>&connection,EventEntry*eventEntry,constInputTarget*inputTarget, boolresumeWithAppendedMotionSample){ ...... //Resumethedispatchcyclewithafreshlyappendedmotionsample. //Firstwecheckthatthelastdispatchentryintheoutboundqueueisforthesame //motioneventtowhichweappendedthemotionsample.Ifwefindsuchadispatch //entry,andifitiscurrentlyinprogressthenwetrytostreamthenewsample. boolwasEmpty=connection->outboundQueue.isEmpty(); if(!wasEmpty&&resumeWithAppendedMotionSample){ ...... return; } //Thisisanewevent. //Enqueueanewdispatchentryontotheoutboundqueueforthisconnection. DispatchEntry*dispatchEntry=mAllocator.obtainDispatchEntry(eventEntry,//incrementsref inputTarget->flags,inputTarget->xOffset,inputTarget->yOffset); ...... //Enqueuethedispatchentry. connection->outboundQueue.enqueueAtTail(dispatchEntry); //Iftheoutboundqueuewaspreviouslyempty,startthedispatchcyclegoing. if(wasEmpty){ ...... startDispatchCycleLocked(currentTime,connection); } } 在开始处理键盘事件之前,这个函数会检查一下传进来的参数connection中的outboundQueue事件队列是否为空,如果不为空,就要看看当前要处理的事件和outboundQueue队列中的最后一个事件是不是同一个motion事件,如果是的话,并且从上面传进来的resumeWithAppendedMotionSample参数为true,这时候就要以流水线的方式来处理这些motion事件了。在我们这个情景中,要处理的是键盘事件,因此在上面Step 12中传进来的resumeWithAppendedMotionSample参数为false,因此,我们略过这种情况。 接下来,就会把当前的键盘事件封装成一个DispatchEntry对象,然后添加到connection对象的outboundQueue队列中去,表示当前键盘事件是一个待处理的键盘事件。 当connection中的outboundQueue事件队列不为空,即wasEmpty为false时,说明当前这个Activity窗口正在处键盘事件了,因此,就不需要调用startDispatchCycleLocked来启动Activity窗口来处理这个事件了,因为一旦这个Activity窗口正在处键盘事件,它就会一直处理下去,直到它里的connection对象的outboundQueue为空为止。当connection中的outboundQueue事件队列为空时,就需要调用startDispatchCycleLocked来通知这个Activity窗口来执行键盘事件处理的流程了。 Step 17.InputDispatcher.startDispatchCycleLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::startDispatchCycleLocked(nsecs_tcurrentTime, constsp<Connection>&connection){ ...... DispatchEntry*dispatchEntry=connection->outboundQueue.headSentinel.next; //Markthedispatchentryasinprogress. dispatchEntry->inProgress=true; //Updatetheconnection'sinputstate. EventEntry*eventEntry=dispatchEntry->eventEntry; ...... //Publishtheevent. status_tstatus; switch(eventEntry->type){ caseEventEntry::TYPE_KEY:{ KeyEntry*keyEntry=static_cast<KeyEntry*>(eventEntry); //Applytargetflags. int32_taction=keyEntry->action; int32_tflags=keyEntry->flags; //Publishthekeyevent. status=connection->inputPublisher.publishKeyEvent(keyEntry->deviceId,keyEntry->source, action,flags,keyEntry->keyCode,keyEntry->scanCode, keyEntry->metaState,keyEntry->repeatCount,keyEntry->downTime, keyEntry->eventTime); ...... break; } ...... } //Sendthedispatchsignal. status=connection->inputPublisher.sendDispatchSignal(); ...... } 这个函数主要围绕传进来的Connection对象做两件事情,一是从它的outboundQueue队列中取出当前需要处理的键盘事件,然后把这个事件记录在它的内部对象inputPublisher中,二是通过它的内部对象inputPublisher通知它所关联的Activity窗口,现在有键盘事件需要处理了。第一件事情是通过调用它的InputPublisher对象的publishKeyEvent函数来完成的,而第二件事情是通过调用它的InputPublisher对象的sendDispatchSignal来完成的。我们先来看InputPublisher的成员函数publishKeyEvent的实现,然后再回来分析它的另外一个成员函数sendDispatchSignal的实现。 Step 18. InputPublisher.publishKeyEvent 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::publishKeyEvent( int32_tdeviceId, int32_tsource, int32_taction, int32_tflags, int32_tkeyCode, int32_tscanCode, int32_tmetaState, int32_trepeatCount, nsecs_tdownTime, nsecs_teventTime){ ...... status_tresult=publishInputEvent(AINPUT_EVENT_TYPE_KEY,deviceId,source); if(result<0){ returnresult; } mSharedMessage->key.action=action; mSharedMessage->key.flags=flags; mSharedMessage->key.keyCode=keyCode; mSharedMessage->key.scanCode=scanCode; mSharedMessage->key.metaState=metaState; mSharedMessage->key.repeatCount=repeatCount; mSharedMessage->key.downTime=downTime; mSharedMessage->key.eventTime=eventTime; returnOK; } 这个函数主要就是把键盘事件记录在InputPublisher类的成员变量mSharedMessage中了,这个mSharedMessage成员变量指向的是一个匿名共享内存。 这个匿名共享内存是什么时候创建的呢?前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),在把Server端的InputChannel封装成一个 Connection对象时,会调用它的initialize成员函数来执行一些初始化工作,就是在这个时候创建这个匿名共享内存的了: sp<Connection>connection=newConnection(inputChannel); status_tstatus=connection->initialize(); 我们来看一下这个initialize函数的实现,它定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::initialize(){ ...... intashmemFd=mChannel->getAshmemFd(); intresult=ashmem_get_size_region(ashmemFd); ...... mAshmemSize=(size_t)result; mSharedMessage=static_cast<InputMessage*>(mmap(NULL,mAshmemSize, PROT_READ|PROT_WRITE,MAP_SHARED,ashmemFd,0)); ...... mPinned=true; mSharedMessage->consumed=false; returnreset(); } InputPublisher的成员变量mChannel就是指注册在InputDispatcher中的Server端InputChannel了。我们知道,这个InputChannel除了拥有一个反向管道的读端文件描述符和一个前向管道的写端文件描述符之后,还有一个匿名共享文件描述符,这个匿名共享文件描述符就是用来创建匿名共享内存mSharedMessage的了。 这个匿名共享内存mSharedMessage的作用是什么呢?原来,在InputChannel中,前向管道和反向管道的作用只是用来在Server端和Client端之间相互通知有事件发生了,但是具体是什么样的事件,还需要去读取这个匿名共享内存的内容才知道。前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 14中(InputChannel.openInputChannelPair)创建Server端和Client端的InputChannel对时,创建一个匿名共享内存,这个匿名共享内存有两个文件描述符同时指向它,其中一个放在Server端的InputChannel中,另外一个放在Client端的InputChannel中。这样,当InputDispatcher通过Server端的InputChannel的前向管道来通知Client端有键盘事件发生时,Client端只要通过它的InputChannel中的匿名共享内存文件描述符去读取匿名共享内存中的内容,就可以知道发生了什么事情了。 回到Step 17中,接下来就是调用InputPublisher的成员函数sendDispatchSignal来通知Activity窗口处理键盘事件了。 Step 19.InputPublishe.sendDispatchSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::sendDispatchSignal(){ ...... mWasDispatched=true; returnmChannel->sendSignal(INPUT_SIGNAL_DISPATCH); } 这个函数很简单,它通过调用内部成员变量mChannel的sendSignal函数来通知相应的Activity窗口来处理键盘事件。 Step 20. InputChannel.sendSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputChannel::sendSignal(charsignal){ ssize_tnWrite; do{ nWrite=::write(mSendPipeFd,&signal,1); }while(nWrite==-1&&errno==EINTR); if(nWrite==1){ ...... returnOK; } return-errno; } 这里所谓的发送信号通知,其实是通过向其内部一个管道的写端写入一个字符来实现的。前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),它把一个InputChannel注册到应用程序主线程中的Looper对象中,然后应用程序的主线程就通过这个Looper对象睡眠等待在这个InputChannel中的前向管道中有新的内容可读了,这里的mSendPipeFd就是对应这个前向管道的写端。现在既然向这个前向管道的写端写入新的内容了,于是,应用程序的主线程就被唤醒了。 在前面分析应用程序注册键盘消息接收通道过程的Step 21中,我们也说过,当应用程序的主线程因为这个InputChannel中的前向管道的写端唤醒时,NativeInputQueue的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行NativeInputQueue的成员函数handleReceiveCallback。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966636,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(11)

Step 8.NativeInputManager.setInputWindows 这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中: voidNativeInputManager::setInputWindows(JNIEnv*env,jobjectArraywindowObjArray){ Vector<InputWindow>windows; jsizelength=env->GetArrayLength(windowObjArray); for(jsizei=0;i<length;i++){ jobjectinputTargetObj=env->GetObjectArrayElement(windowObjArray,i); if(!inputTargetObj){ break;//foundnullelementindicatingendofusedportionofthearray } windows.push(); InputWindow&window=windows.editTop(); boolvalid=populateWindow(env,inputTargetObj,window); if(!valid){ windows.pop(); } env->DeleteLocalRef(inputTargetObj); } mInputManager->getDispatcher()->setInputWindows(windows); } 这个函数首先将Java层的Window转换成C++层的InputWindow,然后放在windows向量中,最后将这些输入窗口设置到InputDispatcher中去。 Step 9. InputDispatcher.setInputWindows 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::setInputWindows(constVector<InputWindow>&inputWindows){ ...... {//acquirelock AutoMutex_l(mLock); //Clearoldwindowpointers. sp<InputChannel>oldFocusedWindowChannel; if(mFocusedWindow){ oldFocusedWindowChannel=mFocusedWindow->inputChannel; mFocusedWindow=NULL; } mWindows.clear(); //Loopovernewwindowsandrebuildthenecessarywindowpointersfor //trackingfocusandtouch. mWindows.appendVector(inputWindows); size_tnumWindows=mWindows.size(); for(size_ti=0;i<numWindows;i++){ constInputWindow*window=&mWindows.itemAt(i); if(window->hasFocus){ mFocusedWindow=window; break; } } ...... }//releaselock ...... } 这里InputDispatcher的成员变量mFocusedWindow就代表当前激活的窗口的。这个函数首先清空mFocusedWindow,然后再通过一个for循环检查当前的输入窗口中的哪一个窗口是获得焦点的,获得焦点的输入窗口即为当前激活的窗口。 这样,InputManager就把当前激活的Activity窗口保存在InputDispatcher中了,后面就可以把键盘消息分发给它来处理。 回到Step 1中的ViewRoot.setView函数中,接下来就调用下面语句来注册键盘消息接收通道的一端到InputManager中去: mInputChannel=newInputChannel(); try{ res=sWindowSession.add(mWindow,mWindowAttributes, getHostVisibility(),mAttachInfo.mContentInsets, mInputChannel); }catch(RemoteExceptione){ ...... }finally{ ...... } 前面说过,这里的sWindowSession是WindowManagerService内部类Session的一个远程接口,通过它可以进入到WindowManagerService中去。 Step 10.WindowManagerService.Session.add 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... privatefinalclassSessionextendsIWindowSession.Stub implementsIBinder.DeathRecipient{ ...... publicintadd(IWindowwindow,WindowManager.LayoutParamsattrs, intviewVisibility,RectoutContentInsets,InputChanneloutInputChannel){ returnaddWindow(this,window,attrs,viewVisibility,outContentInsets, outInputChannel); } ...... } ...... } 这里调用WindowManagerService类的addWindow函数来进一步执行操作。 Step 11.WindowManagerService.addWindow 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... publicintaddWindow(Sessionsession,IWindowclient, WindowManager.LayoutParamsattrs,intviewVisibility, RectoutContentInsets,InputChanneloutInputChannel){ ...... WindowStatewin=null; synchronized(mWindowMap){ ...... win=newWindowState(session,client,token, attachedWindow,attrs,viewVisibility); ...... if(outInputChannel!=null){ Stringname=win.makeInputChannelName(); InputChannel[]inputChannels=InputChannel.openInputChannelPair(name); win.mInputChannel=inputChannels[0]; inputChannels[1].transferToBinderOutParameter(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel); } ...... } ...... } ...... } 这里的outInputChannel即为前面在Step 1中创建的InputChannel,它不为NULL,因此,这里会通过InputChannel.openInputChannelPair函数来创建一对输入通道,其中一个位于WindowManagerService中,另外一个通过outInputChannel参数返回到应用程序中: inputChannels[1].transferToBinderOutParameter(outInputChannel); 创建输入通道之前,WindowManagerService会为当前Activity窗口创建一个WindowState对象win,用来记录这个Activity窗口的状态信息。当创建这对输入管道成功以后,也会把其中的一个管道保存在这个WindowState对象win的成员变量mInputChannel中,后面要注销这个管道的时候,就是从这个WindownState对象中取回这个管道的: win.mInputChannel=inputChannels[0]; 接下来我们就看一下InputChannel.openInputChannelPair函数的实现。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966627,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(5)

Step 19. EventHub.openPlatformInput 这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中: boolEventHub::openPlatformInput(void) { ...... res=scanDir(device_path); if(res<0){ LOGE("scandirfailedfor%s\n",device_path); } returntrue; } 这个函数主要是扫描device_path目录下的设备文件,然后打开它们,这里的变量device_path定义在frameworks/base/libs/ui/EventHub.cpp文件开始的地方: staticconstchar*device_path="/dev/input"; 在设备目录/dev/input中,一般有三个设备文件存在,分别是event0、mice和mouse0设备文件,其中,键盘事件就包含在event0设备文件中了。 Step 20.EventHub.scanDir 这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中: intEventHub::scanDir(constchar*dirname) { chardevname[PATH_MAX]; char*filename; DIR*dir; structdirent*de; dir=opendir(dirname); if(dir==NULL) return-1; strcpy(devname,dirname); filename=devname+strlen(devname); *filename++='/'; while((de=readdir(dir))){ if(de->d_name[0]=='.'&& (de->d_name[1]=='\0'|| (de->d_name[1]=='.'&&de->d_name[2]=='\0'))) continue; strcpy(filename,de->d_name); openDevice(devname); } closedir(dir); return0; } 根据上面一步的分析,这个函数主要就是调用openDevice函数来分别打开/dev/input/event0、/dev/input/mice和/dev/input/mouse0三个设备文件了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/p13,如需转载请自行联系原作者

优秀的个人博客,低调大师

“爱思助手”曝为iOS木马:可绕过苹果DRM机制

一款新的iOS木马已在国内曝光,它可以通过PC感染未越狱的iOS设备,而无需利用企业证书。Palo Alto Networks指出,其名叫“爱思助手”(AceDeceiver),目前正在影响我国的iOS用户。“爱思助手”利用了苹果数字版权管理(DRM)上的FairPlay漏洞来感染iOS设备。 据Palo Alto Networks所述,其采用了所谓的“FairPlay中间人”技术,此前曾被用于传播盗版iOS应用。 该技术借助假冒iTunes软件和欺骗性质的授权代码来将应用安装到iOS设备上,但现在也被用于传播“爱思助手”(AceDeceiver)恶意软件。 苹果允许用户通过计算机上的iTunes客户端里的App Store购买和下载iOS应用,然后将之安装到自己的iOS设备上。 每款安装到iOS设备上的应用都需要提供一个切实购买的授权码,而在FairPlay中间人攻击中,攻击者在App Store上购买了一款应用,然后拦截并保存了该授权码。 之后,他们用开发出来的PC软件来模拟iTunes客户端的行为,欺骗iOS设备相信应用是由受害人购买的。 用户因而可以安装他们实际未支付过的应用,但软件开发者也能够借此向无知用户的设备偷偷安装恶意软件。 从2015年7月到2016年2月,已经有三款“AceDeceiver”应用被上传到了官方iOS App Store。其冒充为一款壁纸应用,从而让攻击者拿到了一个用于攻击的虚假授权代码。 它还有一款配套的“爱思助手”Windows iPhone管理应用,虽自称可提供系统备份和清理服务,但却会给连接的设备安装恶意的iOS应用。 其设计伪装成一个可提供免费内容的第三方应用市场,然后引诱用户递交他们的Apple ID和密码,并将相关信息上传到AceDeceiver服务器。 尽管苹果已经在2月份移除了它,但由于仍然可以用授权码来在iOS设备上安装虚假应用,其攻击仍然持续着。Palo Alto Networks警告称,尽管当前仅限于国内,但类似恶意软件或很快传播到其它地区。 作者:蓝雨泪 来源:51CTO

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册