提交 21ae9c08 authored 作者: 史晓晨's avatar 史晓晨

fix:转换kotlin,修复无法获取设备id问题

上级 1b4c625a
package com.clx.devideId.device_id_plugin;
package com.clx.devideId.device_id_plugin
import android.annotation.SuppressLint;
import android.content.Context;
import android.provider.Settings;
import android.text.TextUtils;
import java.util.UUID;
import android.annotation.SuppressLint
import android.content.Context
import android.provider.Settings
import java.util.UUID
/**
* <pre>
......@@ -14,126 +13,132 @@ import java.util.UUID;
* desc : utils about device
* </pre>
*/
public final class DeviceUtils {
object DeviceUtils {
@SuppressLint("StaticFieldLeak")
private var mContext: Context? = null
private DeviceUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
private const val KEY_UDID = "KEY_UDID"
@SuppressLint("StaticFieldLeak")
private static Context mContext;
@Volatile
private var udid: String? = null
public static void init(Context context) {
mContext = context;
/**
* 初始化设备工具类
*/
fun init(context: Context?) {
if (context == null) return
mContext = context.applicationContext
}
/**
* Return the android id of device.
*
* @return the android id of device
* 获取 Android ID
*/
@SuppressLint("HardwareIds")
public static String getAndroidID() {
String id = Settings.Secure.getString(
mContext.getContentResolver(),
Settings.Secure.ANDROID_ID
);
if ("9774d56d682e549c".equals(id)) return "";
return id == null ? "" : id;
fun getAndroidID(): String {
val context = mContext ?: return ""
val id = Settings.Secure.getString(
context.contentResolver, Settings.Secure.ANDROID_ID
)
return when {
id == "9774d56d682e549c" -> "" // 过滤无效 Android ID
id.isNullOrEmpty() -> ""
else -> id
}
}
private static final String KEY_UDID = "KEY_UDID";
private volatile static String udid;
/**
* Return the unique device id.
* <pre>{1}{UUID(macAddress)}</pre>
* <pre>{2}{UUID(androidId )}</pre>
* <pre>{9}{UUID(random )}</pre>
*
* @return the unique device id
* 获取唯一设备 ID(默认前缀空、使用缓存)
*/
public static String getUniqueDeviceId() {
return getUniqueDeviceId("", true);
fun getUniqueDeviceId(): String {
return getUniqueDeviceId("", true)
}
/**
* Return the unique device id.
* <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>
* <pre>{prefix}{2}{UUID(androidId )}</pre>
* <pre>{prefix}{9}{UUID(random )}</pre>
*
* @param prefix The prefix of the unique device id.
* @return the unique device id
* 获取唯一设备 ID(指定前缀、使用缓存)
* @param prefix 设备 ID 前缀
*/
public static String getUniqueDeviceId(String prefix) {
return getUniqueDeviceId(prefix, true);
fun getUniqueDeviceId(prefix: String?): String {
return getUniqueDeviceId(prefix, true)
}
/**
* Return the unique device id.
* <pre>{1}{UUID(macAddress)}</pre>
* <pre>{2}{UUID(androidId )}</pre>
* <pre>{9}{UUID(random )}</pre>
*
* @param useCache True to use cache, false otherwise.
* @return the unique device id
* 获取唯一设备 ID(默认前缀、指定是否使用缓存)
* @param useCache 是否使用缓存
*/
public static String getUniqueDeviceId(boolean useCache) {
return getUniqueDeviceId("", useCache);
fun getUniqueDeviceId(useCache: Boolean): String {
return getUniqueDeviceId("", useCache)
}
/**
* Return the unique device id.
* <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>
* <pre>{prefix}{2}{UUID(androidId )}</pre>
* <pre>{prefix}{9}{UUID(random )}</pre>
*
* @param prefix The prefix of the unique device id.
* @param useCache True to use cache, false otherwise.
* @return the unique device id
* 获取唯一设备 ID(核心方法)
* @param prefix 设备 ID 前缀
* @param useCache 是否使用缓存
*/
public static String getUniqueDeviceId(String prefix, boolean useCache) {
fun getUniqueDeviceId(prefix: String?, useCache: Boolean): String {
val safePrefix = prefix ?: ""
if (!useCache) {
return getUniqueDeviceIdReal(prefix);
return getUniqueDeviceIdReal(safePrefix)
}
if (udid == null) {
synchronized (DeviceUtils.class) {
synchronized(DeviceUtils::class.java) {
if (udid == null) {
final String id = SPUtils.getInstance().getString(KEY_UDID, null);
if (id != null) {
udid = id;
return udid;
val cachedId = SPUtils.getInstance().getString(KEY_UDID, null)
if (cachedId != null) {
udid = cachedId
return udid!!
}
return getUniqueDeviceIdReal(prefix);
// 缓存不存在则生成新的 UDID
return getUniqueDeviceIdReal(safePrefix)
}
}
}
return udid;
return udid ?: ""
}
private static String getUniqueDeviceIdReal(String prefix) {
try {
final String androidId = getAndroidID();
if (!TextUtils.isEmpty(androidId)) {
return saveUdid(prefix + 2, androidId);
/**
* 生成真实的唯一设备 ID(无缓存)
*/
private fun getUniqueDeviceIdReal(prefix: String): String {
return try {
val androidId = getAndroidID()
if (androidId.isNotEmpty()) {
saveUdid(prefix + 2, androidId)
} else {
saveUdid(prefix + 9, "")
}
} catch (Exception ignore) {/**/}
return saveUdid(prefix + 9, "");
} catch (ignore: Exception) {
// 异常时生成随机 UUID
saveUdid(prefix + 9, "")
}
}
private static String saveUdid(String prefix, String id) {
udid = getUdid(prefix, id);
SPUtils.getInstance().put(KEY_UDID, udid);
return udid;
/**
* 保存 UDID 到 SP 并更新内存缓存(修复:id 判空 + SPUtils 操作加 try-catch)
*/
private fun saveUdid(prefix: String, id: String?): String {
val safeId = id ?: ""
udid = getUdid(prefix, safeId)
SPUtils.getInstance().put(KEY_UDID, udid!!)
return udid ?: ""
}
private static String getUdid(String prefix, String id) {
if (id.equals("")) {
return prefix + UUID.randomUUID().toString().replace("-", "");
/**
* 生成 UDID 字符串
*/
private fun getUdid(prefix: String?, id: String?): String {
val safePrefix = prefix ?: ""
val safeId = id ?: ""
return if (safeId.isEmpty()) {
// ID 为空时生成随机 UUID
safePrefix + UUID.randomUUID().toString().replace("-", "")
} else {
// ID 不为空时基于 ID 生成 UUID
safePrefix + UUID.nameUUIDFromBytes(safeId.toByteArray()).toString().replace("-", "")
}
return prefix + UUID.nameUUIDFromBytes(id.getBytes()).toString().replace("-", "");
}
}
}
\ No newline at end of file
package com.clx.devideId.device_id_plugin;
package com.clx.devideId.device_id_plugin
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import androidx.annotation.NonNull
import java.util.Collections
import java.util.WeakHashMap
/**
* <pre>
......@@ -20,406 +16,292 @@ import java.util.Set;
* </pre>
*/
@SuppressLint("ApplySharedPref")
public final class SPUtils {
private static final Map<String, SPUtils> SP_UTILS_MAP = new HashMap<>();
private final SharedPreferences sp;
object SPUtils {
// 替换 Java 的 HashMap,使用 WeakHashMap 更贴合 Kotlin 内存管理
private val SP_UTILS_MAP = WeakHashMap<String, SPUtilsHelper>()
@SuppressLint("StaticFieldLeak")
private static Context mContext;
public static void init(Context context) {
mContext = context;
}
public static SPUtils getInstance() {
return getInstance("device_info_utils");
}
private lateinit var mContext: Context
/**
* Return the single {@link SPUtils} instance
*
* @param spName The name of sp.
* @return the single {@link SPUtils} instance
* 初始化 SP 工具类(必须先调用)
*/
public static SPUtils getInstance(String spName) {
if (isSpace(spName)) spName = "spUtils";
SPUtils spUtils = SP_UTILS_MAP.get(spName);
if (spUtils == null) {
synchronized (SPUtils.class) {
spUtils = SP_UTILS_MAP.get(spName);
if (spUtils == null) {
spUtils = new SPUtils(spName);
SP_UTILS_MAP.put(spName, spUtils);
}
}
}
return spUtils;
}
private SPUtils(final String spName) {
sp = mContext.getSharedPreferences(spName, Context.MODE_PRIVATE);
fun init(context: Context) {
mContext = context.applicationContext // 强制使用 ApplicationContext 避免内存泄漏
}
/**
* Put the string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* 获取默认 SP 实例(name = "device_info_utils")
*/
public void put(@NonNull final String key, final String value) {
put(key, value, false);
fun getInstance(): SPUtilsHelper {
return getInstance("device_info_utils")
}
/**
* Put the string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
* 获取指定名称的 SP 实例
* @param spName SP 文件名
*/
public void put(@NonNull final String key, final String value, final boolean isCommit) {
if (isCommit) {
sp.edit().putString(key, value).commit();
} else {
sp.edit().putString(key, value).apply();
fun getInstance(spName: String): SPUtilsHelper {
val name = if (spName.isSpace()) "spUtils" else spName
return SP_UTILS_MAP[name] ?: synchronized(SPUtils::class.java) {
SP_UTILS_MAP[name] ?: SPUtilsHelper(name).also { SP_UTILS_MAP[name] = it }
}
}
/**
* Return the string value in sp.
*
* @param key The key of sp.
* @return the string value if sp exists or {@code ""} otherwise
* SP 操作助手类(封装具体的 SP 读写逻辑)
*/
public String getString(@NonNull final String key) {
return getString(key, "");
}
class SPUtilsHelper(private val spName: String) {
private val sp: SharedPreferences by lazy {
check(::mContext.isInitialized) { "SPUtils 未初始化!请先调用 SPUtils.init(context)" }
mContext.getSharedPreferences(spName, Context.MODE_PRIVATE)
}
/**
* Return the string value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the string value if sp exists or {@code defaultValue} otherwise
*/
public String getString(@NonNull final String key, final String defaultValue) {
return sp.getString(key, defaultValue);
}
// ===================== String 类型 =====================
/**
* 保存字符串
*/
fun put(@NonNull key: String, value: String) {
put(key, value, false)
}
/**
* Put the int value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final int value) {
put(key, value, false);
}
/**
* 保存字符串
* @param isCommit true: commit() 同步保存 | false: apply() 异步保存
*/
fun put(@NonNull key: String, value: String, isCommit: Boolean) {
val editor = sp.edit().putString(key, value)
if (isCommit) editor.commit() else editor.apply()
}
/**
* Put the int value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final int value, final boolean isCommit) {
if (isCommit) {
sp.edit().putInt(key, value).commit();
} else {
sp.edit().putInt(key, value).apply();
/**
* 获取字符串(默认值:"")
*/
fun getString(@NonNull key: String): String? {
return getString(key, "")
}
}
/**
* Return the int value in sp.
*
* @param key The key of sp.
* @return the int value if sp exists or {@code -1} otherwise
*/
public int getInt(@NonNull final String key) {
return getInt(key, -1);
}
/**
* 获取字符串
* @param defaultValue 无值时的默认值
*/
fun getString(@NonNull key: String, defaultValue: String?): String? {
return sp.getString(key, defaultValue) ?: defaultValue
}
/**
* Return the int value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the int value if sp exists or {@code defaultValue} otherwise
*/
public int getInt(@NonNull final String key, final int defaultValue) {
return sp.getInt(key, defaultValue);
}
// ===================== Int 类型 =====================
/**
* 保存 Int 类型值
*/
fun put(@NonNull key: String, value: Int) {
put(key, value, false)
}
/**
* Put the long value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final long value) {
put(key, value, false);
}
/**
* 保存 Int 类型值
* @param isCommit true: commit() 同步保存 | false: apply() 异步保存
*/
fun put(@NonNull key: String, value: Int, isCommit: Boolean) {
val editor = sp.edit().putInt(key, value)
if (isCommit) editor.commit() else editor.apply()
}
/**
* Put the long value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final long value, final boolean isCommit) {
if (isCommit) {
sp.edit().putLong(key, value).commit();
} else {
sp.edit().putLong(key, value).apply();
/**
* 获取 Int 类型值(默认值:-1)
*/
fun getInt(@NonNull key: String): Int {
return getInt(key, -1)
}
}
/**
* Return the long value in sp.
*
* @param key The key of sp.
* @return the long value if sp exists or {@code -1} otherwise
*/
public long getLong(@NonNull final String key) {
return getLong(key, -1L);
}
/**
* 获取 Int 类型值
* @param defaultValue 无值时的默认值
*/
fun getInt(@NonNull key: String, defaultValue: Int): Int {
return sp.getInt(key, defaultValue)
}
/**
* Return the long value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the long value if sp exists or {@code defaultValue} otherwise
*/
public long getLong(@NonNull final String key, final long defaultValue) {
return sp.getLong(key, defaultValue);
}
// ===================== Long 类型 =====================
/**
* 保存 Long 类型值
*/
fun put(@NonNull key: String, value: Long) {
put(key, value, false)
}
/**
* Put the float value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final float value) {
put(key, value, false);
}
/**
* 保存 Long 类型值
* @param isCommit true: commit() 同步保存 | false: apply() 异步保存
*/
fun put(@NonNull key: String, value: Long, isCommit: Boolean) {
val editor = sp.edit().putLong(key, value)
if (isCommit) editor.commit() else editor.apply()
}
/**
* Put the float value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final float value, final boolean isCommit) {
if (isCommit) {
sp.edit().putFloat(key, value).commit();
} else {
sp.edit().putFloat(key, value).apply();
/**
* 获取 Long 类型值(默认值:-1L)
*/
fun getLong(@NonNull key: String): Long {
return getLong(key, -1L)
}
}
/**
* Return the float value in sp.
*
* @param key The key of sp.
* @return the float value if sp exists or {@code -1f} otherwise
*/
public float getFloat(@NonNull final String key) {
return getFloat(key, -1f);
}
/**
* 获取 Long 类型值
* @param defaultValue 无值时的默认值
*/
fun getLong(@NonNull key: String, defaultValue: Long): Long {
return sp.getLong(key, defaultValue)
}
/**
* Return the float value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the float value if sp exists or {@code defaultValue} otherwise
*/
public float getFloat(@NonNull final String key, final float defaultValue) {
return sp.getFloat(key, defaultValue);
}
// ===================== Float 类型 =====================
/**
* 保存 Float 类型值
*/
fun put(@NonNull key: String, value: Float) {
put(key, value, false)
}
/**
* Put the boolean value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final boolean value) {
put(key, value, false);
}
/**
* 保存 Float 类型值
* @param isCommit true: commit() 同步保存 | false: apply() 异步保存
*/
fun put(@NonNull key: String, value: Float, isCommit: Boolean) {
val editor = sp.edit().putFloat(key, value)
if (isCommit) editor.commit() else editor.apply()
}
/**
* Put the boolean value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key, final boolean value, final boolean isCommit) {
if (isCommit) {
sp.edit().putBoolean(key, value).commit();
} else {
sp.edit().putBoolean(key, value).apply();
/**
* 获取 Float 类型值(默认值:-1f)
*/
fun getFloat(@NonNull key: String): Float {
return getFloat(key, -1f)
}
}
/**
* Return the boolean value in sp.
*
* @param key The key of sp.
* @return the boolean value if sp exists or {@code false} otherwise
*/
public boolean getBoolean(@NonNull final String key) {
return getBoolean(key, false);
}
/**
* 获取 Float 类型值
* @param defaultValue 无值时的默认值
*/
fun getFloat(@NonNull key: String, defaultValue: Float): Float {
return sp.getFloat(key, defaultValue)
}
/**
* Return the boolean value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the boolean value if sp exists or {@code defaultValue} otherwise
*/
public boolean getBoolean(@NonNull final String key, final boolean defaultValue) {
return sp.getBoolean(key, defaultValue);
}
// ===================== Boolean 类型 =====================
/**
* 保存 Boolean 类型值
*/
fun put(@NonNull key: String, value: Boolean) {
put(key, value, false)
}
/**
* Put the set of string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
*/
public void put(@NonNull final String key, final Set<String> value) {
put(key, value, false);
}
/**
* 保存 Boolean 类型值
* @param isCommit true: commit() 同步保存 | false: apply() 异步保存
*/
fun put(@NonNull key: String, value: Boolean, isCommit: Boolean) {
val editor = sp.edit().putBoolean(key, value)
if (isCommit) editor.commit() else editor.apply()
}
/**
* Put the set of string value in sp.
*
* @param key The key of sp.
* @param value The value of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void put(@NonNull final String key,
final Set<String> value,
final boolean isCommit) {
if (isCommit) {
sp.edit().putStringSet(key, value).commit();
} else {
sp.edit().putStringSet(key, value).apply();
/**
* 获取 Boolean 类型值(默认值:false)
*/
fun getBoolean(@NonNull key: String): Boolean {
return getBoolean(key, false)
}
}
/**
* Return the set of string value in sp.
*
* @param key The key of sp.
* @return the set of string value if sp exists
* or {@code Collections.<String>emptySet()} otherwise
*/
public Set<String> getStringSet(@NonNull final String key) {
return getStringSet(key, Collections.<String>emptySet());
}
/**
* 获取 Boolean 类型值
* @param defaultValue 无值时的默认值
*/
fun getBoolean(@NonNull key: String, defaultValue: Boolean): Boolean {
return sp.getBoolean(key, defaultValue)
}
/**
* Return the set of string value in sp.
*
* @param key The key of sp.
* @param defaultValue The default value if the sp doesn't exist.
* @return the set of string value if sp exists or {@code defaultValue} otherwise
*/
public Set<String> getStringSet(@NonNull final String key,
final Set<String> defaultValue) {
return sp.getStringSet(key, defaultValue);
}
// ===================== Set<String> 类型 =====================
/**
* 保存 String 集合
*/
fun put(@NonNull key: String, value: Set<String>) {
put(key, value, false)
}
/**
* Return all values in sp.
*
* @return all values in sp
*/
public Map<String, ?> getAll() {
return sp.getAll();
}
/**
* 保存 String 集合
* @param isCommit true: commit() 同步保存 | false: apply() 异步保存
*/
fun put(@NonNull key: String, value: Set<String>, isCommit: Boolean) {
val editor = sp.edit().putStringSet(key, value)
if (isCommit) editor.commit() else editor.apply()
}
/**
* Return whether the sp contains the preference.
*
* @param key The key of sp.
* @return {@code true}: yes<br>{@code false}: no
*/
public boolean contains(@NonNull final String key) {
return sp.contains(key);
}
/**
* 获取 String 集合(默认值:空集合)
*/
fun getStringSet(@NonNull key: String): Set<String> {
return getStringSet(key, emptySet())
}
/**
* Remove the preference in sp.
*
* @param key The key of sp.
*/
public void remove(@NonNull final String key) {
remove(key, false);
}
/**
* 获取 String 集合
* @param defaultValue 无值时的默认值
*/
fun getStringSet(@NonNull key: String, defaultValue: Set<String>): Set<String> {
return sp.getStringSet(key, defaultValue) ?: Collections.emptySet()
}
/**
* Remove the preference in sp.
*
* @param key The key of sp.
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void remove(@NonNull final String key, final boolean isCommit) {
if (isCommit) {
sp.edit().remove(key).commit();
} else {
sp.edit().remove(key).apply();
// ===================== 通用方法 =====================
/**
* 获取 SP 中所有键值对
*/
fun getAll(): Map<String, *> {
return sp.all
}
}
/**
* Remove all preferences in sp.
*/
public void clear() {
clear(false);
}
/**
* 判断 SP 中是否包含指定 key
*/
fun contains(@NonNull key: String): Boolean {
return sp.contains(key)
}
/**
* Remove all preferences in sp.
*
* @param isCommit True to use {@link SharedPreferences.Editor#commit()},
* false to use {@link SharedPreferences.Editor#apply()}
*/
public void clear(final boolean isCommit) {
if (isCommit) {
sp.edit().clear().commit();
} else {
sp.edit().clear().apply();
/**
* 移除 SP 中指定 key 的值
*/
fun remove(@NonNull key: String) {
remove(key, false)
}
}
private static boolean isSpace(final String s) {
if (s == null) return true;
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
/**
* 移除 SP 中指定 key 的值
* @param isCommit true: commit() 同步移除 | false: apply() 异步移除
*/
fun remove(@NonNull key: String, isCommit: Boolean) {
val editor = sp.edit().remove(key)
if (isCommit) editor.commit() else editor.apply()
}
/**
* 清空 SP 中所有数据
*/
fun clear() {
clear(false)
}
/**
* 清空 SP 中所有数据
* @param isCommit true: commit() 同步清空 | false: apply() 异步清空
*/
fun clear(isCommit: Boolean) {
val editor = sp.edit().clear()
if (isCommit) editor.commit() else editor.apply()
}
return true;
}
}
/**
* 扩展函数:判断字符串是否为空/全是空白字符
*/
private fun String?.isSpace(): Boolean {
if (this == null) return true
return this.all { it.isWhitespace() }
}
}
\ No newline at end of file
......@@ -202,10 +202,10 @@ packages:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.1"
version: "1.10.2"
stack_trace:
dependency: transitive
description:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论