fix: 修复在android 6.0,Api 23闪退

This commit is contained in:
TongTongStudio
2026-06-11 20:35:48 +08:00
parent 73e373db93
commit a59370cf32
3 changed files with 315 additions and 2 deletions

View File

@@ -2,6 +2,7 @@ package com.ttstd.dialer.manager;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import com.jeremyliao.liveeventbus.LiveEventBus;
@@ -20,6 +21,8 @@ import com.ttstd.dialer.BuildConfig;
import com.ttstd.dialer.bean.CityInfo;
import com.ttstd.dialer.config.CommonConfig;
import com.ttstd.dialer.gson.GsonUtils;
import com.ttstd.dialer.parser.CsvDeserializer;
import com.ttstd.dialer.parser.LegacyCsvParser;
import com.ttstd.dialer.utils.Logger;
import com.ttstd.dialer.utils.NativeUtils;
@@ -136,7 +139,12 @@ public class WeatherManager {
@Override
public void subscribe(@NonNull ObservableEmitter<List<CityInfo>> emitter) throws Throwable {
long time = System.currentTimeMillis();
List<CityInfo> cityInfos = CsvDeserializer.deserializeFromAssets(mContext, "China-City-List-latest.csv");
List<CityInfo> cityInfos;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
cityInfos = CsvDeserializer.deserializeFromAssets(mContext, "China-City-List-latest.csv");
} else {
cityInfos = LegacyCsvParser.parseFromAssets(mContext, "China-City-List-latest.csv");
}
Logger.e(TAG, "subscribe: deserializeFromAssets time = " + (System.currentTimeMillis() - time) + "ms");
emitter.onNext(cityInfos);
}

View File

@@ -1,4 +1,4 @@
package com.ttstd.dialer.manager;
package com.ttstd.dialer.parser;
import android.content.Context;

View File

