fix: 修复在android 6.0,Api 23闪退
This commit is contained in:
@@ -2,6 +2,7 @@ package com.ttstd.dialer.manager;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.jeremyliao.liveeventbus.LiveEventBus;
|
import com.jeremyliao.liveeventbus.LiveEventBus;
|
||||||
@@ -20,6 +21,8 @@ import com.ttstd.dialer.BuildConfig;
|
|||||||
import com.ttstd.dialer.bean.CityInfo;
|
import com.ttstd.dialer.bean.CityInfo;
|
||||||
import com.ttstd.dialer.config.CommonConfig;
|
import com.ttstd.dialer.config.CommonConfig;
|
||||||
import com.ttstd.dialer.gson.GsonUtils;
|
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.Logger;
|
||||||
import com.ttstd.dialer.utils.NativeUtils;
|
import com.ttstd.dialer.utils.NativeUtils;
|
||||||
|
|
||||||
@@ -136,7 +139,12 @@ public class WeatherManager {
|
|||||||
@Override
|
@Override
|
||||||
public void subscribe(@NonNull ObservableEmitter<List<CityInfo>> emitter) throws Throwable {
|
public void subscribe(@NonNull ObservableEmitter<List<CityInfo>> emitter) throws Throwable {
|
||||||
long time = System.currentTimeMillis();
|
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");
|
Logger.e(TAG, "subscribe: deserializeFromAssets time = " + (System.currentTimeMillis() - time) + "ms");
|
||||||
emitter.onNext(cityInfos);
|
emitter.onNext(cityInfos);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.ttstd.dialer.manager;
|
package com.ttstd.dialer.parser;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
305
app/src/main/java/com/ttstd/dialer/parser/LegacyCsvParser.java
Normal file
305
app/src/main/java/com/ttstd/dialer/parser/LegacyCsvParser.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user