后台方法中的Android NetworkOnMainThreadException问题

4

我的应用程序中有一些网络请求,每当调用与网络相关的代码时,我会遇到这个异常。 我已经将它们放在后台:

    @Background
    protected void getBitmapFromURL(String src, ImageView img) {
        try {
            URL url = new URL(src);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            connection.connect();
            InputStream input = connection.getInputStream();
            updateImageView(BitmapFactory.decodeStream(input), img);
            return;
        } catch (IOException e) {
            System.out.println("************** network exception: download image failed");
            return;
        }
    }

    @UiThread
    protected void updateImageView(Bitmap bitmap, ImageView img) {
        img.setImageBitmap(bitmap);
        return;
    }

但是错误仍然存在。我在这里实现了自己的适配器,它继承了BaseAdapter。我使用@EBean进行注释,并在我的activity文件中使用@Bean进行声明。构造函数只接受Context参数,我将其他参数的初始化放在一个单独的方法中。
所以我认为这个设置符合androidannotation的要求,如果网络操作在后台完成,为什么会出现这个错误呢?
我正在扩展的类:
@EBean
public class MomentViewAdapter extends BaseAdapter {
    protected LayoutInflater mInflater;
    protected Context mContext;
    protected List<FavoriteInfo> mDatas;
    protected  int mItemLayoutId;

    public MomentViewAdapter(Context context) {
        this.mContext = context;
//        this.mInflater = LayoutInflater.from(mContext);
//        this.mDatas = mDatas;
//        this.mItemLayoutId = itemLayoutId;
    }

    public void setUp(List<FavoriteInfo> mDatas, int itemLayoutId) {
        this.mInflater = LayoutInflater.from(mContext);
        this.mDatas = mDatas;
        this.mItemLayoutId = itemLayoutId;
    }


    @Override
    public int getCount()
    {
        return mDatas.size();
    }

    @Override
    public FavoriteInfo getItem(int position)
    {
        return mDatas.get(position);
    }

    @Override
    public long getItemId(int position)
    {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {

        if(convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(mItemLayoutId, parent, false);
        }
        ImageView img = (ImageView) convertView.findViewById(R.id.detail_moment_camera_picture);
        if (mDatas.get(position).isVideoFavorite()) {
            Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(mDatas.get(position).getVideoURL(), MediaStore.Video.Thumbnails.MINI_KIND);
            img.setImageBitmap(bitmap);
        } else {
            getBitmapFromURL(mDatas.get(position).getImageURL(), img);
            // img.setImageBitmap(getBitmapFromURL(mDatas.get(position).getImageURL()));
        }
        img.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mContext, GeneratedClassUtils.get(MomentDetailActivity.class));
                Bundle mBundle = new Bundle();
                mBundle.putSerializable(FavoriteInfo.KEY, mDatas.get(position));
                mBundle.putBoolean(CollectionInfo.KEY_WATCH_FLAG, false);
                intent.putExtras(mBundle);
                mContext.startActivity(intent);
            }
        });

        return convertView;

    }

堆栈跟踪(错误在MomentViewAdapter中):

09-02 11:35:17.302  30956-30956/com.bloomsky.bloomsky E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.bloomsky.bloomsky, PID: 30956
    android.os.NetworkOnMainThreadException
            at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
            at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:249)
            at libcore.io.IoBridge.recvfrom(IoBridge.java:553)
            at java.net.PlainSocketImpl.read(PlainSocketImpl.java:485)
            at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:37)
            at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237)
            at com.android.okio.Okio$2.read(Okio.java:113)
            at com.android.okio.RealBufferedSource.indexOf(RealBufferedSource.java:147)
            at com.android.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:94)
            at com.android.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:175)
            at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:101)
            at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:616)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:379)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:323)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:190)
            at com.bloomsky.android.ui.adapter.MomentViewAdapter.getBitmapFromURL(MomentViewAdapter.java:132)
            at com.bloomsky.android.ui.adapter.MomentViewAdapter.getView(MomentViewAdapter.java:85)
            at android.widget.AbsListView.obtainView(AbsListView.java:2346)
            at android.widget.GridView.makeAndAddView(GridView.java:1433)
            at android.widget.GridView.makeRow(GridView.java:361)
            at android.widget.GridView.fillDown(GridView.java:302)
            at android.widget.GridView.fillFromTop(GridView.java:437)
            at android.widget.GridView.layoutChildren(GridView.java:1276)
            at android.widget.AbsListView.onLayout(AbsListView.java:2150)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
            at android.widget.ScrollView.onLayout(ScrollView.java:1502)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at in.srain.cube.views.ptr.PtrFrameLayout.layoutChildren(PtrFrameLayout.java:247)
            at in.srain.cube.views.ptr.PtrFrameLayout.onLayout(PtrFrameLayout.java:216)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1077)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
            at com.bloomsky.android.ui.SwipeBackLayout.onLayout(SwipeBackLayout.java:233)
            at android.view.View.layout(View.java:15604)
            at android.view.ViewGroup.layout(ViewGroup.java:4967)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
            at android.view.View.layout(View.java:15604

按要求提供相关活动代码:

        @Bean
        MomentViewAdapter mGridViewAdapter;mGridViewAdapter = new MomentViewAdapter(this);
        mGridViewAdapter.setUp(mMomentDatas, R.layout.device_detail_moment_listitem_simple);
        mGridView.setAdapter(mGridViewAdapter);

请发布您的堆栈跟踪,并显示您正在调用 getBitmapFromURL() 的位置。您还可以考虑使用似乎无限多的 Android 图像加载库 之一,因为它们已经解决了这个问题。 - CommonsWare
你是在异步类中运行这个吗? - Mark Gilchrist
发布您的Activity和清单文件(在其中声明Activity) - Gabriele Mariotti
@CommonsWare 没有这样的限制。AndroidAnnotations 会在这种情况下添加编译时错误。 :) - WonderCsabo
@GabrieleMariotti 我添加了相关的代码。 - J Freebird
显示剩余6条评论
2个回答

