[英]How to go about parsing a large JSON file for search bar suggestions
我正在嘗試為天氣應用程序解析一個29mb的JSON,其中包含世界上的城市。
這是JSON結構:
[
{
"id": 707860,
"name": "Hurzuf",
"country": "UA",
"coord": {
"lon": 34.283333,
"lat": 44.549999
}
},
{
"id": 519188,
"name": "Novinki",
"country": "RU",
"coord": {
"lon": 37.666668,
"lat": 55.683334
}
},
{
"id": 1283378,
"name": "Gorkhā",
"country": "NP",
"coord": {
"lon": 84.633331,
"lat": 28
}
},
...
]
我有一個Fragment,其中包含保存的城市列表,以及一個fab,用於提示帶有文本字段的警報對話框,用戶應在其中輸入城市名稱並從解析的數據中獲取建議。
我的問題是,解析該文件會占用大量內存,並且減慢了用戶XP的速度(即使該文件在其他線程上運行,該Fab也會被禁用,直到操作完成為止)。 每次用戶訪問城市片段都分析文件似乎很愚蠢,因此將結果對象保留在內存中,所以我想我的問題是我應該如何處理?
使用GSON像這樣的線程或該文章建議是偉大的,但它並沒有解決問題的重復性。
public static List<City> getCitiesFromJSON(Context context){
List<City> cityList = new LinkedList<>();
try{
InputStream is = context.getAssets().open("jsons/city.list.min.json");
JsonReader reader = new JsonReader(new InputStreamReader(is, "UTF-8"));
reader.beginArray();
Gson gson = new GsonBuilder().create();
while (reader.hasNext()){
City cityJson = gson.fromJson(reader, City.class);
City city = new City();
city.setId(cityJson.getId());
city.setName(cityJson.getName());
city.setCountry(cityJson.getCountry());
city.setCoord(new Coord(cityJson.getCoord().getLon(),cityJson.getCoord().getLat()));
cityList.add(city);
}
reader.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return cityList;
}
tl; dr
每次用戶嘗試將其添加到列表時,都需要解析大JSON以獲取搜索建議,但是該操作既耗費內存又耗時,因此我需要一種更好的方法。
我認為在您的情況下,將城市加載到內存(Singleton Object)中比較好,或者如果目標客戶端資源/內存將受到影響,那么您將需要一台遠程服務器來執行搜索引擎(例如ElasticSearch)或其余的應用程序將進行處理和緩存
另外,緩存搜索請求將有助於防止重復處理。
方法1 :更好的方法是在應用程序啟動后立即在后台請求JSON並將其保存到本地存儲(SQlite,Room Persistence或只是sharedpreference)。
方法2:第二個技巧是僅顯示足夠大的數字,而不顯示大數字。 假設用戶有1000個匹配用戶輸入字符串的結果。 只需查詢本地或遠程數據庫以顯示最匹配的100。沒有理智的人可以滾動瀏覽100個結果,而無需再次修改/寫入其輸入文本。
最佳方法:使用分頁庫在用戶向下滾動時加載數據。
這是我解決此問題的方法:
城市實體:
@Entity
public class City {
@PrimaryKey
private long id;
private String name;
private String country;
@Embedded
private Coord coord;
public City(){
}
public City(JSONObject json){
this.id = json.optLong("id");
this.name = json.optString("name");
this.country = json.optString("country");
this.coord = new Coord(json.optJSONObject("coord"));
}
public City(long id, String name, String country, Coord coord) {
this.id = id;
this.name = name;
this.country = country;
this.coord = coord;
}
// getters and setters ...
}
市DAO:
@Dao
public interface CityDao {
@Query("SELECT * FROM city")
List<City> getAll();
// Used to query search suggestions
@Query("SELECT * FROM city WHERE name LIKE :name || '%' ORDER BY name ASC LIMIT 10")
City findByName(String name);
// Used to determine whether or not the db was created
@Query("SELECT COUNT(*) FROM city")
int getCitySize();
@Insert
void insertAll(City... cities);
@Insert
void insertCity(City city);
@Delete
void deleteCity(City city);
@Query("DELETE FROM city")
public void nukeCities();
}
AppDatabase:
@Database(entities = {City.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract CityDao cityDao();
}
應用:
public class App extends Application implements RunnableCompleteListener {
private final CountDownLatch startLatch = new CountDownLatch(1);
private AppDatabase appDB;
@Override
public void onCreate() {
super.onCreate();
appDB = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "cities-db").build();
NotifyingRunnable readCitiesJSON = new NotifyingRunnable() {
List<City> citiesList = new ArrayList<>();
@Override
public void doRun() {
int dbCitySize = appDB.cityDao().getCitySize();
// if db is empty
if (dbCitySize == 0){
citiesList = HelperFunctions.getCitiesFromJSON(getApplicationContext());
appDB.cityDao().insertAll(citiesList.toArray(new City[citiesList.size()]));
}
}
};
readCitiesJSON.addListener(this);
Thread worker = new Thread(readCitiesJSON);
worker.start();
}
public AppDatabase getDatabase() {
return appDB;
}
public CountDownLatch getStartLatch() {
return startLatch;
}
@Override
public void notifyOfRunnableComplete(Runnable runnable) {
startLatch.countDown();
}
}
可運行的實現:
public interface RunnableCompleteListener {
void notifyOfRunnableComplete(final Runnable runnable);
}
public abstract class NotifyingRunnable implements Runnable {
private final Set<RunnableCompleteListener> listeners = new CopyOnWriteArraySet<RunnableCompleteListener>();
public final void addListener(final RunnableCompleteListener listener){
listeners.add(listener);
}
public final void removeListener(final RunnableCompleteListener listener){
listeners.remove(listener);
}
private final void notifyListeners(){
for(RunnableCompleteListener listener : listeners){
listener.notifyOfRunnableComplete(this);
}
}
@Override
public final void run() {
try{
doRun();
}finally {
notifyListeners();
}
}
public abstract void doRun();
}
public static List<City> getCitiesFromJSON(Context context){
String json = "";
try {
InputStream is = context.getAssets().open("jsons/city.list.min.json");
int size = is.available();
// read the entire asset into a local buffer
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
List<City> citiesList = new ArrayList<City>();
Type listType = new TypeToken<ArrayList<City>>() {}.getType();
// convert json into a list of cities
try {
citiesList = new Gson().fromJson(json, listType);
}catch (Exception e){
Log.e("Error parsing", e.toString());
}
return citiesList;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
fabAddCity = view.findViewById(R.id.fabAddCity);
fabAddCity.setOnClickListener(this);
// disabled by default
fabAddCity.setEnabled(false);
fabAddCity.getBackground().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
// the thread that controls the fab state
Thread fabController = new Thread(() -> {
try {
((App)getActivity().getApplication()).getStartLatch().await();
getActivity().runOnUiThread(() -> {
fabAddCity.setEnabled(true);
fabAddCity.getBackground().setColorFilter(null);
});
} catch (InterruptedException e) {
e.printStackTrace();
}
});
fabController.start();
...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.