将 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 相关的事情。
更新: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 相关的事情。