将 REST 与 BaseAdapter 结合使用的 AndroidAnnotations 抛出“无法在未调用 Looper.prepare() 的线程内创建处理程序”

AndroidAnnotations using REST with BaseAdapter throw 'Can't create handler inside thread that has not called Looper.prepare()'

更新:HomeActivity.java 代码已按照 WonderCsabo

的建议更新

我是 Android 开发的新手。我的场景是从服务器加载 REST API(来自@Rest)并加载到 BaseAdapter。

当我运行它时,没有加载任何数据,它只是抛出异常消息 Can't create handler inside thread that has not called Looper.prepare()(请参阅我使用 Log.e 来跟踪错误异常消息)。下面是我的代码:

Product.java

public class Product {
    private int id;
    private String name;
    private String imageUrl;
    private int availableStock;
    private double purchasingPrice;
    private double sellingPrice;

    public Product() {}

    ... hide getter & setter for brevity
}

ProductAPI.java

@Rest(rootUrl = "http://192.168.42.154:1337/api/v1", converters = {GsonHttpMessageConverter.class, MappingJacksonHttpMessageConverter.class })
public interface ProductAPI extends RestClientErrorHandling {

    @Get("/products")
    List<Product> getProducts();

}

ProductListAdapter .java

public class ProductListAdapter extends BaseAdapter {

    List<Product> products;


    public ProductListAdapter(List<Product> products) {
        this.products = products;
    }

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

        if (convertView == null) {
            convertView = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.grid_product, parent, false);
        }

        String imageUrl = "http://lorempixel.com/800/600/sports/" + String.valueOf(position + 1);
        convertView.setTag(imageUrl);

        ImageView image = (ImageView) convertView.findViewById(R.id.image);
        Picasso.with(convertView.getContext())
                .load(imageUrl)
                .into(image);


        TextView name = (TextView) convertView.findViewById(R.id.name);
        name.setText(getItem(position).toString());

        return convertView;
    }

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

    @Override
    public Object getItem(int position) {
        return products.get(position);
    }

    @Override
    public long getItemId(int position) {
        return products.indexOf(getItem(position));
    }
}

HomeActivity.java

@EActivity(R.layout.activity_home)
@OptionsMenu(R.menu.main)
public class HomeActivity extends BaseActivity {

    List<Product> products;
    ProductListAdapter adapter;

    @ViewById
    DrawerLayout drawer;

    @ViewById
    GridView gridView;

    @Bean
    CustomRestErrorHandler restErrorHandler;

    @RestService
    ProductAPI productAPI;

    @Background
    void searchProducts() {
        try {
            products = productAPI.getProducts();
            initAdapter();
            Log.d("test", String.valueOf(products.size()));
        } catch (Exception e) {
            Log.d("test", e.getMessage());
        }
    }

    @UiThread
    void initAdapter() {
        adapter = new ProductListAdapter(products);
    }

    @AfterViews
    void initDrawer() {
        setActionBarIcon(R.drawable.ic_ab_drawer);
        drawer.setDrawerShadow(R.drawable.drawer_shadow, Gravity.START);
        productAPI.setRestErrorHandler(restErrorHandler);
    }

    @AfterViews
    void bindAdapter() {
        searchProducts();

        gridView.setAdapter(adapter);
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                String url = (String) view.getTag();
                Map<String,String> intents = new HashMap<>();

                intents.put("productName", adapter.getItem(i).toString());
                intents.put("DetailActivity:image", url);

                DetailActivity_.launch(HomeActivity.this, view.findViewById(R.id.image), intents);
            }
        });
    }
}

以下是来自 http://192.168.42.154:1337/api/v1/products 的回复:

[
  {
    "name": "Product 1",
    "imageUrl": "0d47e87c-ec21-4aa5-a051-cdd051d4f1e9.png",
    "availableStock": 23,
    "purchasingPrice": 135000,
    "sellingPrice": 150000,
    "id": 1,
    "createdAt": "2015-01-05T13:00:14.212Z",
    "updatedAt": "2015-01-05T13:00:14.212Z"
  },
  {
    "name": "Product 2",
    "imageUrl": "02cc5c02-2654-4d6c-ab96-e4179b2bb967.jpg",
    "availableStock": 7,
    "purchasingPrice": 45000,
    "sellingPrice": 60000,
    "id": 2,
    "createdAt": "2015-01-05T13:00:40.451Z",
    "updatedAt": "2015-01-05T13:00:40.451Z"
  }
]

如果大家帮助我,我很高兴:)

更新:完整的堆栈跟踪

