描述:一个应用,首次安装应用黑屏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);
}
}