欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

获取IMEI号耗时10秒导致黑屏的解决

程序员文章站 2024-02-11 11:19:10
...

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