01-07 08:36:09.818  14122-14124/com.pewh.materialeverywhere D/dalvikvm﹕ GC_CONCURRENT freed 355K, 11% free 9756K/10951K, paused 20ms+13ms, total 69ms
01-07 08:36:20.749  14504-14508/com.pewh.materialeverywhere D/dalvikvm﹕ GC_CONCURRENT freed 281K, 11% free 9505K/10567K, paused 19ms+18ms, total 99ms
01-07 08:36:20.749  14504-14504/com.pewh.materialeverywhere D/dalvikvm﹕ WAIT_FOR_CONCURRENT_GC blocked 4ms
01-07 08:36:20.809  14504-14504/com.pewh.materialeverywhere I/dalvikvm﹕ Could not find method android.view.ViewGroup.onRtlPropertiesChanged, referenced from method android.support.v7.widget.Toolbar.onRtlPropertiesChanged
01-07 08:36:20.809  14504-14504/com.pewh.materialeverywhere W/dalvikvm﹕ VFY: unable to resolve virtual method 11502: Landroid/view/ViewGroup;.onRtlPropertiesChanged (I)V
01-07 08:36:20.809  14504-14504/com.pewh.materialeverywhere D/dalvikvm﹕ VFY: replacing opcode 0x6f at 0x0007
01-07 08:36:20.819  14504-14504/com.pewh.materialeverywhere I/dalvikvm﹕ Could not find method android.content.res.TypedArray.getChangingConfigurations, referenced from method android.support.v7.internal.widget.TintTypedArray.getChangingConfigurations
01-07 08:36:20.819  14504-14504/com.pewh.materialeverywhere W/dalvikvm﹕ VFY: unable to resolve virtual method 420: Landroid/content/res/TypedArray;.getChangingConfigurations ()I
01-07 08:36:20.819  14504-14504/com.pewh.materialeverywhere D/dalvikvm﹕ VFY: replacing opcode 0x6e at 0x0002
01-07 08:36:20.819  14504-14504/com.pewh.materialeverywhere I/dalvikvm﹕ Could not find method android.content.res.TypedArray.getType, referenced from method android.support.v7.internal.widget.TintTypedArray.getType
01-07 08:36:20.819  14504-14504/com.pewh.materialeverywhere W/dalvikvm﹕ VFY: unable to resolve virtual method 442: Landroid/content/res/TypedArray;.getType (I)I
01-07 08:36:20.819  14504-14504/com.pewh.materialeverywhere D/dalvikvm﹕ VFY: replacing opcode 0x6e at 0x0002
01-07 08:36:20.859  14504-14504/com.pewh.materialeverywhere D/AbsListView﹕ Get MotionRecognitionManager
01-07 08:36:20.869  14504-14504/com.pewh.materialeverywhere D/AbsListView﹕ Get MotionRecognitionManager
01-07 08:36:21.049  14504-14508/com.pewh.materialeverywhere D/dalvikvm﹕ GC_CONCURRENT freed 355K, 11% free 9687K/10823K, paused 18ms+3ms, total 92ms
01-07 08:36:21.049  14504-14522/com.pewh.materialeverywhere D/dalvikvm﹕ WAIT_FOR_CONCURRENT_GC blocked 24ms
01-07 08:36:21.059  14504-14515/com.pewh.materialeverywhere D/dalvikvm﹕ WAIT_FOR_CONCURRENT_GC blocked 26ms
01-07 08:36:21.059  14504-14504/com.pewh.materialeverywhere D/dalvikvm﹕ WAIT_FOR_CONCURRENT_GC blocked 26ms
01-07 08:36:21.059  14504-14504/com.pewh.materialeverywhere D/libEGL﹕ loaded /system/lib/egl/libEGL_mali.so
01-07 08:36:21.109  14504-14504/com.pewh.materialeverywhere D/libEGL﹕ loaded /system/lib/egl/libGLESv1_CM_mali.so
01-07 08:36:21.109  14504-14504/com.pewh.materialeverywhere D/libEGL﹕ loaded /system/lib/egl/libGLESv2_mali.so
01-07 08:36:21.149  14504-14504/com.pewh.materialeverywhere D/OpenGLRenderer﹕ Enabling debug mode 0
01-07 08:36:24.232  14504-14522/com.pewh.materialeverywhere E/com.pewh.materialeverywhere﹕ Can't create handler inside thread that has not called Looper.prepare()

罪魁祸首在这里:

searchProducts();
adapter = new ProductListAdapter(products);

searchProducts() 是一个 @Background 注释方法,这意味着它将在后台线程上完成它的工作。而 bindAdapter() 将继续在主线程上执行。这意味着当你调用构造函数时products仍然是null,所以它会传递一个null,这就是为什么NullPointerException被抛出在getCount()中的原因。 searchProducts() 完成后,您必须更新您的适配器,例如:

@Background
void searchProducts() {
    try {
        products = productAPI.getProducts();
        initAdapter();
        Log.d("test", "the size is " + products.size());
    } catch (Exception e) {
        Log.d("test", e.getMessage());
    }
}

@UiThread // will be called on the main thread
void initAdapter() {
    adapter = new ProductListAdapter(products);
    ...
}

编辑:

首先,您应该将所有 adapter 初始化代码移动到 initAdapter()。此时:

@AfterViews
void bindAdapter() {
    searchProducts();

    gridView.setAdapter(adapter);
    ...

adapter 仍然是 null!

其次:无法在未调用 Looper.prepare()[=42= 的线程内创建处理程序],主要是 运行 [=44= 的症状] 后台线程上的相关操作。请确保您没有在 @Background 注释方法中做任何与 UI 相关的事情。