android公开的API提供了访问方法,大家都知道使用TelephonyManager提供的方法,但是有些理解有误,如下国内一个比较大的andorid论坛提供的例子,就出现了错误:
帖子如下http://www.eoeandroid.com/thread-14027-1-3.html,其中实现代码没有注释,只能按照变量定义判断:
TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE); String deviceid = tm.getDeviceId(); String tel = tm.getLine1Number(); //取出用户手机号码,我加的 String imei =tm.getSimSerialNumber(); //取出IMEI,我加的 String imsi =tm.getSubscriberId(); //取出IMSI,我加的
那么上述出现错误了:String imei =tm.getSimSerialNumber(); //取出IMEI
IMEI是手机的序列号,怎么会通过getSimSerialNumber()方法获得,那么查一下andorid源码可以看出:
http://www.netmite.com/android/mydroid/frameworks/base/telephony/java/android/telephony/TelephonyManager.java
从注释里明显看出来这个函数是取SIM卡序列号的,也就是ICCID的,他用错了。
/** * Returns the serial number of the SIM, if applicable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getSimSerialNumber() { try { return getSubscriberInfo().getSimSerialNumber(); } catch (RemoteException ex) { } return null; }
2.相关几个定义、说明:
我们说到的和手机、卡相关的号码数据包括IMSI,MSISDN,ICCID,IMEI
IMSI:international mobiles subscriber identity国际移动用户号码标识,这个一般大家是不知道,GSM必须写在卡内相关文件中;
MSISDN:mobile subscriber ISDN用户号码,这个是我们说的139,136那个号码;
ICCID:ICC identity集成电路卡标识,这个是唯一标识一张卡片物理号码的;
IMEI:international mobile Equipment identity手机唯一标识码;
3.那好我们看看andorid实现TelephonyManager.java的源码:
getDeviceId()取IMEI号没有争议了。
/** * Returns the unique device ID, for example,the IMEI for GSM * phones. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getDeviceId() { try { return getSubscriberInfo().getDeviceId(); } catch (RemoteException ex) { } return null; }
getLine1Number()取MSISDN,这个需要说明两点
1) 为什么这个函数叫getLine1Number(),因为andorid实现的时候应该分为GSM和CDMA的,GSM手机使用这个函数,CDMA应该还会由其它实现的。
2) 取MSISDN具体的方法就会导致最后能否取到了,函数中调用了getSubscriberInfo().getLine1Number()去实现,我们下面找找看。
/** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ public String getLine1Number() { try { return getSubscriberInfo().getLine1Number(); } catch (RemoteException ex) { } return null; }
// 找到了 private IPhoneSubInfo getSubscriberInfo() { // get it each time because that process crashes a lot return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo")); } // 一个接口,再找有一个PhoneSubInfo.java: /** * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones. */ public String getDeviceId() { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE"); return mPhone.getDeviceId(); } //前面定义了Phone mPhone,再找Phone.java: /** * Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones. */ String getSubscriberId(); //原来是个接口,发现PhoneProxy.java有具体实现 public String getSubscriberId() { return mActivePhone.getSubscriberId(); } // 这个mActivePhone是phone的实例,我疯了,于是发现GSMPHONE。java中有了具体实现: public String getSubscriberId() { return mSIMRecords.imsi; } public String getIccSerialNumber() { return mSIMRecords.iccid; } public String getLine1Number() { return mSIMRecords.getMsisdnNumber(); }
从上面看出来,应该是通过SIM卡相关文件记录得到的上述数据,从其中看到:
public void handleMessage(Message msg) 这个函数进行了真正的处理,重点看:
case EVENT_GET_MSISDN_DONE: isRecordLoadResponse = true; ar = (AsyncResult)msg.obj; if (ar.exception != null) { Log.d(LOG_TAG, "Invalid or missing EF[MSISDN]"); //应该是从sim卡的EFmsisdn文件中取出来的 break; } adn = (AdnRecord)ar.result; msisdn = adn.getNumber(); msisdnTag = adn.getAlphaTag(); Log.d(LOG_TAG, "MSISDN: " + msisdn); break;
下面的细节就不分析了,那个问题就归结到是否可以从SIM卡的EFmsisdn文件取出手机号码了,不幸的是一般运营商不会把用户号码写在这个文件的,为什么呢?
因为这个手机号码是在用户买到卡并开通时才将IMSI和MSISDN对应上的,卡内生产出来时只有IMSI,你不知道用户喜欢那个手机号码,因此一般不先对应IMSI和MSISDN,即时有对应也不写这个文件的。
4 总结
TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
String imei = tm.getDeviceId(); //取出IMEI
String tel = tm.getLine1Number(); //取出MSISDN,很可能为空
String imei =tm.getSimSerialNumber(); //取出ICCID
String imsi =tm.getSubscriberId(); //取出IMSI
lp 其实就是上个博客提到的Window Attribute 只不过是ViewRoot的。
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); desiredWindowWidth 根据代码可知,一般情况就是屏幕的宽度;desiredWindowHeight 根据代码可知,一般情况就是屏幕的宽度-状态栏高度; /** * Figures out the measure spec for the root view in a window based on it's * layout params. * * @param windowSize * The available width or height of the window * * @param rootDimension * The layout params for one dimension (width or height) of the * window. * * @return The measure spec to use to measure the root view. */ private int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; } host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
注意ViewRoot并不是View,不过是ViewParent。刷新屏幕都是在这个类中,host就是PhoneWindow的DecorView。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } /** * Utility to return a default size. Uses the supplied size if the * MeasureSpec imposed no contraints. Will get larger if allowed * by the MeasureSpec. * * @param size Default size for this view * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; }
!-- Dummy item to prevent AutoCompleteTextView from receiving focus --> <LinearLayout android:focusable="true" android:focusableInTouchMode="true" android:layout_width="0px" android:layout_height="0px"/> <!-- :nextFocusUp and :nextFocusLeft have been set to the id of this component to prevent the dummy from receiving focus again --> <AutoCompleteTextView android:id="@+id/text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:nextFocusUp="@+id/text" android:nextFocusLeft="@+id/text"/>上面是为了防止
AutoCompleteTextView开始就获得焦点,所以在他的父层设置了焦点设置