@@ -0,0 +1,305 @@
package com.ttstd.dialer.parser;
import android.content.Context;
import com.ttstd.dialer.bean.CityInfo;
import com.ttstd.dialer.utils.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 简单的CSV解析器兼容Android 5.0
* 不依赖OpenCSV库手动解析CSV文件
*/
public class LegacyCsvParser {
private static final String TAG = "SimpleCsvParser";
/**
* 从assets文件夹中的CSV文件解析为CityInfo对象列表
*
* @param context 上下文
* @param fileName assets中的CSV文件名
* @return CityInfo对象列表
*/
public static List<CityInfo> parseFromAssets(Context context, String fileName) {
InputStream inputStream = null;
BufferedReader reader = null;
List<CityInfo> cityList = new ArrayList<>();
try {
long startTime = System.currentTimeMillis();
// 从assets获取CSV文件输入流
inputStream = context.getAssets().open(fileName);
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
// 读取第一行(标题行)
String headerLine = reader.readLine();
if (headerLine == null) {
Logger.w(TAG, "CSV file is empty");
return cityList;
}
// 解析标题行,建立列名到索引的映射
String[] headers = parseLine(headerLine);
Map<String, Integer> columnIndexMap = buildColumnIndexMap(headers);
// 逐行读取数据
String line;
int lineNumber = 1; // 标题行已读从第2行开始
while ((line = reader.readLine()) != null) {
lineNumber++;
// 跳过空行
if (line.trim().isEmpty()) {
continue;
}
try {
// 解析当前行
String[] values = parseLine(line);
// 创建CityInfo对象并填充数据
CityInfo cityInfo = createCityInfo(values, columnIndexMap);
if (cityInfo != null) {
cityList.add(cityInfo);
}
} catch (Exception e) {
Logger.w(TAG, "Error parsing line " + lineNumber + ": " + e.getMessage());
}
}
long endTime = System.currentTimeMillis();
Logger.e(TAG, "parseFromAssets: parsed " + cityList.size() + " cities in "
+ (endTime - startTime) + "ms");
} catch (IOException e) {
Logger.e(TAG, "Error reading CSV file: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
Logger.e(TAG, "Error during CSV parsing: " + e.getMessage());
e.printStackTrace();
} finally {
// 确保关闭流,避免资源泄漏
try {
if (reader != null) {
reader.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
Logger.w(TAG, "Failed to close stream: " + e.getMessage());
}
}
return cityList;
}
/**
* 解析CSV行处理引号和逗号
*
* @param line CSV行文本
* @return 解析后的字段数组
*/
private static String[] parseLine(String line) {
List<String> fields = new ArrayList<>();
StringBuilder field = new StringBuilder();
boolean inQuotes = false;
int i = 0;
while (i < line.length()) {
char c = line.charAt(i);
if (c == '"') {
if (inQuotes) {
// 检查是否是转义的双引号 ""
if (i + 1 < line.length() && line.charAt(i + 1) == '"') {
field.append('"');
i += 2;
continue;
} else {
// 结束引号
inQuotes = false;
}
} else {
// 开始引号
inQuotes = true;
}
} else if (c == ',' && !inQuotes) {
// 字段分隔符
fields.add(field.toString().trim());
field.setLength(0);
} else {
field.append(c);
}
i++;
}
// 添加最后一个字段
fields.add(field.toString().trim());
return fields.toArray(new String[0]);
}
/**
* 建立列名到索引的映射
*
* @param headers 标题数组
* @return 列名到索引的映射
*/
private static Map<String, Integer> buildColumnIndexMap(String[] headers) {
Map<String, Integer> columnIndexMap = new HashMap<>();
for (int i = 0; i < headers.length; i++) {
String header = headers[i].trim();
// 移除可能的引号
if (header.startsWith("\"") && header.endsWith("\"")) {
header = header.substring(1, header.length() - 1);
}
columnIndexMap.put(header, i);
}
return columnIndexMap;
}
/**
* 根据解析的数据创建CityInfo对象
*
* @param values 字段值数组
* @param columnIndexMap 列名到索引的映射
* @return CityInfo对象
*/
private static CityInfo createCityInfo(String[] values, Map<String, Integer> columnIndexMap) {
try {
CityInfo cityInfo = new CityInfo();
// 根据列名映射设置字段值
setFieldIfExists(cityInfo, "setLocation_ID", values, columnIndexMap, "Location_ID");
setFieldIfExists(cityInfo, "setLocation_Name_EN", values, columnIndexMap, "Location_Name_EN");
setFieldIfExists(cityInfo, "setLocation_Name_ZH", values, columnIndexMap, "Location_Name_ZH");
setFieldIfExists(cityInfo, "setISO_3166_1", values, columnIndexMap, "ISO_3166_1");
setFieldIfExists(cityInfo, "setCountry_Region_EN", values, columnIndexMap, "Country_Region_EN");
setFieldIfExists(cityInfo, "setCountry_Region_ZH", values, columnIndexMap, "Country_Region_ZH");
setFieldIfExists(cityInfo, "setAdm1_Name_EN", values, columnIndexMap, "Adm1_Name_EN");
setFieldIfExists(cityInfo, "setAdm1_Name_ZH", values, columnIndexMap, "Adm1_Name_ZH");
setFieldIfExists(cityInfo, "setAdm2_Name_EN", values, columnIndexMap, "Adm2_Name_EN");
setFieldIfExists(cityInfo, "setAdm2_Name_ZH", values, columnIndexMap, "Adm2_Name_ZH");
setFieldIfExists(cityInfo, "setTimezone", values, columnIndexMap, "Timezone");
setFieldIfExists(cityInfo, "setLatitude", values, columnIndexMap, "Latitude");
setFieldIfExists(cityInfo, "setLongitude", values, columnIndexMap, "Longitude");
setFieldIfExists(cityInfo, "setAD_code", values, columnIndexMap, "AD_code");
return cityInfo;
} catch (Exception e) {
Logger.w(TAG, "Error creating CityInfo: " + e.getMessage());
return null;
}
}
/**
* 如果字段存在则设置值
*
* @param cityInfo CityInfo对象
* @param methodName setter方法名
* @param values 字段值数组
* @param columnIndexMap 列名到索引的映射
* @param columnName 列名
*/
private static void setFieldIfExists(CityInfo cityInfo, String methodName,
String[] values, Map<String, Integer> columnIndexMap,
String columnName) {
try {
Integer index = columnIndexMap.get(columnName);
if (index != null && index < values.length) {
String value = values[index];
// 移除可能的引号
if (value != null && value.startsWith("\"") && value.endsWith("\"")) {
value = value.substring(1, value.length() - 1);
}
// 使用反射调用setter方法
java.lang.reflect.Method method = cityInfo.getClass().getMethod(methodName, String.class);
method.invoke(cityInfo, value);
}
} catch (Exception e) {
Logger.w(TAG, "Error setting field " + columnName + ": " + e.getMessage());
}
}
/**
* 从输入流解析为CityInfo对象列表
*
* @param inputStream CSV文件输入流
* @return CityInfo对象列表
*/
public static List<CityInfo> parseFromStream(InputStream inputStream) {
if (inputStream == null) {
return new ArrayList<>();
}
BufferedReader reader = null;
List<CityInfo> cityList = new ArrayList<>();
try {
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
// 读取标题行
String headerLine = reader.readLine();
if (headerLine == null) {
return cityList;
}
// 解析标题行
String[] headers = parseLine(headerLine);
Map<String, Integer> columnIndexMap = buildColumnIndexMap(headers);
// 逐行读取数据
String line;
while ((line = reader.readLine()) != null) {
if (line.trim().isEmpty()) {
continue;
}
try {
String[] values = parseLine(line);
CityInfo cityInfo = createCityInfo(values, columnIndexMap);
if (cityInfo != null) {
cityList.add(cityInfo);
}
} catch (Exception e) {
Logger.w(TAG, "Error parsing line: " + e.getMessage());
}
}
} catch (Exception e) {
Logger.e(TAG, "Error during CSV parsing: " + e.getMessage());
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
Logger.w(TAG, "Failed to close stream: " + e.getMessage());
}
}
return cityList;
}
}