2

试着用这种方式实现

    ImageView imageView;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        imageView = (ImageView) findViewById(R.id.imageView);

        // Create an object for subclass of AsyncTask
        BitmapDownloaderTask task = new BitmapDownloaderTask();
         .
         .
         .        

    }

class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
.
.
.

@Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
     return getBitmapFromURL(params...);
}

@Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
    if (isCancelled()) {
        bitmap = null;
    }
        if (imageView != null) {
            imageView.setImageBitmap(bitmap);
        }
 }
}


protected void getBitmapFromURL(String src) {
        try {
            URL url = new URL(src);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            connection.connect();
            InputStream input = connection.getInputStream();

            //New Line added
            final Bitmap bitmap = BitmapFactory.decodeStream(input);

            //return bitmap
            return bitmap;
        } catch (IOException e) {
            System.out.println("************** network exception: download image failed");
            return;
        }
    }

但是,我建议您使用Volley而不是这种方式。


1
Volley的介绍看起来非常强大。我会学习文档并尝试一下。那么你认为androidannotations在这里不能正常工作? - J Freebird
imageViewReference来自哪里? - J Freebird
@JFreebird 我编辑了我的回答。那是我以前的一个项目的代码,所以我删除了一些代码。 - Jorge Casariego
@JFreebird 我以前没有使用过AndroidAnnotations。 - Jorge Casariego
@Jorge Casariego,@Background 的目的实际上是为了消除 AsyncTask 的样板代码。 - WonderCsabo
@JorgeCasariego,我无法访问imageView,除非将其作为参数传递,但是Asynctask方法只能接受相同类型的参数。 - J Freebird

2
这里的问题是你没有使用生成的类,而是使用了你注解的那个类,这个类当然不包含任何增强功能。你需要在想要使用它的类中创建或注入增强适配器,然后在该实例上调用 background 方法。
@EActivity
public class YourActivity {

  @Bean // will inject instance of generated MomentViewAdapter_
  MomentViewAdapter adapter;

  @AfterInject // injected beans are only available from here
  public void afterInject() {
     yourList.setAdapter(adapter);
  }
}

我得到了如下错误信息:Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.GridView.setAdapter(android.widget.ListAdapter)' on a null object reference。错误出现在 com.bloomsky.android.activities.common.DeviceDetailActivity.afterInject(DeviceDetailActivity.java:626)。 - J Freebird
所以它说'adapter'为空。但我确实使用了:@AfterInject //注入的bean只能从这里使用 public void afterInject() { mGridViewAdapter.setUp(mMomentDatas, R.layout.device_detail_moment_listitem_simple); mGridView.setAdapter(mGridViewAdapter); } 并用 @Bean 进行了注释。 - J Freebird
NPE问题:如果你注入了GridView,你必须使用@AfterViews而不是@AfterInject - WonderCsabo
抱歉,那是旧的代码。我没有新的MomentViewAdapter()。实际上,带有@AfterInject的方法是在另一个带有@AfterViews的方法内部调用的。 - J Freebird
但是该实例的setUp方法没有抛出任何错误,尽管它在setAdapter之前被调用,这让我感到困惑。 - J Freebird
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接