標籤:
最近在做項目的過程中需要唯一標識使用者的裝置,後台在做push notification的時候需要用到這個唯一的標識號。
首先我會想到的是裝置的device id,毫無疑問可以唯一標識裝置,第一個版本也正是這樣做的。國慶期間使用者的一封郵件讓哥很不淡定,因為需要拿到device id,所以必然要在AndroidManifest檔案中添加許可權
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
添加完這條許可權很自然的在使用者下載App的時候會提示以下許可權接受的列表:
OK。問題來了,使用者覺得這個東西比較敏感,我選擇不安裝你們的這個App,心中一萬隻羊駝在奔跑。
想想換換其他方式來實現這一需求吧。今天和大家總結分享下每種方式的利弊。
第一種方式:裝置的Device Id作為唯一標識
在AndroidManifest設定檔中添加許可權
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
具體擷取的方法如下,我是寫在工具類中的:
public static String getDeviceIdInfo(Context mContext) { //String imei = ((TelephonyManager) mContext.getSystemService(mContext.TELEPHONY_SERVICE)).getDeviceId(); String imei = Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID); return imei; }
這種實現方式的缺點在於:
1.遇到安全警覺性比較高的使用者,我不接受這樣的許可權,不安裝你的App。
2.非手機裝置,如果只帶有Wifi的裝置或者音樂播放器沒有通話的硬體功能的話就沒有這個DEVICE_ID。
3.作為手機來講,IMEI是唯一的,它應該類似於359881030314356(除非你有一個沒有量產的手機(水貨)它可能有無效的IMEI,如:0000000000000)。說白了,如果只為了擷取它,沒有用到其他的通話功能,那這個許可權有點大才小用。
4.在少數的一些手機裝置上,該實現有漏洞,會返回垃圾,如:zeros或者asterisks的產品。
第二種方式:擷取MAC ADDRESS
我們也可以通過手機的Wifi或者藍牙裝置擷取MAC ADDRESS作為DEVICE ID,但是並不建議這麼做,因為並不是所有的裝置都有Wifi,並且,如果Wifi沒有開啟,那硬體裝置無法返回MAC ADDRESS.
The WLAN MAC Address string, 是另一個唯一ID。毫無疑問你需要在AndroidManifest檔案中添加如下許可權:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
代碼中的實現:
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
String m_szWLANMAC = wm.getConnectionInfo().getMacAddress();
Returns: 00:11:22:33:44:55 (這不是一個真實的地址。而且這個地址能輕易地被偽造。).WLan不必開啟,就可讀取些值。
第三種方式:BT MAC ADDRESS
只在有藍芽的裝置上運行。並且要加入android.permission.BLUETOOTH 許可權.
BluetoothAdapter m_BluetoothAdapter = null; // Local Bluetooth adapter
m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String m_szBTMAC = m_BluetoothAdapter.getAddress();
Returns: 43:25:78:50:93:38 . 藍芽沒有必要開啟,也能讀取。
第四種方式:Serial Number
裝有SIM卡的裝置可以通過getSystemService(Context.TELEPHONY_SERVIEC).getSimSerialNumber();擷取到
sim serial number。 注意對CDMA裝置,返回的是一個空值。
在Android 2.3可以通過android.os.Build.SERIAL擷取,非手機裝置可以通過該介面擷取。
第五種方式:ANDROID_ID
ANDROID_ID是裝置第一次啟動時產生和儲存的64bit的一個數,當裝置被wipe後該數重設
ANDROID_ID似乎是擷取Device ID的一個好選擇,但它也有缺陷:
它在Android <=2.1 or Android >=2.3的版本是可靠、穩定的,但在2.2的版本並不是100%可靠的
在主流廠商生產的裝置上,有一個很經常的bug,就是每個裝置都會產生相同的ANDROID_ID:9774d56d682e549c
具體擷取的方法:
String m_szAndroidID = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
第六種方式:Installtion ID : UUID
這種方式也正是我最後採用的一種方式。
以上幾種方式都有或多或少存在的一定的局限性或者bug,在這裡,有另外一種方式解決,就是使用UUID,該方法無需訪問裝置的資源,也跟裝置類型無關。
這種方式是通過在程式安裝後第一次運行後產生一個ID實現的,但該方式跟裝置唯一標識不一樣,它會因為不同的應用程式而產生不同的ID,而不是裝置唯一ID。
因此經常用來標識在某個應用中的唯一ID(即Installtion ID),或者跟蹤應用的安裝數量。
我在程式中建立了這麼一個工具類來擷取這個UUID
import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.RandomAccessFile;import java.util.UUID;import android.content.Context;public class Installation { private static String sID = null; private static final String INSTALLATION = "INSTALLATION"; public synchronized static String id(Context context) { if (sID == null) { File installation = new File(context.getFilesDir(), INSTALLATION); try { if (!installation.exists()) writeInstallationFile(installation); sID = readInstallationFile(installation); } catch (Exception e) { throw new RuntimeException(e); } } return sID; } private static String readInstallationFile(File installation) throws IOException { RandomAccessFile f = new RandomAccessFile(installation, "r"); byte[] bytes = new byte[(int) f.length()]; f.readFully(bytes); f.close(); return new String(bytes); } private static void writeInstallationFile(File installation) throws IOException { FileOutputStream out = new FileOutputStream(installation); String id = UUID.randomUUID().toString(); out.write(id.getBytes()); out.close(); }}
綜上所述,我還是比較推薦最後一種方式。如果還有別的實現方式大家可以留言一起學習討論。
Android手機上擷取物理唯一標識碼