如何从JSON API获取URL在RecyclerView点击事件打开 空指针排查
实现目标
从JSON接口返回数据中提取URL链接,在RecyclerView的点击事件中打开对应链接。
现有代码
接口返回JSON结构
{ { "link": "https://example.com" }, { ... } }
URLModel类
public class URLModel { String Link; public MonlixModel(String link) { Link = link; } public String getLink() { return Link; } public void setLink(String link) { Link = link; } }
URLAdapter适配器
public class URLAdapter extends RecyclerView.Adapter<URLAdapter.URLHolder> { Context mContext; List<URLModel> urlModels; public URLAdapter(Context mContext, List<URLAdapter> urlModels) { this.mContext = mContext; this.urlModels= urlModels; } @NonNull @Override public URLHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_view, parent, false); return new URLHolder(view); } @Override public void onBindViewHolder(@NonNull URLHolder holder, int position) { holder.recyclerUrl.setOnClickListener(view -> { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(urlModels.get(position).getLink())); mContext.startActivity(intent); }); } @Override public int getItemCount() { return urlModels.size(); } public static class URLHolder extends RecyclerView.ViewHolder { private final RecyclerView recyclerUrl; public URLHolder(@NonNull View itemView) { super(itemView); recyclerUrl = itemView.findViewById(R.id.recycler_url); } } }
网络请求逻辑(Volley实现)
private void getData() { RequestQueue requestQueue = Volley.newRequestQueue(this); @SuppressLint("NotifyDataSetChanged") JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, Apis.JSON_API, null, response -> { for(int i = 0; i <=response.length(); i++){ try { JSONObject jsonObject = response.getJSONObject(i); URLModelList.add(new URLModel( jsonObject.getString("link") )); } catch (JSONException e) { e.printStackTrace(); } } URLAdapter adapter = new URLAdapter(getApplicationContext(), URLModelList); recyclerView.setAdapter(adapter); adapter.notifyDataSetChanged(); Toast.makeText(Main.this, "Success", Toast.LENGTH_SHORT).show(); }, error -> Toast.makeText(Main.this, error.getMessage(), Toast.LENGTH_SHORT).show()); requestQueue.add(jsonArrayRequest); }
现存问题
运行时抛出空指针异常,初步怀疑问题在Adapter层。使用Volley作为网络框架,曾尝试将URLModel的String字段改为Uri类型,但JSONObject无对应getUri方法,无法实现。
错误点定位与修复
代码中共有6处会导致崩溃或功能异常的问题,逐一修复即可:
- URLModel构造方法名错误:类名为
URLModel,构造方法写成了MonlixModel,创建对象时会找不到匹配构造方法。 - Adapter构造方法参数类型错误:第二个参数声明为
List<URLAdapter>,实际需要传入List<URLModel>,类型不匹配会导致后续读取数据时空指针。 - ViewHolder中控件类型声明错误:
R.id.recycler_url是item布局中的普通可点击控件,声明为RecyclerView类型会导致findViewById返回null,设置点击事件时直接触发空指针。 - JSON遍历数组越界:循环条件写为
i <=response.length(),JSON数组下标从0开始,正确条件应为i < response.length(),否则最后一次循环会取到不存在的下标,触发JSONException导致数据添加失败。 - 请求类型与返回结构不匹配:当前使用
JsonArrayRequest要求接口最外层返回JSON数组([]包裹),贴出的返回结构最外层为JSON对象({}包裹),会直接请求失败拿不到数据。如果接口实际返回最外层是对象,需要替换为JsonObjectRequest。 - Intent缺少必要Flag:使用Application上下文启动Activity时,必须添加
Intent.FLAG_ACTIVITY_NEW_TASK标记,否则会抛出运行时异常。
修复后代码
修复后URLModel
public class URLModel { String link; // 构造方法名与类名保持一致 public URLModel(String link) { this.link = link; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } }
修复后URLAdapter
public class URLAdapter extends RecyclerView.Adapter<URLAdapter.URLHolder> { Context mContext; List<URLModel> urlModels; // 修正参数类型为List<URLModel> public URLAdapter(Context mContext, List<URLModel> urlModels) { this.mContext = mContext; this.urlModels = urlModels; } @NonNull @Override public URLHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_view, parent, false); return new URLHolder(view); } @Override public void onBindViewHolder(@NonNull URLHolder holder, int position) { URLModel currentItem = urlModels.get(position); holder.itemView.setOnClickListener(view -> { String link = currentItem.getLink(); // 链接为空时直接拦截,避免Uri.parse解析失败 if (link == null || link.trim().isEmpty()) { Toast.makeText(mContext, "链接无效", Toast.LENGTH_SHORT).show(); return; } Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(link)); // 非Activity上下文启动Activity添加Flag intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); }); } @Override public int getItemCount() { // 增加空判断,避免列表未初始化时size调用空指针 return urlModels == null ? 0 : urlModels.size(); } public static class URLHolder extends RecyclerView.ViewHolder { // 若需要给指定id的控件加点击,替换为对应控件类型即可,比如TextView、Button private final View clickArea; public URLHolder(@NonNull View itemView) { super(itemView); clickArea = itemView.findViewById(R.id.recycler_url); } } }
修复后网络请求逻辑
private void getData() { RequestQueue requestQueue = Volley.newRequestQueue(this); // 注意:如果接口最外层返回的是JSONObject,把JsonArrayRequest换成JsonObjectRequest即可 JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, Apis.JSON_API, null, response -> { // 每次加载前清空旧数据,避免重复添加 URLModelList.clear(); // 修正循环条件,避免数组越界 for(int i = 0; i < response.length(); i++){ try { JSONObject jsonObject = response.getJSONObject(i); // 用optString取字段,字段不存在时返回默认空字符串,不会抛异常 String link = jsonObject.optString("link", ""); if (!link.isEmpty()) { URLModelList.add(new URLModel(link)); } } catch (JSONException e) { e.printStackTrace(); } } // 不要每次请求都新建Adapter,初始化绑定一次即可,后续只刷新数据 if (recyclerView.getAdapter() == null) { // 优先传Activity上下文,避免Context相关异常 URLAdapter adapter = new URLAdapter(Main.this, URLModelList); recyclerView.setAdapter(adapter); } else { recyclerView.getAdapter().notifyDataSetChanged(); } Toast.makeText(Main.this, "加载成功", Toast.LENGTH_SHORT).show(); }, error -> Toast.makeText(Main.this, "加载失败:" + error.getMessage(), Toast.LENGTH_SHORT).show()); requestQueue.add(jsonArrayRequest); }
补充说明
- Volley完全满足当前网络请求需求,不需要更换框架。URLModel中保留String类型存储链接即可,使用时调用
Uri.parse()转成Uri对象,不需要修改字段类型。 - 记得在
AndroidManifest.xml中添加网络权限:<uses-permission android:name="android.permission.INTERNET" />。Android 11及以上版本需要在manifest中添加queries配置,否则可能无法正常唤起浏览器跳转。
内容的提问来源于stack exchange,提问作者FloFlaFli
相关产品推荐
相关产品推荐

