Android中获取Facebook Bitmap时触发NetworkOnMainThreadException求助
问题原因分析
你遇到的NetworkOnMainThreadException是因为GraphRequest的回调方法是运行在主线程的:虽然request.executeAsync()是异步执行Graph请求,但请求完成后,你写的(object, response) -> {}回调会被调度到主线程执行。而你在这个回调里直接用HttpURLConnection做网络请求,违反了Android主线程不能执行耗时网络操作的规则,所以系统抛出了这个异常。
解决方案1:使用AsyncTask处理网络请求
按照你提到的AsyncTask思路,我们可以把头像的网络请求放到后台线程执行,完成后再回到主线程更新UI。具体修改步骤如下:
- 在你的
FrontPageClass中创建一个内部AsyncTask类:
private class LoadProfileImageTask extends AsyncTask<String, Void, Bitmap> { private ImageView targetImageView; public LoadProfileImageTask(ImageView imageView) { this.targetImageView = imageView; } @Override protected Bitmap doInBackground(String... params) { String userId = params[0]; try { URL url = new URL("https://graph.facebook.com/" + userId + "/picture?type=large"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); InputStream input = connection.getInputStream(); return BitmapFactory.decodeStream(input); } catch (IOException e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (bitmap != null && targetImageView != null) { targetImageView.setImageBitmap(bitmap); } } }
- 修改GraphRequest回调中的头像加载逻辑,替换掉原来的HttpURLConnection代码:
// 删除原来的网络请求代码,替换为AsyncTask调用 new LoadProfileImageTask(drawerProfilePicture).execute(id);
这样,网络请求会在doInBackground(后台线程)执行,加载完成后在onPostExecute(主线程)中更新ImageView,就不会触发异常了。
解决方案2:使用Glide(更推荐)
其实你代码里已经注释了Glide的用法,这是更优的方案——Glide会自动帮你处理线程调度、图片缓存、异常处理,比自己写AsyncTask更简洁可靠。只需要修正URL的小错误(注释里的/pictures是复数,应该改成/picture),然后解开注释即可:
// 替换所有头像加载代码,用这一行即可 String userProfilePicURL = "https://graph.facebook.com/" + id + "/picture?type=large"; Glide.with(FrontPageClass.this).load(userProfilePicURL).into(drawerProfilePicture);
Glide会自动在后台线程下载图片,完成后自动切换到主线程更新ImageView,完全不用手动处理线程问题,还能缓存图片,下次加载更快。
完整修改后的GraphRequest部分代码
这里是整合了Glide方案后的完整回调代码:
if (AccessToken.getCurrentAccessToken() != null && !AccessToken.getCurrentAccessToken().isExpired()) { GraphRequest request = GraphRequest.newMeRequest(AccessToken.getCurrentAccessToken(), (object, response) -> { try { userFullName = object.getString("name"); drawerUserName.setText(userFullName); String id = object.getString("id"); // 使用Glide加载头像,简洁又安全 String userProfilePicURL = "https://graph.facebook.com/" + id + "/picture?type=large"; Glide.with(FrontPageClass.this).load(userProfilePicURL).into(drawerProfilePicture); Log.i(TAG, "name from Success: " + userFullName); Log.i(TAG, "email from Sucess: " + userEmail); } catch (JSONException e) { e.printStackTrace(); } }); Bundle parameters = new Bundle(); // 如果需要获取邮箱,添加此行:parameters.putString("fields", "name,email"); request.setParameters(parameters); request.executeAsync(); }
注意:如果需要获取用户邮箱,记得在parameters里添加字段:parameters.putString("fields", "name,email");,否则object.getString("email")会抛出JSONException。
内容的提问来源于stack exchange,提问作者DJ. Aduvanchik

