描述:一个应用,首次安装应用黑屏5秒左右后才开始显示正常界面。在做桌面应用的时候,由于桌面一直被用所以也没怎么发现,而且该问题是只有每次卸载(或者之前没有该应用)之后再次安装首次启动才会出现黑屏。后来经过打印时间才定位到是因为初始化的时候获取IMEI耗时了10s多(在界面设置要显示的View之前).
获取IMEI(设备ID):Requires Permission: READ_PHONE_STATE
TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Service.TELEPHONY_SERVICE); String deviceId = tm.getDeviceId();
查看源码:
622 public String More ...getDeviceId() { 623 try { 624 return getITelephony().getDeviceId(); 625 } catch (RemoteException ex) { 626 return null; 627 } catch (NullPointerException ex) { 628 return null; 629 } 630 }
2369 private ITelephony More ...getITelephony() { 2370 return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); 2371 }
好吧,AIDL实现的,比较底层,木有去看,不过也猜出了个一二了,因为初始化的时候直接在主线程里去获取IMEI,而获取IMEI是需要权限的,这个时候系统会弹出个框让你选择是否授权,一般当用户点击的时候已经过了系统判断的时间了,于是又等10秒再去判断(当然这只是结合以为大神和我自己的看法),这就导致了主线程需要10s才获得IMEI,获取到IMEI之后才去设置要显示的View,所以之前就出现了黑屏。
解决:在初始化的时候开启一个线程去获取,当获取到值的时候再回调给需要IMEI的地方。但是这里获取IMEI的时间不一,可能要几毫秒,也可能是10多秒。再者就是不同的时间去获取,等待的时间不一,可能先获取的地方等待的时间更长,所以,干脆每次需要获取的时候都去开启一个线程获取,于是便有了以下设计
(为什么不是直接用一个子线程去获取然后赋值?耗时长,多次请求可以加大概率,而且可能不同的地方需要获得该值),如果是只有一个地方需要初始化的话,可以只要单独一个线程去获取就好。
public class MainActivity extends Activity { private Context mContext; private String mImei; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; // 初始化获取IMEI IMEIUtils.getInstance().init(mContext); // 其他事情 // 其他事情 // 在需要的地方获取IMEI; mImei = IMEIUtils.getInstance().getImei(); if (IMEIUtils.getInstance().isImeiGetting(mImei)) {// 如果是正在获取当中 mImei = IMEIUtils.getInstance().getNullImei();// 先给他初始化一个空值的 // 添加监听 IMEIUtils.getInstance().addOnImeiGetListener(new ImeiGetListener() { @Override public void onGetIMEIOk(String imei) {// 得到之后将回调其值给需要的地方 mImei = imei; } }); } } }
public class IMEIUtils { private static final String TAG = "IMEIUtils"; private static final String IMEI_RESULT_GETTING = "imei_result_getting"; private String mIMEI = ""; private ArrayList<ImeiGetListener> mEncodeImeiListenerList = new ArrayList<ImeiGetListener>(); private static IMEIUtils mEncodeImeiUtils = null; private Context mContext; private IMEIUtils() { } public synchronized static IMEIUtils getInstance() { if (mEncodeImeiUtils == null) { mEncodeImeiUtils = new IMEIUtils(); } return mEncodeImeiUtils; } /** * 初始化 * * @param context */ public void init(Context context) { Log.d(TAG, "initImei"); mContext = context; initImei(); } /** * 开启一个线程去获取IMEI */ private void initImei() { new Thread(new Runnable() { @Override public void run() { if (isImeiGot()) {// 已经获取到该ID了,就不用再去获取了 Log.d(TAG, "initImei is not null, no need to get IMEI " + mIMEI); return; } mIMEI = IMEI_RESULT_GETTING;// 设置该值为获取中 long beginTime = System.currentTimeMillis(); Log.d(TAG, "initEncodeImei begin TelephonyManager.getDeviceId"); TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Service.TELEPHONY_SERVICE); String deviceId = tm.getDeviceId(); if (isImeiGot()) {// 已经获取到该ID了 Log.d(TAG, "sEncodeIMEI is not null ,no need to get value " + mIMEI); } else { mIMEI = deviceId == null ? "test" : deviceId;// ID为空对象则返回一个test(自定义,通常不管获得一个什么字符串,最好都要进行加密) Log.d(TAG, "mIMEI get a value =" + mIMEI); // 通知所有的监听器,将所有结果都回调 for (ImeiGetListener listener : mEncodeImeiListenerList) { listener.onGetIMEIOk(mIMEI); } mEncodeImeiListenerList.clear(); } // 打印一下具体的耗时 long deltaIMEITime = System.currentTimeMillis() - beginTime; Log.d(TAG, "deltaIMEITime=" + deltaIMEITime); } }).start(); } /** * 如果不是空对象,而且不是正在获取中,则说明已经获得了结果了 * * @return */ private boolean isImeiGot() { return mIMEI != null && !IMEI_RESULT_GETTING.equals(mIMEI); } /** * * @return */ public String getImei() { // 空对象,则返回没有空值加密数据 if (null == mIMEI) { Log.d(TAG, "getImei mIMEI==null"); return getNullImei(); } // 正在获取,则再开启一个线程去获取, // 返回IMEI_RESULT_GETTING,获取的地方需要判断,如果是该值则要做一些处理 if (IMEI_RESULT_GETTING.equals(mIMEI) || "".equals(mIMEI)) {// getting Log.d(TAG, "getImei mIMEI is getting"); initImei(); return IMEI_RESULT_GETTING; } // 否则就是已经获取到了,直接返回所需要的 Log.d(TAG, "getImei mIMEI=" + mIMEI); return mIMEI; } public String getNullImei() { return "test";// 自己处理加密 } public boolean isImeiGetting(String encodeImei) { if (encodeImei.equals(IMEI_RESULT_GETTING)) { Log.d(TAG, "isImeiGetting=true"); return true; } return false; } public void addOnImeiGetListener(ImeiGetListener encodeImeiListener) { if (encodeImeiListener == null) { return; } if (isImeiGot()) {// 已经获得了,无需再添加监听器 encodeImeiListener.onGetIMEIOk(mIMEI); return; } if (!mEncodeImeiListenerList.contains(encodeImeiListener)) {// 已经有该监听器了 mEncodeImeiListenerList.add(encodeImeiListener); } } // 监听,回调 public interface ImeiGetListener { public void onGetIMEIOk(String imei); } }
相关推荐
android获取IMEI号
PC端通过adb命令静默安装,不需要root权限 部分手机无法用adb命令获取IMEI 但是可以通过PC端静默安装一个APK到android端并启动它 APK启动后通过USB发送IMEI号到PC端
获取IMEI号码
在android系统中获取imei号,手机号等。
C# 获取IMEI即获取移动设备手机串号手机编码号源代码
不同手机设备 获取IMEI不同手机设备 获取IMEI不同手机设备 获取IMEI不同手机设备 获取IMEI不同手机设备 获取IMEI
核心代码:Imei = ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).getDeviceId();1.加入权限在manifest.xml文件中要添加 ”android.permission.READ_PHONE_STATE”>2.代码 代码如下:package ...
获取手机号和IMEI.rar
Android获取IMEI码Demo 相关文章:http://blog.csdn.net/etzmico/article/details/6853670
获取手机IMEI号、手机型号等
NULL 博文链接:https://zhanglimijavaeye.iteye.com/blog/1745768
基于Android10.0的设备上,获取相应的MAC地址,手机IMEI号,手机号,手机MSISDN号,手机ICCID号,进行可行性验证,特别是MAC地址方面,在Android10.0前后的设备均进行相应的接口验证,保证其可用性。适合正在致力于...
android平台获取手机IMSI,IMEI ,序列号,和 手机号的方法
Imei号无效解决方案
绝对不是网络上搜索的那种获取IMEI的方式,100%原创 C++语言
在JNI中得到手机IMEI号,C++的拿来就能用
本程序描述了基于JAVA CARD技术如何实现获取ME 的IMEI信息。
获取手机IMEI号,sim卡的IMSI号,手机型号等 C#源码
Android 读取IMSI 和IMEI号支持双卡双待,可以判断双卡双待通过那个卡上网,及读取双卡双待的电话卡的信息
因此,为了解决此无效 IMEI 问题并恢复 Android 智能手机上的 IMEI 号码,您必须在 Android 手机中刷新 IMEI 号码。 PS:本教程在 Android 手机上刷新 IMEI 号码适用于大多数品牌,包括 – Alcatel、BLU、Celkon、...