Android 当我尝试在当前 activity 上使用适配器显示内容时,应用程序一直返回主 activity UI
Android app keeps going back to main activity UI when I try to display something using an adapter on the current activity
我是 android 开发的新手,正在构建一个可以从 BLE 设备读取特征的应用程序。我能够连接到设备并读取其特性。但是,我有一个问题是使用适配器显示特性。最初,我尝试使用 RecycleView 适配器,但是当我试图通知我的 recycleview 适配器时,我的程序一直返回到主 activity,Logcat 上剩下的所有内容都是这样的:
2020-11-24 13:35:37.220 12966-12966/? I/zygote64: Late-enabling -Xcheck:jni
2020-11-24 13:35:37.346 12966-12966/com.example.wbdmx I/LoadedApk: No resource references to update in package zui.platform
2020-11-24 13:35:37.368 12966-12966/com.example.wbdmx W/Typeface: setThemeFont(): FontPath Not Changed!
2020-11-24 13:35:37.683 12966-12993/com.example.wbdmx D/OpenGLRenderer: HWUI GL Pipeline
2020-11-24 13:35:37.757 12966-12993/com.example.wbdmx I/Adreno: QUALCOMM build : e89802c, I1ef8061d77
Build Date : 03/28/18
OpenGL ES Shader Compiler Version: EV031.22.00.01
Local Branch :
Remote Branch : quic/gfx-adreno.lnx.1.0.r36-rel
Remote Branch : NONE
Reconstruct Branch : NOTHING
2020-11-24 13:35:37.766 12966-12993/com.example.wbdmx I/Adreno: PFP: 0x005ff087, ME: 0x005ff063
2020-11-24 13:35:37.774 12966-12993/com.example.wbdmx I/zygote64: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0
2020-11-24 13:35:37.776 12966-12993/com.example.wbdmx I/OpenGLRenderer: Initialized EGL, version 1.4
2020-11-24 13:35:37.776 12966-12993/com.example.wbdmx D/OpenGLRenderer: Swap behavior 2
2020-11-24 13:35:37.871 12966-12966/com.example.wbdmx D/QuickpanelHelper: mUpdateSettings connect cost:3
2020-11-24 13:35:37.887 12966-12966/com.example.wbdmx D/QuickpanelHelper: mUpdateSettings connect cost:1
我认为 recycleview 是问题所在,现在我正在使用自定义列表视图适配器,但在连接设备并且应用程序返回 MainActivity 视图后,我的 Logcat 中也会出现同样的问题。这是我的文件:
MainActivity.java:
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothLeScanner bleScanner=null;
private ScanSettings scanSettings;
private ScanCallback scanCallback;
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_PERMISSIONS = 2;
private boolean isScanning = false;
private ArrayList<ScanResult> scanResult = new ArrayList<>();
private scanResultAdapter scanAdapter;
private RecyclerView recyclerView;
private Button scan_btn;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this, "bluetooth_not_supported", Toast.LENGTH_SHORT).show();
finish();
}
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(linearLayoutManager);
scanAdapter = new scanResultAdapter(scanResult, new CustomItemClickListner() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onItemClick(View v, int position) {
if(isScanning){
stopScan();
}
final BluetoothDevice device = scanResult.get(position).getDevice();
Log.w("ScanResultAdapter", "Connecting to device "+device.getAddress());
Operations(v, device);
}
});
recyclerView.setAdapter(scanAdapter);
//button so that we can change its text when clicked
scan_btn = (Button) findViewById(R.id.search_button);
}
public void Operations(View view, BluetoothDevice device){
Intent intent = new Intent(this, BleOperationsActivity.class);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
startActivity(intent);
}
@Override
protected void onResume() {
super.onResume();
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
return;
}
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_PERMISSIONS);
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void startScan(){
//response to the scan button
bleScanner = mBluetoothAdapter.getBluetoothLeScanner();
scanSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
scanResult.clear();
scanAdapter.notifyDataSetChanged();
scanCallback = new ScanCallback(){
@Override
public void onScanResult(int callbackType, ScanResult result){
ListIterator<ScanResult> item_iterator = scanResult.listIterator();
boolean flag = true;
while(item_iterator.hasNext()){
ScanResult item = item_iterator.next();
if(item.getDevice().getAddress().equals(result.getDevice().getAddress())){
flag = false;
}
}
if(flag) {
Log.i("ScanCallback", "Found unique BLE device! Name : " + result.getDevice().getName() + " address: " +result.getDevice().getAddress());
scanResult.add(result);
scanAdapter.notifyItemInserted(scanResult.size()-1); }
}
@Override
public void onScanFailed(int errorCode){
Log.e("ScanCallback", "onScanFailed: code " + errorCode);
}
};
scanResult.clear();
// ScanFilter filter = new ScanFilter.Builder().setDeviceAddress("D7:42:44:9F:56:C9").build();
// List<ScanFilter> filters = null;
// filters.add(filter);
bleScanner.startScan(null, scanSettings, scanCallback);
isScanning = true;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void stopScan(){
bleScanner.stopScan(scanCallback);
isScanning = false;
scan_btn.setText("SEARCH FOR DEVICES");
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void button_function(View v) {
if(isScanning){
stopScan();
}
else{
startScan();
scan_btn.setText("STOP SEARCH");
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onPause() {
super.onPause();
if (bleScanner != null) {
stopScan();
bleScanner = null;
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onStop() {
super.onStop();
if (bleScanner != null) {
stopScan();
bleScanner = null;
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDestroy() {
super.onDestroy();
if (bleScanner != null) {
stopScan();
bleScanner = null;
}
}
}
scanResultAdapter.java:
package com.example.wbdmx;
import android.bluetooth.le.ScanResult;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class scanResultAdapter extends RecyclerView.Adapter<scanResultAdapter.ViewHolder> {
private List<ScanResult> scanResult;
private CustomItemClickListner listner;
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView deviceName;
TextView deviceAddress;
TextView deviceRssi;
public ViewHolder(View view) {
super(view);
// Define click listener for the ViewHolder's View
deviceName = view.findViewById(R.id.device_name);
deviceAddress = view.findViewById(R.id.device_address);
deviceRssi = view.findViewById(R.id.device_rssi);
}
}
public scanResultAdapter(List<ScanResult> dataSet, CustomItemClickListner listner) {
scanResult = dataSet;
this.listner = listner;
}
// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view, which defines the UI of the list
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.listitem_device, viewGroup, false);
final ViewHolder mViewHolder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listner.onItemClick(v, mViewHolder.getAdapterPosition());
}
});
return mViewHolder;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the
// contents of the view with that element
viewHolder.deviceName.setText(scanResult.get(position).getDevice().getName() == null ? "Unknown": scanResult.get(position).getDevice().getName());
viewHolder.deviceRssi.setText(String.valueOf(scanResult.get(position).getRssi()));
viewHolder.deviceAddress.setText(scanResult.get(position).getDevice().getAddress());
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
if (scanResult == null) return 0;
return scanResult.size();
}
}
BleOperations.java:
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.OrientationHelper;
import androidx.recyclerview.widget.RecyclerView;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanResult;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class BleOperationsActivity extends AppCompatActivity {
private BluetoothDevice device;
private ArrayList<BluetoothGattCharacteristic> characteristics = new ArrayList<>();
private List<BluetoothGattService> services;
private static final int GATT_MAX_MTU_SIZE = 517;
ListView listView;
private characteristicAdapter charAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device == null) Log.e("BleOperationsIntent", "Missing BluetoothDevice from MainActivity");
device.connectGatt(this, false, gattCallback);
setContentView(R.layout.activity_ble_operations);
listView = (ListView)findViewById(R.id.list);
}
// public void setupRecyclerView(){
// recyclerView = (RecyclerView) findViewById(R.id.characteristics_recycler_view);
// LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
// recyclerView.setLayoutManager(linearLayoutManager);
// charAdapter = new characteristicAdapter(characteristics, new CustomItemClickListner() {
// @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
// @Override
// public void onItemClick(View v, int position) {
// final BluetoothGattCharacteristic character= characteristics.get(position);
// }
// }, this);
// recyclerView.setAdapter(charAdapter);
// }
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if(status == gatt.GATT_SUCCESS){
if (newState == BluetoothProfile.STATE_CONNECTED){
Log.w("BluetoothGattCallback", "Successfully connected to device " + gatt.getDevice().getAddress());
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
boolean ans = gatt.discoverServices();
Log.d("onConnectionStateChange", "Discover Services started: " + ans);
gatt.requestMtu(GATT_MAX_MTU_SIZE);
}
});
//gatt.discoverServices();
}
else if(newState == BluetoothProfile.STATE_DISCONNECTED){
Log.w("BluetoothGattCallback", "Succesfully disconnected form device "+ gatt.getDevice().getAddress());
gatt.close();
}
else{
Log.w("BluetoothGattCallback", "Error "+status+" encountered for "+gatt.getDevice().getAddress()+ "\nDisconnecting...");
gatt.close();
}
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status){
super.onServicesDiscovered(gatt, status);
services = gatt.getServices();
Log.w("BluetoothGattCallback", "Discovered "+ services.size()+" for "+gatt.getDevice().getAddress());
characteristics.addAll(services.stream().flatMap(s -> s.getCharacteristics().stream())
.collect(Collectors.toList()));
Log.w("onServicesDiscovered", "size = "+characteristics.size());
printGattTable(services);
runOnUiThread(new Runnable() {
@Override
public void run() {
// charAdapter.notifyDataSetChanged();
// Log.w("onServicesDiscovered", "adapter item count = "+ charAdapter.getItemCount());
charAdapter = new characteristicAdapter(characteristics, getApplicationContext());
listView.setAdapter(charAdapter);
}
});
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status){
boolean event = status ==BluetoothGatt.GATT_SUCCESS;
Log.w("onMtuChanged", "ATT MTU changed to "+mtu+" "+ event);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){
if(status == BluetoothGatt.GATT_SUCCESS){
Log.i("BluetoothGattCallback", "Read characteristic success for "+ characteristic.getUuid().toString());
}
else if(status == BluetoothGatt.GATT_READ_NOT_PERMITTED){
Log.i("BluetoothGattCallback", "Read not permitted for "+ characteristic.getUuid().toString());
}
else{
Log.i("BluetoothGattCallback", "Characteristic read failed for "+ characteristic.getUuid().toString());
}
}
};
private void printGattTable(List<BluetoothGattService> services){
if(services.isEmpty()){
Log.i("printGattTable", "No service and characteristic available, call discoverServices() first?");
return;
}
Iterator<BluetoothGattService> it = services.iterator();
while(it.hasNext()){
BluetoothGattService service = it.next();
Log.i("printGattTable", "Service "+ service.getUuid()+"\nCharacteristics:\n"+service.getCharacteristics()+"\n");
}
}
}
characteristicsAdapter.java:
package com.example.wbdmx;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.List;
public class characteristicAdapter extends ArrayAdapter<BluetoothGattCharacteristic> implements View.OnClickListener{
private List<BluetoothGattCharacteristic> characteristics;
private Context mContext;
private static class ViewHolder{
TextView characteristic_uuid;
TextView characteristic_property;
}
public characteristicAdapter(List<BluetoothGattCharacteristic> characteristics, Context m) {
super(m, R.layout.row_characteristic, characteristics);
this.characteristics = characteristics;
this.mContext = m;
}
@Override
public void onClick(View v) {
}
public View getView(int position, View convertView, ViewGroup parent){
BluetoothGattCharacteristic character = getItem(position);
ViewHolder viewHolder;
final View result;
if(convertView == null){
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.row_characteristic, parent, false);
viewHolder.characteristic_uuid = (TextView) convertView.findViewById(R.id.characteristic_uuid);
viewHolder.characteristic_property= (TextView) convertView.findViewById(R.id.characteristic_properties);
result = convertView;
convertView.setTag(viewHolder);
}
else{
viewHolder = (ViewHolder)convertView.getTag();
result = convertView;
}
viewHolder.characteristic_uuid.setText(character.getUuid().toString());
viewHolder.characteristic_property.setText((character.getProperties()));
return convertView;
}
}
编辑 1:删除了构建 gradle 文件和一些 xml 文件,因为我达到了字符数限制。
这是导致崩溃的原因:
2020-11-24 14:54:13.897 16688-16789/com.example.wbdmx D/BluetoothGatt: onConnectionUpdated() - Device=D7:42:44:9F:56:C9 interval=39 latency=0 timeout=500 status=0
2020-11-24 14:54:13.901 16688-16688/com.example.wbdmx W/ResourceType: No package identifier when getting value for resource number 0x00000002
2020-11-24 14:54:13.902 16688-16688/com.example.wbdmx D/AndroidRuntime: Shutting down VM
2020-11-24 14:54:13.912 16688-16688/com.example.wbdmx E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.wbdmx, PID: 16688
android.content.res.Resources$NotFoundException: String resource ID #0x2
at android.content.res.Resources.getText(Resources.java:339)
at android.widget.TextView.setText(TextView.java:5496)
at com.example.wbdmx.characteristicAdapter.getView(characteristicAdapter.java:53)
at android.widget.AbsListView.obtainView(AbsListView.java:2388)
at android.widget.ListView.measureHeightOfChildren(ListView.java:1408)
at android.widget.ListView.onMeasure(ListView.java:1315)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
at android.view.View.measure(View.java:22087)
at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:792)
at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:583)
at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.layout(ConstraintWidgetContainer.java:682)
at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.solveLinearSystem(BasicMeasure.java:159)
at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:290)
at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:119)
at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1578)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1690)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:146)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at androidx.appcompat.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:490)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:786)
at android.view.View.measure(View.java:22087)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2504)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1542)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1799)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1430)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6885)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
at android.view.Choreographer.doCallbacks(Choreographer.java:778)
at android.view.Choreographer.doFrame(Choreographer.java:713)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6606)
at java.lang.reflect.Method.invoke(Native Method)
2020-11-24 14:54:13.913 16688-16688/com.example.wbdmx E/AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)
所有这一切的发生是因为我没有在 characteristicAdapter 文件中的 getView() 中将 character.getProperties() 转换为字符串值。
我是 android 开发的新手,正在构建一个可以从 BLE 设备读取特征的应用程序。我能够连接到设备并读取其特性。但是,我有一个问题是使用适配器显示特性。最初,我尝试使用 RecycleView 适配器,但是当我试图通知我的 recycleview 适配器时,我的程序一直返回到主 activity,Logcat 上剩下的所有内容都是这样的:
2020-11-24 13:35:37.220 12966-12966/? I/zygote64: Late-enabling -Xcheck:jni
2020-11-24 13:35:37.346 12966-12966/com.example.wbdmx I/LoadedApk: No resource references to update in package zui.platform
2020-11-24 13:35:37.368 12966-12966/com.example.wbdmx W/Typeface: setThemeFont(): FontPath Not Changed!
2020-11-24 13:35:37.683 12966-12993/com.example.wbdmx D/OpenGLRenderer: HWUI GL Pipeline
2020-11-24 13:35:37.757 12966-12993/com.example.wbdmx I/Adreno: QUALCOMM build : e89802c, I1ef8061d77
Build Date : 03/28/18
OpenGL ES Shader Compiler Version: EV031.22.00.01
Local Branch :
Remote Branch : quic/gfx-adreno.lnx.1.0.r36-rel
Remote Branch : NONE
Reconstruct Branch : NOTHING
2020-11-24 13:35:37.766 12966-12993/com.example.wbdmx I/Adreno: PFP: 0x005ff087, ME: 0x005ff063
2020-11-24 13:35:37.774 12966-12993/com.example.wbdmx I/zygote64: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0
2020-11-24 13:35:37.776 12966-12993/com.example.wbdmx I/OpenGLRenderer: Initialized EGL, version 1.4
2020-11-24 13:35:37.776 12966-12993/com.example.wbdmx D/OpenGLRenderer: Swap behavior 2
2020-11-24 13:35:37.871 12966-12966/com.example.wbdmx D/QuickpanelHelper: mUpdateSettings connect cost:3
2020-11-24 13:35:37.887 12966-12966/com.example.wbdmx D/QuickpanelHelper: mUpdateSettings connect cost:1
我认为 recycleview 是问题所在,现在我正在使用自定义列表视图适配器,但在连接设备并且应用程序返回 MainActivity 视图后,我的 Logcat 中也会出现同样的问题。这是我的文件:
MainActivity.java:
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothLeScanner bleScanner=null;
private ScanSettings scanSettings;
private ScanCallback scanCallback;
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_PERMISSIONS = 2;
private boolean isScanning = false;
private ArrayList<ScanResult> scanResult = new ArrayList<>();
private scanResultAdapter scanAdapter;
private RecyclerView recyclerView;
private Button scan_btn;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this, "bluetooth_not_supported", Toast.LENGTH_SHORT).show();
finish();
}
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(linearLayoutManager);
scanAdapter = new scanResultAdapter(scanResult, new CustomItemClickListner() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onItemClick(View v, int position) {
if(isScanning){
stopScan();
}
final BluetoothDevice device = scanResult.get(position).getDevice();
Log.w("ScanResultAdapter", "Connecting to device "+device.getAddress());
Operations(v, device);
}
});
recyclerView.setAdapter(scanAdapter);
//button so that we can change its text when clicked
scan_btn = (Button) findViewById(R.id.search_button);
}
public void Operations(View view, BluetoothDevice device){
Intent intent = new Intent(this, BleOperationsActivity.class);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
startActivity(intent);
}
@Override
protected void onResume() {
super.onResume();
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
return;
}
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_PERMISSIONS);
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void startScan(){
//response to the scan button
bleScanner = mBluetoothAdapter.getBluetoothLeScanner();
scanSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
scanResult.clear();
scanAdapter.notifyDataSetChanged();
scanCallback = new ScanCallback(){
@Override
public void onScanResult(int callbackType, ScanResult result){
ListIterator<ScanResult> item_iterator = scanResult.listIterator();
boolean flag = true;
while(item_iterator.hasNext()){
ScanResult item = item_iterator.next();
if(item.getDevice().getAddress().equals(result.getDevice().getAddress())){
flag = false;
}
}
if(flag) {
Log.i("ScanCallback", "Found unique BLE device! Name : " + result.getDevice().getName() + " address: " +result.getDevice().getAddress());
scanResult.add(result);
scanAdapter.notifyItemInserted(scanResult.size()-1); }
}
@Override
public void onScanFailed(int errorCode){
Log.e("ScanCallback", "onScanFailed: code " + errorCode);
}
};
scanResult.clear();
// ScanFilter filter = new ScanFilter.Builder().setDeviceAddress("D7:42:44:9F:56:C9").build();
// List<ScanFilter> filters = null;
// filters.add(filter);
bleScanner.startScan(null, scanSettings, scanCallback);
isScanning = true;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void stopScan(){
bleScanner.stopScan(scanCallback);
isScanning = false;
scan_btn.setText("SEARCH FOR DEVICES");
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void button_function(View v) {
if(isScanning){
stopScan();
}
else{
startScan();
scan_btn.setText("STOP SEARCH");
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onPause() {
super.onPause();
if (bleScanner != null) {
stopScan();
bleScanner = null;
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onStop() {
super.onStop();
if (bleScanner != null) {
stopScan();
bleScanner = null;
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDestroy() {
super.onDestroy();
if (bleScanner != null) {
stopScan();
bleScanner = null;
}
}
}
scanResultAdapter.java:
package com.example.wbdmx;
import android.bluetooth.le.ScanResult;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class scanResultAdapter extends RecyclerView.Adapter<scanResultAdapter.ViewHolder> {
private List<ScanResult> scanResult;
private CustomItemClickListner listner;
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView deviceName;
TextView deviceAddress;
TextView deviceRssi;
public ViewHolder(View view) {
super(view);
// Define click listener for the ViewHolder's View
deviceName = view.findViewById(R.id.device_name);
deviceAddress = view.findViewById(R.id.device_address);
deviceRssi = view.findViewById(R.id.device_rssi);
}
}
public scanResultAdapter(List<ScanResult> dataSet, CustomItemClickListner listner) {
scanResult = dataSet;
this.listner = listner;
}
// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view, which defines the UI of the list
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.listitem_device, viewGroup, false);
final ViewHolder mViewHolder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listner.onItemClick(v, mViewHolder.getAdapterPosition());
}
});
return mViewHolder;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the
// contents of the view with that element
viewHolder.deviceName.setText(scanResult.get(position).getDevice().getName() == null ? "Unknown": scanResult.get(position).getDevice().getName());
viewHolder.deviceRssi.setText(String.valueOf(scanResult.get(position).getRssi()));
viewHolder.deviceAddress.setText(scanResult.get(position).getDevice().getAddress());
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
if (scanResult == null) return 0;
return scanResult.size();
}
}
BleOperations.java:
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.OrientationHelper;
import androidx.recyclerview.widget.RecyclerView;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanResult;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class BleOperationsActivity extends AppCompatActivity {
private BluetoothDevice device;
private ArrayList<BluetoothGattCharacteristic> characteristics = new ArrayList<>();
private List<BluetoothGattService> services;
private static final int GATT_MAX_MTU_SIZE = 517;
ListView listView;
private characteristicAdapter charAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device == null) Log.e("BleOperationsIntent", "Missing BluetoothDevice from MainActivity");
device.connectGatt(this, false, gattCallback);
setContentView(R.layout.activity_ble_operations);
listView = (ListView)findViewById(R.id.list);
}
// public void setupRecyclerView(){
// recyclerView = (RecyclerView) findViewById(R.id.characteristics_recycler_view);
// LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
// recyclerView.setLayoutManager(linearLayoutManager);
// charAdapter = new characteristicAdapter(characteristics, new CustomItemClickListner() {
// @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
// @Override
// public void onItemClick(View v, int position) {
// final BluetoothGattCharacteristic character= characteristics.get(position);
// }
// }, this);
// recyclerView.setAdapter(charAdapter);
// }
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if(status == gatt.GATT_SUCCESS){
if (newState == BluetoothProfile.STATE_CONNECTED){
Log.w("BluetoothGattCallback", "Successfully connected to device " + gatt.getDevice().getAddress());
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
boolean ans = gatt.discoverServices();
Log.d("onConnectionStateChange", "Discover Services started: " + ans);
gatt.requestMtu(GATT_MAX_MTU_SIZE);
}
});
//gatt.discoverServices();
}
else if(newState == BluetoothProfile.STATE_DISCONNECTED){
Log.w("BluetoothGattCallback", "Succesfully disconnected form device "+ gatt.getDevice().getAddress());
gatt.close();
}
else{
Log.w("BluetoothGattCallback", "Error "+status+" encountered for "+gatt.getDevice().getAddress()+ "\nDisconnecting...");
gatt.close();
}
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status){
super.onServicesDiscovered(gatt, status);
services = gatt.getServices();
Log.w("BluetoothGattCallback", "Discovered "+ services.size()+" for "+gatt.getDevice().getAddress());
characteristics.addAll(services.stream().flatMap(s -> s.getCharacteristics().stream())
.collect(Collectors.toList()));
Log.w("onServicesDiscovered", "size = "+characteristics.size());
printGattTable(services);
runOnUiThread(new Runnable() {
@Override
public void run() {
// charAdapter.notifyDataSetChanged();
// Log.w("onServicesDiscovered", "adapter item count = "+ charAdapter.getItemCount());
charAdapter = new characteristicAdapter(characteristics, getApplicationContext());
listView.setAdapter(charAdapter);
}
});
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status){
boolean event = status ==BluetoothGatt.GATT_SUCCESS;
Log.w("onMtuChanged", "ATT MTU changed to "+mtu+" "+ event);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){
if(status == BluetoothGatt.GATT_SUCCESS){
Log.i("BluetoothGattCallback", "Read characteristic success for "+ characteristic.getUuid().toString());
}
else if(status == BluetoothGatt.GATT_READ_NOT_PERMITTED){
Log.i("BluetoothGattCallback", "Read not permitted for "+ characteristic.getUuid().toString());
}
else{
Log.i("BluetoothGattCallback", "Characteristic read failed for "+ characteristic.getUuid().toString());
}
}
};
private void printGattTable(List<BluetoothGattService> services){
if(services.isEmpty()){
Log.i("printGattTable", "No service and characteristic available, call discoverServices() first?");
return;
}
Iterator<BluetoothGattService> it = services.iterator();
while(it.hasNext()){
BluetoothGattService service = it.next();
Log.i("printGattTable", "Service "+ service.getUuid()+"\nCharacteristics:\n"+service.getCharacteristics()+"\n");
}
}
}
characteristicsAdapter.java:
package com.example.wbdmx;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.List;
public class characteristicAdapter extends ArrayAdapter<BluetoothGattCharacteristic> implements View.OnClickListener{
private List<BluetoothGattCharacteristic> characteristics;
private Context mContext;
private static class ViewHolder{
TextView characteristic_uuid;
TextView characteristic_property;
}
public characteristicAdapter(List<BluetoothGattCharacteristic> characteristics, Context m) {
super(m, R.layout.row_characteristic, characteristics);
this.characteristics = characteristics;
this.mContext = m;
}
@Override
public void onClick(View v) {
}
public View getView(int position, View convertView, ViewGroup parent){
BluetoothGattCharacteristic character = getItem(position);
ViewHolder viewHolder;
final View result;
if(convertView == null){
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.row_characteristic, parent, false);
viewHolder.characteristic_uuid = (TextView) convertView.findViewById(R.id.characteristic_uuid);
viewHolder.characteristic_property= (TextView) convertView.findViewById(R.id.characteristic_properties);
result = convertView;
convertView.setTag(viewHolder);
}
else{
viewHolder = (ViewHolder)convertView.getTag();
result = convertView;
}
viewHolder.characteristic_uuid.setText(character.getUuid().toString());
viewHolder.characteristic_property.setText((character.getProperties()));
return convertView;
}
}
编辑 1:删除了构建 gradle 文件和一些 xml 文件,因为我达到了字符数限制。
这是导致崩溃的原因:
2020-11-24 14:54:13.897 16688-16789/com.example.wbdmx D/BluetoothGatt: onConnectionUpdated() - Device=D7:42:44:9F:56:C9 interval=39 latency=0 timeout=500 status=0
2020-11-24 14:54:13.901 16688-16688/com.example.wbdmx W/ResourceType: No package identifier when getting value for resource number 0x00000002
2020-11-24 14:54:13.902 16688-16688/com.example.wbdmx D/AndroidRuntime: Shutting down VM
2020-11-24 14:54:13.912 16688-16688/com.example.wbdmx E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.wbdmx, PID: 16688
android.content.res.Resources$NotFoundException: String resource ID #0x2
at android.content.res.Resources.getText(Resources.java:339)
at android.widget.TextView.setText(TextView.java:5496)
at com.example.wbdmx.characteristicAdapter.getView(characteristicAdapter.java:53)
at android.widget.AbsListView.obtainView(AbsListView.java:2388)
at android.widget.ListView.measureHeightOfChildren(ListView.java:1408)
at android.widget.ListView.onMeasure(ListView.java:1315)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
at android.view.View.measure(View.java:22087)
at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:792)
at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:583)
at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.layout(ConstraintWidgetContainer.java:682)
at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.solveLinearSystem(BasicMeasure.java:159)
at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:290)
at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:119)
at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1578)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1690)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:146)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at androidx.appcompat.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:490)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1514)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:806)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:685)
at android.view.View.measure(View.java:22087)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6602)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:786)
at android.view.View.measure(View.java:22087)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2504)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1542)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1799)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1430)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6885)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
at android.view.Choreographer.doCallbacks(Choreographer.java:778)
at android.view.Choreographer.doFrame(Choreographer.java:713)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6606)
at java.lang.reflect.Method.invoke(Native Method)
2020-11-24 14:54:13.913 16688-16688/com.example.wbdmx E/AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)
所有这一切的发生是因为我没有在 characteristicAdapter 文件中的 getView() 中将 character.getProperties() 转换为字符串值。