- 浏览: 21493280 次
- 性别:
- 来自: 杭州
最新评论
-
ZY199266:
配置文件还需要额外的配置ma
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
ZY199266:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我又一个问题就是 如果像你的这种形式写。配置文件还需要额外的 ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
y1210251848:
你的那个错误应该是项目所使用的目标框架不支持吧
log4net配置(web中使用log4net,把web.config放在单独的文件中)
Android 2.3 SD卡挂载流程浅析(六)
1.首先找到系统设置的源码。
路径:AndroidSorceCode2.3/package/app/Settings/src/com/android/settings/deviceinfo/Memory.java
该文件其实就是我们打开系统设置-存储的一个界面,如下图:
在设置中点击存储便进入到Memory的界面,如下:
如果已经插入SD卡并且系统已经挂载了的话,这里会有显示。也就是说我们的最终目标在这里,SD卡的挂载信息是如何传递到这里的。我们继续回到Memory.java文件中。我们要如何知道一步该做什么呢?我们先在Eclipse中的logcat添加TAG名为Memory的TAG,然后插入SD卡,我们会发现有以下log输出:
这就是我们需要的关键点,因为这句log是从Memory.java中输出的,因此我们首先要找到该log的出处:
- <spanstyle="font-size:18px;">StorageEventListenermStorageListener=newStorageEventListener(){
- @Override
- publicvoidonStorageStateChanged(Stringpath,StringoldState,StringnewState){
- Log.i(TAG,"Receivedstoragestatechangednotificationthat"+
- path+"changedstatefrom"+oldState+
- "to"+newState);
- updateMemoryStatus();
- }
- };</span>
从以上代码中可以知道,这里就是输出关键log的地方,换句话说当我们插入SD卡的时候,系统触发这个onStroageStateChanged()方法,在该方法中一并执行了updateMemoryStatus()方法,我们跟踪进入updateMemoryStatus()方法看看,根据这名字我们大致可以猜测其作用是更新存储设备的状态信息:
- privatevoidupdateMemoryStatus(){
- Stringstatus=Environment.getExternalStorageState();
- StringreadOnly="";
- if(status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)){
- status=Environment.MEDIA_MOUNTED;
- readOnly=mRes.getString(R.string.read_only);
- }
- if(status.equals(Environment.MEDIA_MOUNTED)){
- if(!Environment.isExternalStorageRemovable()){
- //Thisdevicehasbuilt-instoragethatisnotremovable.
- //Thereisnoreasonfortheusertounmountit.
- if(mSdMountToggleAdded){
- mSdMountPreferenceGroup.removePreference(mSdMountToggle);
- mSdMountToggleAdded=false;
- }
- }
- try{
- Filepath=Environment.getExternalStorageDirectory();
- StatFsstat=newStatFs(path.getPath());
- longblockSize=stat.getBlockSize();
- longtotalBlocks=stat.getBlockCount();
- longavailableBlocks=stat.getAvailableBlocks();
- mSdSize.setSummary(formatSize(totalBlocks*blockSize));
- mSdAvail.setSummary(formatSize(availableBlocks*blockSize)+readOnly);
- mSdMountToggle.setEnabled(true);
- mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
- mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
- }catch(IllegalArgumentExceptione){
- //thiscanoccuriftheSDcardisremoved,butwehaven'treceivedthe
- //ACTION_MEDIA_REMOVEDIntentyet.
- status=Environment.MEDIA_REMOVED;
- }
- }else{
- mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
- mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
- if(!Environment.isExternalStorageRemovable()){
- if(status.equals(Environment.MEDIA_UNMOUNTED)){
- if(!mSdMountToggleAdded){
- mSdMountPreferenceGroup.addPreference(mSdMountToggle);
- mSdMountToggleAdded=true;
- }
- }
- }
- if(status.equals(Environment.MEDIA_UNMOUNTED)||
- status.equals(Environment.MEDIA_NOFS)||
- status.equals(Environment.MEDIA_UNMOUNTABLE)){
- mSdMountToggle.setEnabled(true);
- mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
- mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
- }else{
- mSdMountToggle.setEnabled(false);
- mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
- mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
- }
- }
- Filepath=Environment.getDataDirectory();
- StatFsstat=newStatFs(path.getPath());
- longblockSize=stat.getBlockSize();
- longavailableBlocks=stat.getAvailableBlocks();
- findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks*blockSize));
- }
果然不出我们所料,这里也就是真正更新设置-存储界面里面信息的方法。
2.跟着源码中的Memory.java顺藤摸瓜
我们回到StorageEventListener实例化对象的地方:
- StorageEventListenermStorageListener=newStorageEventListener(){
- @Override
- publicvoidonStorageStateChanged(Stringpath,StringoldState,StringnewState){
- Log.i(TAG,"Receivedstoragestatechangednotificationthat"+
- path+"changedstatefrom"+oldState+
- "to"+newState);
- updateMemoryStatus();
- }
- };
通过代码我们可以知道,StorageEventListener是一个抽象类,在这里通过实例化自己的对象并用匿名内部类实现了自己定义中的抽象方法。我接着回到Memory.java中的onCreate()方法中:
- if(mStorageManager==null){
- mStorageManager=(StorageManager)getSystemService(Context.STORAGE_SERVICE);
- mStorageManager.registerListener(mStorageListener);
- }
在这里我们可以看到,StorageEventListener的对象mStorageListener通过StorageManager的方法registerListener()完成注册。这里我们需要详细了解一下这个注册的过程,因为这里所谓的注册就为后面的触发埋下了伏笔,注册存储事件监听器(StorageEventListener)的目的就是为了在存储设备状态发生改变并触发事件的时候,接收并处理这些事件。
(1).mStorageManager初始化
- mStorageManager=(StorageManager)getSystemService(Context.STORAGE_SERVICE);
- @Override
- publicObjectgetSystemService(Stringname){
- if(getBaseContext()==null){
- thrownewIllegalStateException(
- "SystemservicesnotavailabletoActivitiesbeforeonCreate()");
- }
- if(WINDOW_SERVICE.equals(name)){
- returnmWindowManager;
- }elseif(SEARCH_SERVICE.equals(name)){
- ensureSearchManager();
- returnmSearchManager;
- }
- returnsuper.getSystemService(name);
- }
这里因为不满足if的判断条件,因此会返回调用父类的getSystemService方法。接下来继续跟踪到其父类中的getSystemService方法中查看,这里的Activity继承了ContextThemeWrapper这个类:
- @Override
- publicObjectgetSystemService(Stringname){
- if(LAYOUT_INFLATER_SERVICE.equals(name)){
- if(mInflater==null){
- mInflater=LayoutInflater.from(mBase).cloneInContext(this);
- }
- returnmInflater;
- }
- returnmBase.getSystemService(name);
- }
根据if的判断条件来看,这里还是不会满足判断条件。如果这里我们继续点击getSystemService()方法去跟踪的话我们会发现,我们来到了一个抽象类Context类中。该类中的getSystemService()方法也是一个抽象方法,那么到这里我们已经无法分析了吗?非也非也。如果细心的话我们会发现这里的getSystemService()方法前面有一个mBase对象,该对象是Context的,因为抽象类不可能有自己的实例化对象,因此根据多态性可以知道,这里的mBase肯定是其子类的对象,因此我们需要找到该子类。
(2)getSystemService()峰回路转
我们首先看看这个mBase的定义,直接跳转过去可以看到:
- privateContextmBase;
- ...
- publicContextThemeWrapper(Contextbase,intthemeres){
- super(base);
- mBase=base;
- mThemeResource=themeres;
- }
- @OverrideprotectedvoidattachBaseContext(ContextnewBase){
- super.attachBaseContext(newBase);
- mBase=newBase;
- }
这里只截取了其中部分,但我已经可以看到给mBase赋值的地方有两处,这里该怎么断定呢?按照常理我们先去跟踪ContextThemeWrapper构造方法的调用处,直接在Eclipse对该方法点击右键,选择Open Call Hierarchy,这样就会出现调用该方法的地方,这样一步步跟踪下去似乎越来越乱,因为调转点实在是太多了,因此先就此打住。我们回过头先查看这里的attachBaseContext方法,通过同样的方法(因为自己也是第一次分析,很多东西都不懂,只能自己摸着石头过河,高手请勿见笑)。我们直接跳转会来到Activity中的attach()方法中:
- finalvoidattach(Contextcontext,ActivityThreadaThread,
- Instrumentationinstr,IBindertoken,intident,
- Applicationapplication,Intentintent,ActivityInfoinfo,
- CharSequencetitle,Activityparent,Stringid,
- ObjectlastNonConfigurationInstance,
- HashMap<String,Object>lastNonConfigurationChildInstances,
- Configurationconfig){
- attachBaseContext(context);//这里调用
这里截取了部分代码,只抓取了我们需要的部分,这里发现如果调用了attach()方法的话会传递一个Context的对象,那么我们继续跟踪,在Activity的performLaunchActivity方法中我们发现了attach()方法的调用处:
- activity.attach(appContext,this,getInstrumentation(),r.token,
- r.ident,app,r.intent,r.activityInfo,title,r.parent,
- r.embeddedID,r.lastNonConfigurationInstance,
- r.lastNonConfigurationChildInstances,config);
通过以上代码我们可以发现这里传递了一个appContext参数,跟踪此参数,可以看到:
- ContextImplappContext=newContextImpl();
原来是ContextImpl的对象,原来应该传入的对象是Context的,这里传入的却是ContextImpl的对象,因此不用想我们也知道,ContextImpl肯定是Context的子类,跟踪过去一看,果不其然:
- classContextImplextendsContext
既然ContextImpl继承了Context类,并将自己的对象作为参数传递进去,那么前面的mBase对象就应该是ContextImpl的对象,因此调用的getSystemService()方法也应该在ContextImpl类中有覆写。直接搜索可以找到:
- @Override
- publicObjectgetSystemService(Stringname){
- if(WINDOW_SERVICE.equals(name)){
- returnWindowManagerImpl.getDefault();
- }elseif(LAYOUT_INFLATER_SERVICE.equals(name)){
- synchronized(mSync){
- LayoutInflaterinflater=mLayoutInflater;
- if(inflater!=null){
- returninflater;
- }
- mLayoutInflater=inflater=
- PolicyManager.makeNewLayoutInflater(getOuterContext());
- returninflater;
- }
- }elseif(SENSOR_SERVICE.equals(name)){
- returngetSensorManager();
- ......
- }elseif(STORAGE_SERVICE.equals(name)){
- returngetStorageManager();//这里是我们所需要的
- }elseif(USB_SERVICE.equals(name)){
- returngetUsbManager();
- }elseif(VIBRATOR_SERVICE.equals(name)){
- returngetVibrator();
- }
- ......
- returnnull;
- }
原来,我们梦里寻她千百度,蓦然回首,getSystemService()竟然藏在此处。因为我们在Memory.java中传递过来的是STORAGE_SERVICE,因此这里会执行getStorageManager()方法。
(3).继续探索getStorageManager()
继续跟踪getStorageManager()方法我们会看到:
- privateStorageManagergetStorageManager(){
- synchronized(mSync){
- if(mStorageManager==null){
- try{
- mStorageManager=newStorageManager(mMainThread.getHandler().getLooper());
- }catch(RemoteExceptionrex){
- Log.e(TAG,"FailedtocreateStorageManager",rex);
- mStorageManager=null;
- }
- }
- }
- returnmStorageManager;
- }
通过该方法可以看到,返回的是一个StorageManager对象。但我们需要关注的是StorageManager(mMainThread.getHandler().getLooper())在这个方法中传递的参数是ActivityThread的handler中的looper。继续跟踪此方法就可以来到StorageManager的带参构造函数:
- publicStorageManager(LoopertgtLooper)throwsRemoteException{
- mMountService=IMountService.Stub.asInterface(ServiceManager.getService("mount"));
- if(mMountService==null){
- Log.e(TAG,"Unabletoconnecttomountservice!-isitrunningyet?");
- return;
- }
- mTgtLooper=tgtLooper;
- mBinderListener=newMountServiceBinderListener();
- mMountService.registerListener(mBinderListener);
- }
在该方法中,首先初始化了IMountService的对象,因为目前自己对Binder这一块还不是很熟悉,因此只能凭借自己的理解来分析。我们先去看看ServiceManager.getService("mount")方法:
- publicstaticIBindergetService(Stringname){
- try{
- IBinderservice=sCache.get(name);
- if(service!=null){
- returnservice;
- }else{
- returngetIServiceManager().getService(name);
- }
- }catch(RemoteExceptione){
- Log.e(TAG,"erroringetService",e);
- }
- returnnull;
- }
“该方法将返回一个服务的引用,这个服务的名称就是我们传递进去的参数名称。如果这个服务不存在的话将返回null。”源码注释里面是这么说的,但我们就从代码中可以知道,实际上返回的是一个IBinder的对象。
接着调用回到StorageManager的构造函数中的IMountService.Stub.asInterface()方法:
- publicstaticIMountServiceasInterface(IBinderobj){
- if(obj==null){
- returnnull;
- }
- IInterfaceiin=obj.queryLocalInterface(DESCRIPTOR);
- if(iin!=null&&iininstanceofIMountService){
- return(IMountService)iin;
- }
- returnnewIMountService.Stub.Proxy(obj);
- }
“该方法将一个IBinder对象转换成一个IMountService接口,如果必要的话将通过代理来实现”,这里所说的代理指的是Proxy()方法。
这里又需要跳转到obj.queryLocaIInterface()方法中(PS:大家不要觉得枯燥,作为一个新手很多东西我也是第一次接触因此可能会走很多弯路,但过程还是很精彩的):
- publicIInterfacequeryLocalInterface(Stringdescriptor);
很明显,这是一个在IBinder中的接口,因为Binder类实现了IBinder接口,因此我们直接去Binder类中查找该方法:
- publicIInterfacequeryLocalInterface(Stringdescriptor){
- if(mDescriptor.equals(descriptor)){
- returnmOwner;
- }
- returnnull;
- }
以上方法的作用是,根据传入的描述符返回一个IInterface的mOwner对象,该mOwner对象在Binder类中有方法带参数传递如下:
- publicvoidattachInterface(IInterfaceowner,Stringdescriptor){
- mOwner=owner;
- mDescriptor=descriptor;
- }
分析到这一步,我相信大家都头都晕了吧(不管你晕不晕,我反正是晕了,休息休息...)。
*************************************************分割线*********************************************************
休息好了,我们继续分析吧。
因为IInterface实际上也是一个接口,因此不可能实例化对象来传递,所以这里我们也不用想,只要找到其子类那么传递的对象就是其子类实例化的对象。但是要怎么找呢?我们这里的descriptor是“IMountService”因此我们可以在IMountService.java中寻找线索:
- publicinterfaceIMountServiceextendsIInterface{
- /**Local-sideIPCimplementationstubclass.*/
- publicstaticabstractclassStubextendsBinderimplementsIMountService{
- privatestaticclassProxyimplementsIMountService{
- privateIBindermRemote;
- Proxy(IBinderremote){
- mRemote=remote;
- }
- ...省略
- /**Constructthestubatattachittotheinterface.*/
- publicStub(){
- attachInterface(this,DESCRIPTOR);
- }
- ...省略
这里可以看到IMountService继承了IInterface并且其内部类Stub还继承了Binder并实现了IMountService。(这里会涉及到了Android中的AIDL即Android Interface Defenition Language的知识,关于AIDL我会在博客中另起文章介绍并结合源码分析。
虽然通过以上代码的分析,但似乎已经是死胡同了,那么接下来该肿么办呢?
(4).切莫误入歧途
我们回到StorageManager的带参构造函数中(别忘了我们从这里开始分支的):
- publicStorageManager(LoopertgtLooper)throwsRemoteException{
- mMountService=IMountService.Stub.asInterface(ServiceManager.getService("mount"));
- if(mMountService==null){
- Log.e(TAG,"Unabletoconnecttomountservice!-isitrunningyet?");
- return;
- }
- mTgtLooper=tgtLooper;
- mBinderListener=newMountServiceBinderListener();
- mMountService.registerListener(mBinderListener);
- }
- mMountService.registerListener(mBinderListener);
- publicvoidregisterListener(IMountServiceListenerlistener)throwsRemoteException{
- Parcel_data=Parcel.obtain();
- Parcel_reply=Parcel.obtain();
- try{
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder((listener!=null?listener.asBinder():null));
- mRemote.transact(Stub.TRANSACTION_registerListener,_data,_reply,0);
- _reply.readException();
- }finally{
- _reply.recycle();
- _data.recycle();
- }
- }
估计很多朋友看到这里又晕了,“这是又是什么东西啊...“
大家请勿惊慌,因为在Android中涉及到很多设计思路,这里就是其中之一——Android中的IPC机制。这里我会展开去说这个东西的原理以及如何实现的,因为我们的目标并不是它,所以我们只需要了解其大概意思就行了。
在IMountService.Stub中的registerListener()方法中,实现了对数据的封装并发送。那么哪里会接收呢?
(5).神秘的接收者
那么到底是谁来接收呢?答案是:MountService.java
这里的接收需要有一点AIDL的知识,这一点我回在后面的博文中加入实例以及和源码的分析。
MountService继承了IMountService.Stub并覆写了其中的registerListener()方法,真正调用的也就是MountService中的registerListener()方法:
- publicvoidregisterListener(IMountServiceListenerlistener){
- synchronized(mListeners){
- MountServiceBinderListenerbl=newMountServiceBinderListener(listener);
- try{
- listener.asBinder().linkToDeath(bl,0);
- mListeners.add(bl);
- }catch(RemoteExceptionrex){
- Slog.e(TAG,"Failedtolinktolistenerdeath");
- }
- }
- }
- publicStorageManager(LoopertgtLooper)throwsRemoteException{
- mMountService=IMountService.Stub.asInterface(ServiceManager.getService("mount"));
- if(mMountService==null){
- Log.e(TAG,"Unabletoconnecttomountservice!-isitrunningyet?");
- return;
- }
- mTgtLooper=tgtLooper;
- mBinderListener=newMountServiceBinderListener();
- mMountService.registerListener(mBinderListener);
- }
细心的朋友已经发现了吧!对,没错,我们传递的参数分明是MountServiceBinderListener的对象,而在MountService中的registerListener接收的参数却是IMountServiceListener类型的。这是怎么回事呢?我们可以在StorageManager中跟踪MountServiceBinderListener类,会发现:
- privateclassMountServiceBinderListenerextendsIMountServiceListener.Stub
原来MountServiceBinderListener继承了IMountServiceListener.Stub,而Stub有实现了IMountServiceListener,因此根据多态性,参数为IMountServiceListener可以接收为MountServiceBinderListener的对象。
接下来我们回到MountService中的registerListener方法中,继续分析:
- <fontxmlns="http://www.w3.org/1999/xhtml"size="4"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml">MountServiceBinderListenerbl=newMountServiceBinderListener(listener);
- </font></font></font></font></font></font></font></font>
- <fontxmlns="http://www.w3.org/1999/xhtml"size="4"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml">privatefinalclassMountServiceBinderListenerimplementsIBinder.DeathRecipient{
- finalIMountServiceListenermListener;
- MountServiceBinderListener(IMountServiceListenerlistener){
- mListener=listener;
- }
- ...省略
- }</font></font></font></font></font></font></font>
在这个MountServiceBinderListener的构造函数中,相当于对IMountServiceListener对象进行了实例化,而实例化的对象就是StorageManager中传递过来的MountServiceBinderListener对象。
接着分析后面的代码:
- <fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml">listener.asBinder().linkToDeath(bl,0);
- mListeners.add(bl);</font></font></font></font></font></font></font>
这两句代码的意思就是注册一个IBinder进程死亡标志,该方法用来接收进程退出的消息,然后执行然后执行mListeners.add(bl);将bl对象加入Arraylist中。
(6).胜利的曙光
经过前面那么长,注意啊,是那么长的分析,我们回到到原点:
- <fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml">if(mStorageManager==null){
- mStorageManager=(StorageManager)getSystemService(Context.STORAGE_SERVICE);
- mStorageManager.registerListener(mStorageListener);
- }</font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font>
牢骚话不多说了,我们继续分析,接下来跳转到mStorageManager.registerListener()方法中:
- <fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml">publicvoidregisterListener(StorageEventListenerlistener){
- if(listener==null){
- return;
- }
- synchronized(mListeners){
- mListeners.add(newListenerDelegate(listener));
- }
- }</font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font>
继续跳转到ListenerDelegate()方法中:
- <fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml"><fontxmlns="http://www.w3.org/1999/xhtml">ListenerDelegate(StorageEventListenerlistener){
- mStorageEventListener=listener;
- mHandler=newHandler(mTgtLooper){
- @Override
- publicvoidhandleMessage(Messagemsg){
- StorageEvente=(StorageEvent)msg.obj;
- if(msg.what==StorageEvent.EVENT_UMS_CONNECTION_CHANGED){
- UmsConnectionChangedStorageEventev=(UmsConnectionChangedStorageEvent)e;
- mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available);
- }elseif(msg.what==StorageEvent.EVENT_STORAGE_STATE_CHANGED){
- StorageStateChangedStorageEventev=(StorageStateChangedStorageEvent)e;
- mStorageEventListener.onStorageStateChanged(ev.path,ev.oldState,ev.newState);
- }else{
- Log.e(TAG,"Unsupportedevent"+msg.what);
- }
- }
- };
- }</font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font>
根据以上代码可以知道,这里是将StorageEventListener的对象传入ListenerDelegate的构造函数,并返回一个ListenerDelegate的对象,将该返回的对象加入ArrayList<ListenerDelegate>中。我们可以看到在ListenerDelegate类的构造函数中有一个handleMessage,用于接收handler传递的消息。这一步分析到这里也就完成了,相当于监听的初始化已经完成。
后文将继续分析,敬请关注...
相关推荐
Android23SD卡挂载流程浅析.docx
Android23SD卡挂载流程浅析.doc
本文档基于android4.2平台,sd卡热插拔在android层是如何传递消息
主要介绍了Android2.3实现SD卡与U盘自动挂载的方法,较为详细的分析了Android2.3实现SD卡与U盘自动挂载的具体步骤与相关技巧,需要的朋友可以参考下
但是每款定制过的android 系统的外置SD卡的路径都不一样,那我们怎么才能去获取这个路径呢,我们可以想其它的办法,我这里提供了一个类可以获取外置SD卡或内置SD卡的 label(名称),path(路径),mount_point(挂载点)...
本文实例讲述了Android判断SD卡是否已经挂载的方法。分享给大家供大家参考。具体如下: 提供一个监听方法BroadcastReceiver 设置IntentFilter为: Intent.ACTION_MEDIA_MOUNTED Intent.ACTION_MEDIA_EJECT Intent....
AndroidStudio编写
SD卡任意挂载工具
SD卡挂载高歌好用
android 内部存储 sd卡app私有文件 等
android读取sd卡中MP3文件
在ubuntu系统中1、 查看sd卡名字,2、挂在sd卡,3、 查看sd卡内容,
这是一个修改自内核的SD driver 模块 :linux3.4.2 SD/MMC/TF卡挂载问题的解决
android 读取和存储sd卡一个例子,欢迎下载!
Android手机SD卡文件浏览器:遍历出手机Sd卡中的文件。
android 读取外置和内置存储卡路径和大小,亲测好使,项目中以运用
在Android N上并没有提供直接的方法获取外置SD卡或挂载U盘路径,可以通过下面方法获取内置sd卡路径 Environment.getExternalStorageDirectory().getAbsolutePath(); 通过查看getExternalStorageDirectory源码发现,...