อุปกรณ์ Android มีรหัสเฉพาะหรือไม่ถ้าอย่างนั้นวิธีง่ายๆในการเข้าถึงโดยใช้ Java คืออะไร?
อุปกรณ์ Android มีรหัสเฉพาะหรือไม่ถ้าอย่างนั้นวิธีง่ายๆในการเข้าถึงโดยใช้ Java คืออะไร?
คำตอบ:
Settings.Secure#ANDROID_ID
ส่งคืน ID Android เป็นค่าเฉพาะสำหรับผู้ใช้สตริง hex 64 บิตแต่ละราย
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
UPDATE : สำหรับ Android เวอร์ชันล่าสุดปัญหามากมายที่ANDROID_ID
ได้รับการแก้ไขแล้วและฉันเชื่อว่าวิธีการนี้ไม่จำเป็นอีกต่อไป โปรดดูที่คำตอบแอนโทนี
การเปิดเผยข้อมูลเต็มรูปแบบ: app ของฉันใช้ด้านล่างวิธีการเดิม แต่ไม่ใช้วิธีนี้และตอนนี้เราใช้วิธีการที่ระบุไว้ในบล็อกของนักพัฒนา Androidรายการที่emmby คำตอบของการเชื่อมโยงไป (กล่าวคือการสร้างและการประหยัดUUID#randomUUID()
)
มีคำตอบมากมายสำหรับคำถามนี้ซึ่งส่วนใหญ่จะใช้เวลา "บางส่วน" เท่านั้นและน่าเสียดายที่ยังไม่ดีพอ
จากการทดสอบอุปกรณ์ของฉัน (โทรศัพท์ทุกเครื่องอย่างน้อยหนึ่งอันไม่ได้เปิดใช้งาน):
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
(ตามที่คาดไว้)ANDROID_ID
ANDROID_ID
และTelephonyManager.getDeviceId()
-ตราบใดที่มีการเพิ่มบัญชี Google ในระหว่างการตั้งค่าดังนั้นหากคุณต้องการบางสิ่งบางอย่างที่ไม่ซ้ำกับอุปกรณ์นั้นTM.getDeviceId()
ควรจะเพียงพอ เห็นได้ชัดว่าผู้ใช้บางคนมีความหวาดระแวงมากกว่าผู้อื่นดังนั้นจึงอาจมีประโยชน์ในการแฮช 1 ตัวหรือมากกว่าของตัวระบุเหล่านี้เพื่อให้สตริงยังคงเป็นเอกลักษณ์เฉพาะกับอุปกรณ์ แต่ไม่ได้ระบุอุปกรณ์จริงของผู้ใช้อย่างชัดเจน ตัวอย่างเช่นการใช้String.hashCode()
รวมกับ UUID:
final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);
final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
tmSerial = "" + tm.getSimSerialNumber();
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();
อาจส่งผลให้บางสิ่งเช่น: 00000000-54b3-e7c7-0000-000046bffd97
มันทำงานได้ดีพอสำหรับฉัน
ตามที่ Richard กล่าวไว้ด้านล่างอย่าลืมว่าคุณต้องได้รับอนุญาตให้อ่านTelephonyManager
คุณสมบัติดังนั้นเพิ่มสิ่งนี้ลงในรายการของคุณ:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
นำเข้า libs
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
ในไฟล์รายการ หากการจัดเก็บในฐานข้อมูลสตริงที่ส่งคืนมีความยาว 36 ตัวอักษร
หลังจากอ่านโพสต์ Stack Overflow ทุกครั้งเกี่ยวกับการสร้าง ID ที่ไม่ซ้ำกันบล็อกของนักพัฒนาซอฟต์แวร์ของ Google และ Android ฉันรู้สึกราวกับว่า 'Pseudo ID' เป็นตัวเลือกที่ดีที่สุด
รหัส Psuedo:
if API >= 9/10: (99.5% of devices)
return unique ID containing serial id (rooted devices may be different)
else
return the unique ID of build information (may overlap data - API < 9)
ขอบคุณ @stansult สำหรับการโพสต์ตัวเลือกทั้งหมดของเรา (ในคำถาม Stack Overflow)
อีเมลผู้ใช้ - ซอฟต์แวร์
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
หรือ<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
( วิธีรับที่อยู่อีเมลหลักของอุปกรณ์ Android )หมายเลขโทรศัพท์ของผู้ใช้ - ซอฟต์แวร์
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - ฮาร์ดแวร์ (เฉพาะโทรศัพท์ที่ต้องการandroid.permission.READ_PHONE_STATE
)
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Android ID - ฮาร์ดแวร์ (อาจเป็นโมฆะสามารถเปลี่ยนได้เมื่อรีเซ็ตจากโรงงานสามารถแก้ไขได้บนอุปกรณ์ที่รูท)
ที่อยู่ WLAN MAC - ฮาร์ดแวร์ (ต้องการandroid.permission.ACCESS_WIFI_STATE
)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
ที่อยู่ MAC Bluetooth - ฮาร์ดแวร์ (อุปกรณ์ที่มีบลูทู ธ ต้องการandroid.permission.BLUETOOTH
)
<uses-permission android:name="android.permission.BLUETOOTH "/>
Pseudo-Unique ID - ซอฟต์แวร์ (สำหรับอุปกรณ์ Android ทั้งหมด)
ฉันรู้ว่าไม่มีวิธี 'สมบูรณ์แบบ' ในการรับ ID เฉพาะโดยไม่ต้องใช้การอนุญาต อย่างไรก็ตามบางครั้งเราจำเป็นต้องติดตามการติดตั้งอุปกรณ์เท่านั้น เมื่อมันมาถึงการสร้าง ID ที่ไม่ซ้ำกันเราสามารถสร้าง 'pseudo unique id' ตามข้อมูลที่ Android API ให้กับเราโดยไม่ต้องใช้สิทธิ์พิเศษ ด้วยวิธีนี้เราสามารถแสดงความเคารพต่อผู้ใช้และพยายามเสนอประสบการณ์ผู้ใช้ที่ดีเช่นกัน
ด้วยรหัสปลอมที่ไม่ซ้ำใครคุณจะพบความจริงว่าอาจมีการซ้ำซ้อนตามความจริงที่ว่ามีอุปกรณ์ที่คล้ายกัน คุณสามารถปรับแต่งวิธีการรวมเพื่อให้มีความพิเศษมากขึ้น อย่างไรก็ตามนักพัฒนาซอฟต์แวร์บางรายจำเป็นต้องติดตามการติดตั้งอุปกรณ์และสิ่งนี้จะทำการหลอกลวงหรือประสิทธิภาพตามอุปกรณ์ที่คล้ายกัน
หากอุปกรณ์ Android ของพวกเขาคือ API 9 หรือสูงกว่าสิ่งนี้รับประกันว่าจะไม่ซ้ำกันเนื่องจากฟิลด์ 'Build.SERIAL'
โปรดจำไว้ว่าคุณจะพลาดเพียง 0.5% ของผู้ใช้ที่มี API <9เท่านั้น ดังนั้นคุณสามารถมุ่งเน้นที่เหลือ: นี่คือ 99.5% ของผู้ใช้!
หากอุปกรณ์ Android ของผู้ใช้ต่ำกว่า API 9; หวังว่าพวกเขายังไม่ได้รีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและ 'Secure.ANDROID_ID' ของพวกเขาจะถูกเก็บไว้หรือไม่เป็น 'โมฆะ' (ดูhttp://developer.android.com/about/dashboards/index.html )
หากทุกอย่างล้มเหลวหากผู้ใช้มีต่ำกว่า API 9 (ต่ำกว่า Gingerbread) ได้รีเซ็ตอุปกรณ์หรือ 'Secure.ANDROID_ID' ส่งคืน 'null' เพียงแค่ ID ที่ส่งคืนจะถูกยึดตามข้อมูลอุปกรณ์ Android ของพวกเขาเพียงผู้เดียว นี่คือที่ที่การชนสามารถเกิดขึ้นได้
การเปลี่ยนแปลง:
โปรดดูวิธีการด้านล่าง:
/**
* Return pseudo unique ID
* @return ID
*/
public static String getUniquePsuedoID() {
// If all else fails, if the user does have lower than API 9 (lower
// than Gingerbread), has reset their device or 'Secure.ANDROID_ID'
// returns 'null', then simply the ID returned will be solely based
// off their Android device information. This is where the collisions
// can happen.
// Thanks http://www.pocketmagic.net/?p=1662!
// Try not to use DISPLAY, HOST or ID - these items could change.
// If there are collisions, there will be overlapping data
String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);
// Thanks to @Roman SL!
// https://stackoverflow.com/a/4789483/950427
// Only devices with API >= 9 have android.os.Build.SERIAL
// http://developer.android.com/reference/android/os/Build.html#SERIAL
// If a user upgrades software or roots their device, there will be a duplicate entry
String serial = null;
try {
serial = android.os.Build.class.getField("SERIAL").get(null).toString();
// Go ahead and return the serial for api => 9
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
// String needs to be initialized
serial = "serial"; // some value
}
// Thanks @Joe!
// https://stackoverflow.com/a/2853253/950427
// Finally, combine the values we have found by using the UUID class to create a unique identifier
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
จากคอนโซลของนักพัฒนาซอฟต์แวร์ Google Play:
ตั้งแต่วันที่ 1 สิงหาคม 2014 นโยบายโปรแกรมนักพัฒนาซอฟต์แวร์ Google Play ต้องมีการอัปโหลดและอัปเดตแอปใหม่ทั้งหมดเพื่อใช้รหัสโฆษณาแทนตัวระบุถาวรอื่น ๆ เพื่อวัตถุประสงค์ในการโฆษณา เรียนรู้เพิ่มเติม
การใช้งาน :
สิทธิ์:
<uses-permission android:name="android.permission.INTERNET" />
รหัส:
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;
...
// Do not call this function from the main thread. Otherwise,
// an IllegalStateException will be thrown.
public void getIdThread() {
Info adInfo = null;
try {
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);
} catch (IOException exception) {
// Unrecoverable error connecting to Google Play services (e.g.,
// the old version of the service doesn't support getting AdvertisingId).
} catch (GooglePlayServicesAvailabilityException exception) {
// Encountered a recoverable error connecting to Google Play services.
} catch (GooglePlayServicesNotAvailableException exception) {
// Google Play services is not available entirely.
}
final String id = adInfo.getId();
final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
}
แหล่งที่มา / เอกสาร:
http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
มีวัตถุประสงค์เพื่อให้รหัสโฆษณาแทนที่การใช้งานตัวระบุอื่น ๆ ที่มีอยู่เพื่อวัตถุประสงค์โฆษณาอย่างสมบูรณ์ (เช่นการใช้ ANDROID_ID ในการตั้งค่าการรักษาความปลอดภัย) เมื่อ Google Play Services พร้อมใช้งาน กรณีที่บริการ Google Play ไม่สามารถใช้งานได้จะถูกระบุโดย GooglePlayServicesNotAvailableException ที่ถูกโยนโดย getAdvertisingIdInfo ()
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
ฉันพยายามอ้างอิงทุกลิงก์ที่ฉันนำข้อมูลมา หากคุณขาดหายไปและจำเป็นต้องรวมโปรดแสดงความคิดเห็น!
Build
คลาสจะไม่เปลี่ยนแปลงเมื่ออัปเดตระบบปฏิบัติการหรือไม่ โดยเฉพาะอย่างยิ่งถ้า API ได้รับการปรับปรุง? ถ้าเป็นเช่นนั้นคุณจะรับประกันได้อย่างไรว่าสิ่งนี้มีลักษณะเฉพาะ (พูดเกี่ยวกับวิธีการที่คุณเขียน)
ในฐานะที่เป็นเดฟเวบบ์กล่าวถึงบล็อกนักพัฒนา Android มีบทความที่ครอบคลุมเรื่องนี้ โซลูชันที่พวกเขาต้องการคือการติดตามการติดตั้งแอพมากกว่าอุปกรณ์และจะใช้งานได้ดีสำหรับกรณีส่วนใหญ่ โพสต์บล็อกจะแสดงรหัสที่จำเป็นสำหรับการทำงานและฉันขอแนะนำให้คุณลองดู
อย่างไรก็ตามโพสต์บล็อกยังคงมีอยู่เพื่อหารือเกี่ยวกับการแก้ปัญหาหากคุณต้องการตัวระบุอุปกรณ์แทนที่จะเป็นตัวระบุการติดตั้งแอป ฉันได้พูดคุยกับใครบางคนใน Google เพื่อขอความกระจ่างเพิ่มเติมเกี่ยวกับบางรายการในเหตุการณ์ที่คุณต้องทำ นี่คือสิ่งที่ฉันค้นพบเกี่ยวกับตัวระบุอุปกรณ์ที่ไม่ได้กล่าวถึงในบล็อกโพสต์ดังกล่าว:
ตามคำแนะนำของ Google ฉันใช้คลาสที่จะสร้าง UUID ที่ไม่ซ้ำกันสำหรับอุปกรณ์แต่ละเครื่องโดยใช้ ANDROID_ID เป็นเมล็ดพันธุ์ตามความเหมาะสมย้อนกลับไปที่ TelephonyManager.getDeviceId () ตามความจำเป็นและหากล้มเหลวหันไปใช้ UUID ที่สร้างขึ้นแบบสุ่ม ที่ยังคงมีอยู่ระหว่างการรีสตาร์ทแอป (แต่ไม่ใช่การติดตั้งแอปใหม่)
โปรดทราบว่าสำหรับอุปกรณ์ที่ต้องย้อนกลับไปที่ ID อุปกรณ์ ID ที่ไม่ซ้ำกันจะยังคงมีอยู่ในการรีเซ็ตจากโรงงาน นี่คือสิ่งที่ต้องระวัง หากคุณต้องการตรวจสอบให้แน่ใจว่าการรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานจะรีเซ็ต ID ที่ไม่ซ้ำกันของคุณคุณอาจต้องการพิจารณาย้อนกลับไปที่ UUID สุ่มแทน ID อุปกรณ์
รหัสนี้ใช้สำหรับรหัสอุปกรณ์ไม่ใช่รหัสการติดตั้งแอป สำหรับสถานการณ์ส่วนใหญ่ ID การติดตั้งแอปอาจเป็นสิ่งที่คุณกำลังมองหา แต่ถ้าคุณต้องการรหัสอุปกรณ์รหัสต่อไปนี้อาจใช้ได้กับคุณ
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import java.io.UnsupportedEncodingException;
import java.util.UUID;
public class DeviceUuidFactory {
protected static final String PREFS_FILE = "device_id.xml";
protected static final String PREFS_DEVICE_ID = "device_id";
protected volatile static UUID uuid;
public DeviceUuidFactory(Context context) {
if (uuid == null) {
synchronized (DeviceUuidFactory.class) {
if (uuid == null) {
final SharedPreferences prefs = context
.getSharedPreferences(PREFS_FILE, 0);
final String id = prefs.getString(PREFS_DEVICE_ID, null);
if (id != null) {
// Use the ids previously computed and stored in the
// prefs file
uuid = UUID.fromString(id);
} else {
final String androidId = Secure.getString(
context.getContentResolver(), Secure.ANDROID_ID);
// Use the Android ID unless it's broken, in which case
// fallback on deviceId,
// unless it's not available, then fallback on a random
// number which we store to a prefs file
try {
if (!"9774d56d682e549c".equals(androidId)) {
uuid = UUID.nameUUIDFromBytes(androidId
.getBytes("utf8"));
} else {
final String deviceId = (
(TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE))
.getDeviceId();
uuid = deviceId != null ? UUID
.nameUUIDFromBytes(deviceId
.getBytes("utf8")) : UUID
.randomUUID();
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
// Write the value out to the prefs file
prefs.edit()
.putString(PREFS_DEVICE_ID, uuid.toString())
.commit();
}
}
}
}
}
/**
* Returns a unique UUID for the current android device. As with all UUIDs,
* this unique ID is "very highly likely" to be unique across all Android
* devices. Much more so than ANDROID_ID is.
*
* The UUID is generated by using ANDROID_ID as the base key if appropriate,
* falling back on TelephonyManager.getDeviceID() if ANDROID_ID is known to
* be incorrect, and finally falling back on a random UUID that's persisted
* to SharedPreferences if getDeviceID() does not return a usable value.
*
* In some rare circumstances, this ID may change. In particular, if the
* device is factory reset a new device ID may be generated. In addition, if
* a user upgrades their phone from certain buggy implementations of Android
* 2.2 to a newer, non-buggy version of Android, the device ID may change.
* Or, if a user uninstalls your app on a device that has neither a proper
* Android ID nor a Device ID, this ID may change on reinstallation.
*
* Note that if the code falls back on using TelephonyManager.getDeviceId(),
* the resulting ID will NOT change after a factory reset. Something to be
* aware of.
*
* Works around a bug in Android 2.2 for many devices when using ANDROID_ID
* directly.
*
* @see http://code.google.com/p/android/issues/detail?id=10603
*
* @return a UUID that may be used to uniquely identify your device for most
* purposes.
*/
public UUID getDeviceUuid() {
return uuid;
}
}
นี่คือรหัสที่ Reto Meier ใช้ในการนำเสนอGoogle I / Oในปีนี้เพื่อรับรหัสเฉพาะสำหรับผู้ใช้:
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
public synchronized static String id(Context context) {
if (uniqueID == null) {
SharedPreferences sharedPrefs = context.getSharedPreferences(
PREF_UNIQUE_ID, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
}
}
return uniqueID;
}
หากคุณจับคู่สิ่งนี้ด้วยกลยุทธ์การสำรองข้อมูลเพื่อส่งการตั้งค่าไปยังคลาวด์ (อธิบายไว้ในการพูดคุยของ Reto คุณควรมี ID ที่เชื่อมโยงกับผู้ใช้และเกาะติดหลังจากที่อุปกรณ์ถูกเช็ดหรือเปลี่ยนไปแล้ว ในการวิเคราะห์ไปข้างหน้า (ในคำอื่น ๆ ฉันยังไม่ได้ทำบิตนั้น :)
นอกจากนี้คุณอาจพิจารณาที่อยู่ MAC ของอะแดปเตอร์ Wi-Fi เรียกคืนดังนั้น:
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();
ต้องได้รับอนุญาตandroid.permission.ACCESS_WIFI_STATE
ในรายการ
รายงานว่าพร้อมใช้งานแม้ว่าจะไม่ได้เชื่อมต่อ Wi-Fi หากโจจากคำตอบข้างต้นให้ลองใช้อุปกรณ์นี้ของเขาดูว่าจะดีหรือไม่
ในบางอุปกรณ์จะไม่สามารถใช้งานได้เมื่อปิด Wi-Fi
หมายเหตุ:จาก Android 6.x จะส่งคืนที่อยู่ mac ปลอมที่สอดคล้องกัน:02:00:00:00:00:00
android.permission.ACCESS_WIFI_STATE
02:00:00:00:00:00
มีข้อมูลที่เป็นประโยชน์ค่อนข้างเป็นที่นี่
ครอบคลุมห้าประเภท ID ที่แตกต่างกัน:
android.permission.READ_PHONE_STATE
)android.permission.ACCESS_WIFI_STATE
)android.permission.BLUETOOTH
)อย่างเป็นทางการ Android Developers Blog ตอนนี้มีบทความเต็มรูปแบบเพียงเกี่ยวกับเรื่องนี้มากระบุการติดตั้ง App
ที่Google I / O Reto Meier เปิดตัวคำตอบที่มีประสิทธิภาพเกี่ยวกับวิธีการเข้าถึงสิ่งนี้ซึ่งน่าจะตอบสนองความต้องการของนักพัฒนาส่วนใหญ่ในการติดตามผู้ใช้ในการติดตั้ง แอนโทนี่โนแลนแสดงทิศทางในคำตอบของเขา แต่ฉันคิดว่าฉันเขียนแนวทางเต็มรูปแบบเพื่อให้ผู้อื่นสามารถเห็นวิธีการใช้งานได้อย่างง่ายดาย
วิธีนี้จะให้ ID ผู้ใช้ที่ไม่ระบุชื่อและปลอดภัยซึ่งจะคงอยู่ตลอดไปสำหรับผู้ใช้ในอุปกรณ์ต่าง ๆ (ตามบัญชี Google หลัก) และระหว่างการติดตั้ง วิธีการพื้นฐานคือการสร้าง ID ผู้ใช้แบบสุ่มและเก็บไว้ในการตั้งค่าที่แชร์ของแอพ จากนั้นคุณใช้ตัวแทนสำรองของ Google เพื่อจัดเก็บค่ากำหนดที่แชร์ซึ่งเชื่อมโยงกับบัญชี Google ในคลาวด์
เรามาดูวิธีการทั้งหมด ก่อนอื่นเราต้องสร้างการสำรองข้อมูลสำหรับ SharedPreferences โดยใช้บริการสำรองข้อมูล Android เริ่มต้นด้วยการลงทะเบียนแอปของคุณผ่านทางhttp://developer.android.com/google/backup/signup.html
เริ่มต้นโดยการลงทะเบียนผ่านทางแอปของคุณ
Google จะให้รหัสบริการสำรองซึ่งคุณต้องเพิ่มในไฟล์ Manifest คุณต้องบอกแอปพลิเคชันเพื่อใช้ BackupAgent ดังนี้:
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="your_backup_service_key" />
</application>
จากนั้นคุณต้องสร้างเอเจนต์สำรองและบอกให้ใช้เอเจนต์ผู้ช่วยเหลือสำหรับการแชร์ล่วงหน้า:
public class MyBackupAgent extends BackupAgentHelper {
// The name of the SharedPreferences file
static final String PREFS = "user_preferences";
// A key to uniquely identify the set of backup data
static final String PREFS_BACKUP_KEY = "prefs";
// Allocate a helper and add it to the backup agent
@Override
public void onCreate() {
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
เพื่อให้การสำรองข้อมูลเสร็จสมบูรณ์คุณต้องสร้างตัวอย่างของ BackupManager ในกิจกรรมหลักของคุณ:
BackupManager backupManager = new BackupManager(context);
สุดท้ายสร้าง ID ผู้ใช้หากยังไม่มีอยู่และเก็บไว้ใน SharedPreferences:
public static String getUserID(Context context) {
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
if (uniqueID == null) {
SharedPreferences sharedPrefs = context.getSharedPreferences(
MyBackupAgent.PREFS, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
//backup the changes
BackupManager mBackupManager = new BackupManager(context);
mBackupManager.dataChanged();
}
}
return uniqueID;
}
User_ID นี้จะคงอยู่ตลอดการติดตั้งแม้ว่าผู้ใช้จะย้ายอุปกรณ์
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการนี้ดูพูดคุย Reto ของ
และสำหรับรายละเอียดของวิธีการใช้ตัวแทนสำรองดูการสำรองข้อมูล ฉันขอแนะนำส่วนที่ด้านล่างของการทดสอบโดยเฉพาะอย่างยิ่งการสำรองข้อมูลไม่ได้เกิดขึ้นอย่างทันทีทันใดเพื่อทดสอบคุณต้องบังคับให้มีการสำรองข้อมูล
ฉันคิดว่านี่เป็นวิธีที่แน่นอนในการสร้างโครงกระดูกสำหรับรหัสเฉพาะ ... ลองดู
Pseudo-Unique ID ที่ทำงานบนอุปกรณ์ Android ทั้งหมดอุปกรณ์บางอย่างไม่มีโทรศัพท์ (เช่นแท็บเล็ต) หรือด้วยเหตุผลบางอย่างคุณไม่ต้องการรวมสิทธิ์ READ_PHONE_STATE คุณยังสามารถอ่านรายละเอียดเช่นเวอร์ชั่น ROM ชื่อผู้ผลิตประเภท CPU และรายละเอียดฮาร์ดแวร์อื่น ๆ ซึ่งจะเหมาะสมอย่างยิ่งหากคุณต้องการใช้ ID สำหรับการตรวจสอบรหัสอนุกรมหรือวัตถุประสงค์ทั่วไปอื่น ๆ ID ที่คำนวณด้วยวิธีนี้จะไม่ซ้ำกัน: เป็นไปได้ที่จะหาอุปกรณ์สองชิ้นที่มี ID เดียวกัน (ขึ้นอยู่กับฮาร์ดแวร์และอิมเมจ ROM เดียวกัน) แต่การเปลี่ยนแปลงในแอปพลิเคชันในโลกแห่งความเป็นจริงนั้นไม่มีความสำคัญ เพื่อจุดประสงค์นี้คุณสามารถใช้คลาส Build:
String m_szDevIDShort = "35" + //we make this look like a valid IMEI
Build.BOARD.length()%10+ Build.BRAND.length()%10 +
Build.CPU_ABI.length()%10 + Build.DEVICE.length()%10 +
Build.DISPLAY.length()%10 + Build.HOST.length()%10 +
Build.ID.length()%10 + Build.MANUFACTURER.length()%10 +
Build.MODEL.length()%10 + Build.PRODUCT.length()%10 +
Build.TAGS.length()%10 + Build.TYPE.length()%10 +
Build.USER.length()%10 ; //13 digits
สมาชิก Build ส่วนใหญ่เป็นสายอักขระสิ่งที่เรากำลังทำที่นี่คือการใช้ความยาวและแปลงมันผ่านโมดูโลเป็นตัวเลข เรามี 13 หลักดังกล่าวและเรากำลังเพิ่มอีกสองหน้า (35) เพื่อให้มีขนาด ID เดียวกับ IMEI (15 หลัก) มีความเป็นไปได้อื่น ๆ ที่นี่ดีเพียงแค่ดูที่สตริงเหล่านี้ 355715565309247
ผลตอบแทนที่ได้สิ่งที่ต้องการ ไม่จำเป็นต้องได้รับอนุญาตพิเศษทำให้วิธีนี้สะดวกมาก
(ข้อมูลเพิ่มเติม: เทคนิคที่ให้ไว้ข้างต้นถูกคัดลอกมาจากบทความในPocket Magic )
รหัสต่อไปนี้จะคืนค่าหมายเลขซีเรียลของอุปกรณ์โดยใช้ Android API ที่ซ่อนอยู่ แต่รหัสนี้ใช้ไม่ได้กับ Samsung Galaxy Tab เพราะไม่ได้ตั้ง "ro.serialno" ในอุปกรณ์นี้
String serial = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serial = (String) get.invoke(c, "ro.serialno");
}
catch (Exception ignored) {
}
ro.serialno
Settings.Secure.ANDROID_ID
ดังนั้นพวกเขาจึงเป็นตัวแทนที่แตกต่างกันของค่าเดียวกัน
ANDROID_ID
ได้รับมาจากมัน
android.os.Build.SERIAL
android.os.Build.SERIAL
จะเลิกใช้ใน Android O ดูandroid-developers.googleblog.com/2017/04/…
มันเป็นคำถามง่าย ๆ ที่ไม่มีคำตอบง่ายๆ
ยิ่งไปกว่านั้นคำตอบที่มีอยู่ทั้งหมดนี้ไม่ว่าจะล้าสมัยหรือไม่น่าเชื่อถือ
ดังนั้นหากคุณกำลังค้นหาทางออกในปี 2020ถ้าคุณกำลังมองหาวิธีการแก้ปัญหาในปี 2020
ต่อไปนี้เป็นสิ่งที่ควรคำนึงถึง:
ตัวระบุฮาร์ดแวร์ทั้งหมด (SSAID, IMEI, MAC และอื่น ๆ ) ไม่น่าเชื่อถือสำหรับอุปกรณ์ที่ไม่ใช่ของ Google (ทุกอย่างยกเว้น Pixels และ Nexuses) ซึ่งเป็นอุปกรณ์ที่ใช้งานอยู่ทั่วโลกมากกว่า 50% ดังนั้นแนวทางปฏิบัติที่ดีที่สุดสำหรับ Androidอย่างเป็นทางการระบุไว้ชัดเจน:
หลีกเลี่ยงการใช้ตัวระบุฮาร์ดแวร์เช่น SSAID (Android ID), IMEI, ที่อยู่ MAC ฯลฯ ...
นั่นทำให้คำตอบส่วนใหญ่ข้างต้นไม่ถูกต้อง นอกจากนี้เนื่องจากการอัปเดตความปลอดภัยของ Android ที่แตกต่างกันบางรายการต้องการการอนุญาตรันไทม์ที่ใหม่กว่าและเข้มงวดกว่าซึ่งผู้ใช้สามารถปฏิเสธได้
เป็นตัวอย่างCVE-2018-9489
ที่มีผลต่อเทคนิค WIFI ทั้งหมดที่กล่าวถึงข้างต้น
ซึ่งทำให้ตัวระบุเหล่านั้นไม่เพียง แต่ไม่น่าเชื่อถือ แต่ยังไม่สามารถเข้าถึงได้ในหลายกรณี
ดังนั้นในคำที่ง่ายกว่า: อย่าใช้เทคนิคเหล่านั้นไม่ได้ใช้เทคนิคเหล่านั้น
คำตอบอื่น ๆ อีกมากมายที่นี่แนะนำให้ใช้AdvertisingIdClient
ซึ่งยังไม่เข้ากันเนื่องจากการออกแบบควรใช้สำหรับการทำโปรไฟล์โฆษณาเท่านั้น มันยังระบุไว้ในเอกสารอ้างอิงอย่างเป็นทางการ
ใช้ ID โฆษณาสำหรับการทำโปรไฟล์ผู้ใช้หรือโฆษณาใช้เคสเท่านั้น
ไม่เพียง แต่ไม่น่าเชื่อถือสำหรับการระบุอุปกรณ์ แต่คุณยังต้องปฏิบัติตามนโยบายความเป็นส่วนตัวของผู้ใช้เกี่ยวกับการติดตามโฆษณาซึ่งระบุไว้อย่างชัดเจนว่าผู้ใช้สามารถรีเซ็ตหรือบล็อกได้ตลอดเวลา
ดังนั้นอย่าใช้ด้วย
เนื่องจากคุณไม่สามารถมีตัวระบุอุปกรณ์ที่เชื่อถือได้ทั่วโลกและเชื่อถือได้ การอ้างอิงอย่างเป็นทางการของ Android แนะนำ:
ใช้ FirebaseInstanceId หรือ GUID ที่เก็บไว้เป็นส่วนตัวเมื่อใดก็ตามที่เป็นไปได้สำหรับกรณีการใช้งานอื่น ๆ ทั้งหมดยกเว้นการป้องกันการฉ้อโกงการชำระเงินและการโทรศัพท์
มันไม่เหมือนใครสำหรับการติดตั้งแอปพลิเคชันบนอุปกรณ์ดังนั้นเมื่อผู้ใช้ถอนการติดตั้งแอป - มันถูกลบออกดังนั้นจึงไม่น่าเชื่อถือ 100% แต่เป็นสิ่งที่ดีที่สุดถัดไป
หากต้องการใช้FirebaseInstanceId
เพิ่มการพึ่งพาการส่งข้อความ Firebase ล่าสุดลงในระดับเสียงของคุณ
implementation 'com.google.firebase:firebase-messaging:20.2.0'
และใช้รหัสด้านล่างในชุดข้อความพื้นหลัง:
String reliableIdentifier = FirebaseInstanceId.getInstance().getId();
หากคุณต้องการจัดเก็บข้อมูลระบุอุปกรณ์บนเซิร์ฟเวอร์ระยะไกลของคุณอย่าเก็บไว้ตามที่เป็น (ข้อความธรรมดา) แต่มีแฮชที่ใส่เกลือกัญชาด้วยเกลือ
วันนี้มันไม่ใช่แค่วิธีปฏิบัติที่ดีที่สุดคุณต้องทำตามกฎหมายตามGDPR - ตัวระบุและข้อบังคับที่คล้ายกัน
ใช้รหัสด้านล่างคุณจะได้รับรหัสอุปกรณ์ที่ไม่ซ้ำกันของอุปกรณ์ Android OS เป็นสตริง
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
สิ่งหนึ่งที่ฉันจะเพิ่ม - ฉันมีหนึ่งในสถานการณ์พิเศษเหล่านั้น
โดยใช้:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
ปรากฎว่าแม้ว่า Viewsonic G แท็บเล็ตของฉันจะรายงาน DeviceID ที่ไม่เป็นโมฆะ G แท็บเล็ตทุกเครื่องจะรายงานหมายเลขเดียวกัน
ทำให้การเล่น "Pocket Empires" น่าสนใจซึ่งช่วยให้คุณสามารถเข้าถึงบัญชีของใครบางคนโดยใช้ DeviceID "ที่ไม่ซ้ำใคร"
อุปกรณ์ของฉันไม่มีวิทยุมือถือ
9774d56d682e549c
?
สำหรับคำแนะนำรายละเอียดเกี่ยวกับวิธีการที่จะได้รับรหัสเฉพาะสำหรับแต่ละอุปกรณ์ Android ของแอพลิเคชันของคุณมีการติดตั้งจากดูอย่างเป็นทางการของนักพัฒนาซอฟต์แวร์ Android บล็อกโพสต์ระบุการติดตั้ง App
ดูเหมือนว่าวิธีที่ดีที่สุดคือให้คุณสร้างด้วยตัวคุณเองเมื่อทำการติดตั้งแล้วอ่านมันเมื่อเปิดใช้งานแอปพลิเคชันอีกครั้ง
ฉันเองพบว่ายอมรับได้ แต่ไม่เหมาะ ไม่มีตัวระบุหนึ่งตัวที่ Android จัดทำขึ้นในทุกกรณีเนื่องจากส่วนใหญ่ขึ้นอยู่กับสถานะวิทยุของโทรศัพท์ (เปิด / ปิด Wi-Fi, เปิด / ปิดสัญญาณโทรศัพท์มือถือ, เปิด / ปิดบลูทู ธ ) คนอื่น ๆ เช่นSettings.Secure.ANDROID_ID
จะต้องดำเนินการโดยผู้ผลิตและไม่รับประกันว่าจะไม่ซ้ำกัน
ต่อไปนี้เป็นตัวอย่างของการเขียนข้อมูลไปยังไฟล์การติดตั้งที่จะถูกจัดเก็บพร้อมกับข้อมูลอื่น ๆ ที่แอปพลิเคชันบันทึกไว้ในเครื่อง
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();
}
}
เพิ่มรหัสด้านล่างในไฟล์คลาส:
final TelephonyManager tm = (TelephonyManager) getBaseContext()
.getSystemService(SplashActivity.TELEPHONY_SERVICE);
final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
Log.v("DeviceIMEI", "" + tmDevice);
tmSerial = "" + tm.getSimSerialNumber();
Log.v("GSM devices Serial Number[simcard] ", "" + tmSerial);
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(),
android.provider.Settings.Secure.ANDROID_ID);
Log.v("androidId CDMA devices", "" + androidId);
UUID deviceUuid = new UUID(androidId.hashCode(),
((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();
Log.v("deviceIdUUID universally unique identifier", "" + deviceId);
String deviceModelName = android.os.Build.MODEL;
Log.v("Model Name", "" + deviceModelName);
String deviceUSER = android.os.Build.USER;
Log.v("Name USER", "" + deviceUSER);
String devicePRODUCT = android.os.Build.PRODUCT;
Log.v("PRODUCT", "" + devicePRODUCT);
String deviceHARDWARE = android.os.Build.HARDWARE;
Log.v("HARDWARE", "" + deviceHARDWARE);
String deviceBRAND = android.os.Build.BRAND;
Log.v("BRAND", "" + deviceBRAND);
String myVersion = android.os.Build.VERSION.RELEASE;
Log.v("VERSION.RELEASE", "" + myVersion);
int sdkVersion = android.os.Build.VERSION.SDK_INT;
Log.v("VERSION.SDK_INT", "" + sdkVersion);
เพิ่มใน AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
TelephonyManager
และANDROID_ID
ได้รับโดย:String deviceId;
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
deviceId = mTelephony.getDeviceId();
}
else {
deviceId = Secure.getString(
getApplicationContext().getContentResolver(),
Secure.ANDROID_ID);
}
แต่ผมขอแนะนำวิธีการที่แนะนำโดย Google โปรดดูการระบุการติดตั้ง App
มีวิธีการที่แตกต่างกันมากมายในการANDROID_ID
แก้ไขปัญหาเหล่านั้น(อาจเป็นnull
บางครั้งหรืออุปกรณ์ของรุ่นเฉพาะส่งคืน ID เดียวกันเสมอ) ด้วยข้อดีข้อเสีย:
ฉันชอบที่จะใช้การใช้งาน OpenUDID ที่มีอยู่ (ดูhttps://github.com/ylechelle/OpenUDID ) สำหรับ Android (ดูhttps://github.com/vieux/OpenUDID ) มันง่ายที่จะรวมและใช้ประโยชน์จากANDROID_ID
กับ fallbacks สำหรับปัญหาที่กล่าวถึงข้างต้น
แล้วIMEIล่ะ นั่นเป็นเอกลักษณ์สำหรับ Android หรืออุปกรณ์มือถืออื่น ๆ
นี่คือวิธีที่ฉันสร้าง id เฉพาะ:
public static String getDeviceId(Context ctx)
{
TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
String tmDevice = tm.getDeviceId();
String androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
String serial = null;
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) serial = Build.SERIAL;
if(tmDevice != null) return "01" + tmDevice;
if(androidId != null) return "02" + androidId;
if(serial != null) return "03" + serial;
// other alternatives (i.e. Wi-Fi MAC, Bluetooth MAC, etc.)
return null;
}
สองเซ็นต์ของฉัน - NB นี่เป็นอุปกรณ์ (ผิดพลาด) รหัสที่ไม่ซ้ำกัน - ไม่ได้ติดตั้งอย่างใดอย่างหนึ่งตามที่กล่าวไว้ในบล็อกของนักพัฒนาของ Android
โปรดทราบว่าโซลูชันที่ให้บริการโดย @emmby จะกลับไปใช้ในแต่ละ ID แอปพลิเคชันเนื่องจาก SharedPreferences ไม่ได้รับการซิงโครไนซ์ข้ามกระบวนการ (ดูที่นี่และที่นี่ ) ดังนั้นฉันจึงหลีกเลี่ยงสิ่งนี้โดยสิ้นเชิง
แต่ฉันสรุปกลยุทธ์ต่าง ๆ ในการรับ (อุปกรณ์) ID ใน enum - การเปลี่ยนลำดับของค่าคงที่ enum มีผลต่อลำดับความสำคัญของวิธีต่างๆในการรับ ID ID ที่ไม่ใช่นัลแรกถูกส่งคืนหรือมีการโยนข้อยกเว้น (ตามแนวทางปฏิบัติที่ดีของ Java ที่ไม่ให้ความหมายเป็นโมฆะ) ตัวอย่างเช่นฉันมีโทรศัพท์หนึ่งตัวแรก - แต่ตัวเลือกเริ่มต้นที่ดีคือ ANDROID_ID เบต้า:
import android.Manifest.permission;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
// TODO : hash
public final class DeviceIdentifier {
private DeviceIdentifier() {}
/** @see http://code.google.com/p/android/issues/detail?id=10603 */
private static final String ANDROID_ID_BUG_MSG = "The device suffers from "
+ "the Android ID bug - its ID is the emulator ID : "
+ IDs.BUGGY_ANDROID_ID;
private static volatile String uuid; // volatile needed - see EJ item 71
// need lazy initialization to get a context
/**
* Returns a unique identifier for this device. The first (in the order the
* enums constants as defined in the IDs enum) non null identifier is
* returned or a DeviceIDException is thrown. A DeviceIDException is also
* thrown if ignoreBuggyAndroidID is false and the device has the Android ID
* bug
*
* @param ctx
* an Android constant (to retrieve system services)
* @param ignoreBuggyAndroidID
* if false, on a device with the android ID bug, the buggy
* android ID is not returned instead a DeviceIDException is
* thrown
* @return a *device* ID - null is never returned, instead a
* DeviceIDException is thrown
* @throws DeviceIDException
* if none of the enum methods manages to return a device ID
*/
public static String getDeviceIdentifier(Context ctx,
boolean ignoreBuggyAndroidID) throws DeviceIDException {
String result = uuid;
if (result == null) {
synchronized (DeviceIdentifier.class) {
result = uuid;
if (result == null) {
for (IDs id : IDs.values()) {
try {
result = uuid = id.getId(ctx);
} catch (DeviceIDNotUniqueException e) {
if (!ignoreBuggyAndroidID)
throw new DeviceIDException(e);
}
if (result != null) return result;
}
throw new DeviceIDException();
}
}
}
return result;
}
private static enum IDs {
TELEPHONY_ID {
@Override
String getId(Context ctx) {
// TODO : add a SIM based mechanism ? tm.getSimSerialNumber();
final TelephonyManager tm = (TelephonyManager) ctx
.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null) {
w("Telephony Manager not available");
return null;
}
assertPermission(ctx, permission.READ_PHONE_STATE);
return tm.getDeviceId();
}
},
ANDROID_ID {
@Override
String getId(Context ctx) throws DeviceIDException {
// no permission needed !
final String andoidId = Secure.getString(
ctx.getContentResolver(),
android.provider.Settings.Secure.ANDROID_ID);
if (BUGGY_ANDROID_ID.equals(andoidId)) {
e(ANDROID_ID_BUG_MSG);
throw new DeviceIDNotUniqueException();
}
return andoidId;
}
},
WIFI_MAC {
@Override
String getId(Context ctx) {
WifiManager wm = (WifiManager) ctx
.getSystemService(Context.WIFI_SERVICE);
if (wm == null) {
w("Wifi Manager not available");
return null;
}
assertPermission(ctx, permission.ACCESS_WIFI_STATE); // I guess
// getMacAddress() has no java doc !!!
return wm.getConnectionInfo().getMacAddress();
}
},
BLUETOOTH_MAC {
@Override
String getId(Context ctx) {
BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
if (ba == null) {
w("Bluetooth Adapter not available");
return null;
}
assertPermission(ctx, permission.BLUETOOTH);
return ba.getAddress();
}
}
// TODO PSEUDO_ID
// http://www.pocketmagic.net/2011/02/android-unique-device-id/
;
static final String BUGGY_ANDROID_ID = "9774d56d682e549c";
private final static String TAG = IDs.class.getSimpleName();
abstract String getId(Context ctx) throws DeviceIDException;
private static void w(String msg) {
Log.w(TAG, msg);
}
private static void e(String msg) {
Log.e(TAG, msg);
}
}
private static void assertPermission(Context ctx, String perm) {
final int checkPermission = ctx.getPackageManager().checkPermission(
perm, ctx.getPackageName());
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Permission " + perm + " is required");
}
}
// =========================================================================
// Exceptions
// =========================================================================
public static class DeviceIDException extends Exception {
private static final long serialVersionUID = -8083699995384519417L;
private static final String NO_ANDROID_ID = "Could not retrieve a "
+ "device ID";
public DeviceIDException(Throwable throwable) {
super(NO_ANDROID_ID, throwable);
}
public DeviceIDException(String detailMessage) {
super(detailMessage);
}
public DeviceIDException() {
super(NO_ANDROID_ID);
}
}
public static final class DeviceIDNotUniqueException extends
DeviceIDException {
private static final long serialVersionUID = -8940090896069484955L;
public DeviceIDNotUniqueException() {
super(ANDROID_ID_BUG_MSG);
}
}
}
มีคำตอบมากกว่า 30 ข้อที่นี่และบางคำเหมือนกันและบางคำตอบไม่เหมือนกัน คำตอบนี้ขึ้นอยู่กับคำตอบเหล่านั้นสองสามข้อ หนึ่งในนั้นคือคำตอบของ @Lenn Dolling
มันรวม 3 ID และสร้างสตริงเลขฐานสิบหก 32 หลัก มันทำงานได้ดีมากสำหรับฉัน
3 ID คือ:
Pseudo-ID - มันถูกสร้างขึ้นตามข้อกำหนดทางกายภาพของอุปกรณ์
ANDROID_ID - Settings.Secure.ANDROID_ID
ที่อยู่บลูทู ธ - ที่อยู่อะแดปเตอร์ Bluetooth
มันจะส่งคืนสิ่งนี้: 551F27C060712A72730B0A0F734064B1
หมายเหตุ: คุณสามารถเพิ่ม ID ได้มากขึ้นในlongId
สตริง ตัวอย่างเช่น Serial # ที่อยู่อะแดปเตอร์ไร้สาย IMEI วิธีที่คุณทำให้อุปกรณ์นี้แตกต่างกันมากขึ้น
@SuppressWarnings("deprecation")
@SuppressLint("HardwareIds")
public static String generateDeviceIdentifier(Context context) {
String pseudoId = "35" +
Build.BOARD.length() % 10 +
Build.BRAND.length() % 10 +
Build.CPU_ABI.length() % 10 +
Build.DEVICE.length() % 10 +
Build.DISPLAY.length() % 10 +
Build.HOST.length() % 10 +
Build.ID.length() % 10 +
Build.MANUFACTURER.length() % 10 +
Build.MODEL.length() % 10 +
Build.PRODUCT.length() % 10 +
Build.TAGS.length() % 10 +
Build.TYPE.length() % 10 +
Build.USER.length() % 10;
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String btId = "";
if (bluetoothAdapter != null) {
btId = bluetoothAdapter.getAddress();
}
String longId = pseudoId + androidId + btId;
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(longId.getBytes(), 0, longId.length());
// get md5 bytes
byte md5Bytes[] = messageDigest.digest();
// creating a hex string
String identifier = "";
for (byte md5Byte : md5Bytes) {
int b = (0xFF & md5Byte);
// if it is a single digit, make sure it have 0 in front (proper padding)
if (b <= 0xF) {
identifier += "0";
}
// add number to string
identifier += Integer.toHexString(b);
}
// hex string to uppercase
identifier = identifier.toUpperCase();
return identifier;
} catch (Exception e) {
Log.e("TAG", e.toString());
}
return "";
}
longId
และเก็บไว้ในไฟล์จะทำให้มันเป็นตัวระบุที่ไม่ซ้ำใคร:String uuid = UUID.randomUUID().toString();
longId
ไป เปลี่ยนหนึ่งบรรทัดเช่นนี้: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
สิ่งนี้รับประกันว่า ID ที่สร้างจะไม่ซ้ำกัน
อีกวิธีคือใช้/sys/class/android_usb/android0/iSerial
ในแอพที่ไม่มีสิทธิ์ใด ๆ
user@creep:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root root 4096 2013-01-10 21:08 iSerial
user@creep:~$ adb shell cat /sys/class/android_usb/android0/iSerial
0A3CXXXXXXXXXX5
ในการทำเช่นนี้ใน Java เพียงแค่ใช้ FileInputStream เพื่อเปิดไฟล์ iSerial และอ่านตัวอักษร ตรวจสอบให้แน่ใจว่าคุณใส่ไว้ในตัวจัดการข้อยกเว้นเนื่องจากอุปกรณ์บางตัวไม่มีไฟล์นี้
อย่างน้อยอุปกรณ์ต่อไปนี้ทราบว่าไฟล์นี้สามารถอ่านได้ทั่วโลก:
นอกจากนี้คุณยังสามารถดูหมายเลขซีเรียลของฮาร์ดแวร์โพสต์บล็อกของAndroid ที่รั่วไหลไปยังแอพที่ไม่มีสิทธิพิเศษได้
TelephonyManger.getDeviceId ()ส่งคืน ID อุปกรณ์เฉพาะเช่น IMEI สำหรับ GSM และ MEID หรือ ESN สำหรับโทรศัพท์ CDMA
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String myAndroidDeviceId = mTelephony.getDeviceId();
แต่ฉันแนะนำให้ใช้:
Settings.Secure.ANDROID_IDที่ส่งคืน ID Android เป็นสตริงเลขฐานสิบหก 64 บิตที่ไม่ซ้ำกัน
String myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
บางครั้งTelephonyManger.getDeviceId ()จะคืนค่า null ดังนั้นเพื่อให้มั่นใจว่า id ที่ไม่ซ้ำกันคุณจะใช้วิธีนี้:
public String getUniqueID(){
String myAndroidDeviceId = "";
TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null){
myAndroidDeviceId = mTelephony.getDeviceId();
}else{
myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
}
return myAndroidDeviceId;
}
สำหรับการรับรู้ฮาร์ดแวร์ของอุปกรณ์ Android คุณสามารถตรวจสอบที่อยู่ MAC
คุณสามารถทำได้เช่นนั้น:
ใน AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
ตอนนี้ในรหัสของคุณ:
List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface interface : interfacesList) {
// This will give you the interface MAC ADDRESS
interface.getHardwareAddress();
}
ในอุปกรณ์ Android ทุกเครื่องอย่างน้อยพวกมันก็คือ "wlan0" Interface witch เป็นชิป Wi-Fi รหัสนี้ใช้งานได้แม้ไม่ได้เปิด Wi-Fi
ป.ล. พวกเขาเป็นกลุ่มของการเชื่อมต่ออื่น ๆ ที่คุณจะได้รับจากรายการที่มี MACS แต่สิ่งนี้สามารถเปลี่ยนระหว่างโทรศัพท์
ฉันใช้รหัสต่อไปนี้เพื่อรับIMEI
หรือใช้ Secure ANDROID_ID
เป็นทางเลือกเมื่ออุปกรณ์ไม่มีความสามารถในการใช้โทรศัพท์:
String identifier = null;
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));
if (tm != null)
identifier = tm.getDeviceId();
if (identifier == null || identifier .length() == 0)
identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);
โดยเฉพาะอย่างยิ่ง, Settings.Secure.ANDROID_ID
. นี่เป็นปริมาณ 64- บิตที่สร้างและจัดเก็บเมื่ออุปกรณ์บู๊ตเป็นครั้งแรก มันจะถูกรีเซ็ตเมื่ออุปกรณ์เช็ด
ANDROID_ID
ดูเหมือนจะเป็นตัวเลือกที่ดีสำหรับตัวระบุอุปกรณ์ที่ไม่ซ้ำใคร มีข้อเสีย: ประการแรกมันไม่น่าเชื่อถือ 100% ในการเปิดตัว Android ก่อนหน้า 2.2 (“Froyo”).
นอกจากนี้ยังมีข้อผิดพลาดที่สังเกตได้อย่างน้อยหนึ่งครั้งในโทรศัพท์มือถือยอดนิยมจากผู้ผลิตรายใหญ่ซึ่งทุกกรณีมี ANDROID_ID เดียวกัน
เพื่อทำความเข้าใจกับรหัสที่ไม่ซ้ำที่มีอยู่ในอุปกรณ์ Android ใช้คู่มืออย่างเป็นทางการนี้
แนวทางปฏิบัติที่ดีที่สุดสำหรับตัวระบุที่ไม่ซ้ำกัน:
IMEI, ที่อยู่ Mac, รหัสอินสแตนซ์, GUID, SSAID, รหัสโฆษณา, Safety Net API เพื่อตรวจสอบอุปกรณ์
https://developer.android.com/training/articles/user-data-ids
รหัส Google อินสแตนซ์
เผยแพร่เมื่อ I / O 2015; บน Android ต้องใช้บริการเล่น 7.5
https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation
InstanceID iid = InstanceID.getInstance( context ); // Google docs are wrong - this requires context
String id = iid.getId(); // blocking call
ดูเหมือนว่า Google จะใช้ ID นี้เพื่อระบุการติดตั้งใน Android, Chrome และ iOS
มันระบุถึงการติดตั้งแทนที่จะเป็นอุปกรณ์ แต่หลังจากนั้นอีกครั้ง ANDROID_ID (ซึ่งเป็นคำตอบที่ยอมรับได้) จะไม่ระบุอุปกรณ์อีกต่อไป ด้วย ARC runtime ANDROID_ID ใหม่จะถูกสร้างขึ้นสำหรับการติดตั้งทุกครั้ง ( รายละเอียดที่นี่ ) เช่นเดียวกับ ID อินสแตนซ์ใหม่นี้ นอกจากนี้ฉันคิดว่าการระบุการติดตั้ง (ไม่ใช่อุปกรณ์) คือสิ่งที่พวกเราส่วนใหญ่มองหา
ข้อดีของ ID อินสแตนซ์
สำหรับฉันดูเหมือนว่า Google ตั้งใจจะให้มันใช้เพื่อจุดประสงค์นี้ (ระบุการติดตั้งของคุณ) มันเป็นแพลตฟอร์มข้ามและสามารถใช้เพื่อวัตถุประสงค์อื่น ๆ ได้ (ดูลิงก์ด้านบน)
หากคุณใช้ GCM ในที่สุดคุณจะต้องใช้ ID อินสแตนซ์นี้เพราะคุณจำเป็นต้องใช้เพื่อรับโทเค็น GCM (ซึ่งจะแทนที่ ID การลงทะเบียน GCM เก่า)
ข้อเสีย / ปัญหา
ในการนำไปใช้งานปัจจุบัน (GPS 7.5) จะมีการดึง ID อินสแตนซ์จากเซิร์ฟเวอร์เมื่อแอปของคุณร้องขอ ซึ่งหมายความว่าการโทรด้านบนเป็นการปิดกั้นการโทร - ในการทดสอบทางวิทยาศาสตร์ของฉันจะใช้เวลา 1-3 วินาทีหากอุปกรณ์ออนไลน์และ 0.5 - 1.0 วินาทีหากออฟไลน์ (สมมุติว่านี่เป็นระยะเวลารอก่อนที่จะเลิกและสร้าง รหัสสุ่ม) สิ่งนี้ได้รับการทดสอบในอเมริกาเหนือใน Nexus 5 พร้อม Android 5.1.1 และ GPS 7.5
หากคุณใช้ ID เพื่อจุดประสงค์ที่พวกเขาต้องการ - เช่น การตรวจสอบแอปการระบุแอป GCM - ฉันคิดว่า 1-3 วินาทีนี้อาจสร้างความรำคาญ (ขึ้นอยู่กับแอพของคุณ)
ANDROID_ID
โปรดอ่านคำตอบนี้และข้อผิดพลาดนี้