You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android英雄联盟应用:AsyncTask中英雄Key转名称的异常解决

解决英雄Key转名称的两种方案

首先,你遇到的NetworkOnMainThreadException是因为Android不允许在主线程(也就是onPostExecute方法)执行网络请求,所以把英雄数据的获取逻辑移到doInBackground里是完全正确的方向,不过我们可以再优化这个方案,同时也给你提供另一种更高效的Data Dragon JSON映射方案。


方案一:优化AsyncTask中的API调用

你修改后的代码已经把英雄数据请求放到了doInBackground,但可以考虑缓存英雄数据,避免多次重复请求同一个英雄的信息(比如召唤师连续玩了同一个英雄):

class Wrapper {
    Summoner summoner;
    MatchList matchList;
    RiotApi api;
    Map<Integer, String> championKeyToNameMap; // 新增缓存映射
}

@SuppressLint("StaticFieldLeak")
public class FetchMatchListTask extends AsyncTask<String, Void, Wrapper> {
    @Override
    protected Wrapper doInBackground(String... params) {
        ApiConfig config = new ApiConfig().setKey("MY-API-KEY");
        Wrapper wrapper = new Wrapper();
        wrapper.api = new RiotApi(config);
        wrapper.championKeyToNameMap = new HashMap<>(); // 初始化缓存

        try {
            wrapper.summoner = wrapper.api.getSummonerByName(Platform.EUNE, params[0]);
            wrapper.matchList = wrapper.api.getMatchListByAccountId(Platform.EUNE, wrapper.summoner.getAccountId());

            if (wrapper.matchList.getMatches() != null) {
                int matchLimitCounter = 0;
                for (MatchReference match : wrapper.matchList.getMatches()) {
                    if(matchLimitCounter == 10) break;
                    matchLimitCounter++;

                    int championKey = match.getChampion();
                    String champName;

                    // 先查缓存,不存在再请求API
                    if (wrapper.championKeyToNameMap.containsKey(championKey)) {
                        champName = wrapper.championKeyToNameMap.get(championKey);
                    } else {
                        Champion champion = wrapper.api.getDataChampion(Platform.EUNE, championKey);
                        champName = champion.getName();
                        wrapper.championKeyToNameMap.put(championKey, champName); // 存入缓存
                    }

                    Match myMatch = new Match(wrapper.summoner.getProfileIconId(), champName, championKey, "lala");
                    matchList.add(myMatch);
                }
            }
            return wrapper;
        } catch (RiotApiException e) {
            Log.d("RiotMSG", "Blad: " + RiotApiException.getMessage(e.getErrorCode()));
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Wrapper wrapp) {
        super.onPostExecute(wrapp);
        if(wrapp != null) {
            numOfGamesText.setText(String.valueOf(wrapp.matchList.getTotalGames()));
            // 这里可以用matchList里的champName通过Picasso加载图片
            // 比如:Picasso.get().load("http://ddragon.leagueoflegends.com/cdn/9.22.1/img/champion/" + champName + ".png").into(imageView);
        } else {
            throw new NullPointerException("wrapp.matchList object is null");
        }
    }
}

这样做的好处是减少不必要的API调用,避免触发Riot API的速率限制,同时保证所有网络操作都在后台线程执行。


方案二:解析Data Dragon的champion.json实现本地映射

如果想完全避免调用英雄信息API,可以直接下载Data Dragon的champion.json文件,解析后建立英雄Key到名称的本地映射,这是更高效的方案:

步骤1:下载并解析champion.json

你可以在应用启动时(或者首次运行时)下载对应的JSON文件,然后解析成一个全局的Map<Integer, String>:

// 可以放在Application类或者单例类中初始化
public class ChampionNameMapper {
    private static Map<Integer, String> keyToNameMap;

    public static void init(Context context) {
        // 这里可以用OkHttp或者Volley下载JSON,注意要在后台线程执行
        new AsyncTask<Void, Void, Map<Integer, String>>() {
            @Override
            protected Map<Integer, String> doInBackground(Void... voids) {
                Map<Integer, String> map = new HashMap<>();
                try {
                    URL url = new URL("http://ddragon.leagueoflegends.com/cdn/9.22.1/data/en_US/champion.json");
                    BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        sb.append(line);
                    }
                    reader.close();

                    // 用Gson解析JSON
                    JsonObject jsonObject = new JsonParser().parse(sb.toString()).getAsJsonObject();
                    JsonObject data = jsonObject.getAsJsonObject("data");

                    for (String champKey : data.keySet()) {
                        JsonObject champData = data.getAsJsonObject(champKey);
                        int key = champData.get("key").getAsInt();
                        String name = champData.get("name").getAsString();
                        map.put(key, name);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return map;
            }

            @Override
            protected void onPostExecute(Map<Integer, String> map) {
                keyToNameMap = map;
            }
        }.execute();
    }

    public static String getChampionName(int key) {
        return keyToNameMap != null ? keyToNameMap.get(key) : null;
    }
}

步骤2:在AsyncTask中直接使用映射

这样在doInBackground里就不需要调用英雄API了,直接通过映射获取名称:

@Override
protected Wrapper doInBackground(String... params) {
    ApiConfig config = new ApiConfig().setKey("MY-API-KEY");
    Wrapper wrapper = new Wrapper();
    wrapper.api = new RiotApi(config);

    try {
        wrapper.summoner = wrapper.api.getSummonerByName(Platform.EUNE, params[0]);
        wrapper.matchList = wrapper.api.getMatchListByAccountId(Platform.EUNE, wrapper.summoner.getAccountId());

        if (wrapper.matchList.getMatches() != null) {
            int matchLimitCounter = 0;
            for (MatchReference match : wrapper.matchList.getMatches()) {
                if(matchLimitCounter == 10) break;
                matchLimitCounter++;

                int championKey = match.getChampion();
                String champName = ChampionNameMapper.getChampionName(championKey);

                Match myMatch = new Match(wrapper.summoner.getProfileIconId(), champName, championKey, "lala");
                matchList.add(myMatch);
            }
        }
        return wrapper;
    } catch (RiotApiException e) {
        Log.d("RiotMSG", "Blad: " + RiotApiException.getMessage(e.getErrorCode()));
        e.printStackTrace();
    }
    return null;
}

这种方案的优势是完全避免英雄信息API的调用,节省API配额,而且本地查询速度更快,适合需要频繁获取英雄名称的场景。


注意事项

  • 如果你使用方案二,记得要处理JSON版本的更新问题(比如LOL版本更新后,Data Dragon的CDN版本号也要对应修改),可以通过Riot的API获取最新版本号后再下载对应JSON。
  • 不管用哪种方案,加载图片时都要确保用Picasso/Glide等图片加载库,它们会自动在后台线程处理图片下载,避免主线程阻塞。

内容的提问来源于stack exchange,提问作者1haker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:45:33