Android开发经验分享(4)那些项目中我所犯过愚蠢的错误
【1】FragmentDialog与BaseListAdapter数据传递空引用
2014-09-17 10:24
一个普通的类BaseListAdapter通过在其的onClickListener单项中用Bundle bundle = new Bundle();
bundle.putSerializable("card",card);键值对的方式传递数据
然后 用FragmentDialog.setArguments(bundle); 传递数据
payDialog.show() 显示相应的对话框
【总结】 当时出现NullPoitException,空引用错误。原因是我虽然将bundle中add进去对象的数据了,但是没有setArguments(bundle)调用这句话,导致出现空引用错误。当时以为是传进去的数据card为空,找了半天没找到问题的本质。
【2】用SD卡来存储SQLite数据库的数据
在我还没有折腾出如何才能在adb shell中实现数据库的sql语句查询的时候。我当时想到一个办法,就是把sqlite数据库放到SD卡中读取。然后用Android文件浏览器查看当前sqlite数据库的位置。拿出数据库。中途碰到很多错误。比如公司提供的测试机他压根儿没SD卡,我在那找程序错误,找了很久没找到。写代码中不规范,没有使用单利模式对数据库进行操作。之后附上【SD卡读取Sqlite数据库操作源代码】
【总结】思维定式,出错以为全是程序没写对,没有发现。手机是个便携式设备,也会因为没有硬件资源而导致调用的某些库时,没有相应的硬件资源支持。对单例模式以及数据库的不理解操作不理解导致代码结构不清晰。
【注意】
SQLite中保存在SD卡中需要加如下权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
package cn.dxy.inderal.dao; import java.io.File; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteException; import android.os.Environment; import android.util.Log; public abstract class SDSQLiteOpenHelper { private static final String TAG = SDSQLiteOpenHelper.class.getSimpleName(); private final String mName; private final CursorFactory mFactory; private final int mNewVersion; protected Context mContext; protected SQLiteDatabase mDatabase = null; private boolean mIsInitializing = false; /** * Create a helper object to create, open, and/or manage a database. The * database is not actually created or opened until one of * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called. * * @param context * to use to open or create the database * @param name * of the database file, or null for an in-memory database * @param factory * to use for creating cursor objects, or null for the default * @param version * number of the database (starting at 1); if the database is * older, {@link #onUpgrade} will be used to upgrade the database */ public SDSQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) { if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version); mContext = context; mName = name; mFactory = factory; mNewVersion = version; } /** * Create and/or open a database that will be used for reading and writing. * Once opened successfully, the database is cached, so you can call this * method every time you need to write to the database. Make sure to call * {@link #close} when you no longer need it. * * <p> * Errors such as bad permissions or a full disk may cause this operation to * fail, but future attempts may succeed if the problem is fixed. * </p> * * @throws SQLiteException * if the database cannot be opened for writing * @return a read/write database object valid until {@link #close} is called */ public synchronized SQLiteDatabase getWritableDatabase() throws SQLiteException { if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) { return mDatabase; // The database is already open for business } if (mIsInitializing) { throw new IllegalStateException("getWritableDatabase called recursively"); } // If we have a read-only database open, someone could be using it // (though they shouldn't), which would cause a lock to be held on // the file, and our attempts to open the database read-write would // fail waiting for the file lock. To prevent that, we acquire the // lock on the read-only database, which shuts out other users. boolean success = false; SQLiteDatabase db = null; try { mIsInitializing = true; if (mName == null) { db = SQLiteDatabase.create(null); } else { // File dbFile = getDatabasePath(mName); String path = getDatabasePath(mName).getPath(); db = SQLiteDatabase.openOrCreateDatabase(path,mFactory); // if (!dbFile.exists()) { // Log.e(TAG, "DB file is not exist !"); // return null; // } // db = SQLiteDatabase.openOrCreateDatabase(dbFile.getPath(), mFactory); } int version = db.getVersion(); if (version != mNewVersion) { db.beginTransaction(); try { if (version == 0) { onCreate(db); } else { onUpgrade(db, version, mNewVersion); } db.setVersion(mNewVersion); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } onOpen(db); success = true; return db; } finally { mIsInitializing = false; if (success) { if (mDatabase != null) { try { mDatabase.close(); } catch (Exception e) { } } mDatabase = db; } else { if (db != null && db != mDatabase) db.close(); } } } /** * Create and/or open a database. This will be the same object returned by * {@link #getWritableDatabase} unless some problem, such as a full disk, * requires the database to be opened read-only. In that case, a read-only * database object will be returned. If the problem is fixed, a future call * to {@link #getWritableDatabase} may succeed, in which case the read-only * database object will be closed and the read/write object will be returned * in the future. * * @throws SQLiteException * if the database cannot be opened * @return a database object valid until {@link #getWritableDatabase} or * {@link #close} is called. */ public synchronized SQLiteDatabase getReadableDatabase() { if (mDatabase != null && mDatabase.isOpen()) { return mDatabase; // The database is already open for business } if (mIsInitializing) { throw new IllegalStateException("getReadableDatabase called recursively"); } try { return getWritableDatabase(); } catch (SQLiteException e) { if (mName == null) throw e; // Can't open a temp database read-only! Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e); } SQLiteDatabase db = null; try { mIsInitializing = true; String path = getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READWRITE); if (db.getVersion() != mNewVersion) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + mNewVersion + ": " + path); } onOpen(db); Log.w(TAG, "Opened " + mName + " in read-only mode"); mDatabase = db; return mDatabase; } finally { mIsInitializing = false; if (db != null && db != mDatabase) db.close(); } } /** * Close any open database object. */ public synchronized void close() { if (mIsInitializing) throw new IllegalStateException("Closed during initialization"); if (mDatabase != null && mDatabase.isOpen()) { mDatabase.close(); mDatabase = null; } } public File getDatabasePath(String name) { String EXTERN_PATH = null; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) == true) { EXTERN_PATH = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/dxy/inderal/"; File f = new File(EXTERN_PATH); if (!f.exists()) { f.mkdirs(); } } return new File(EXTERN_PATH + name); } /** * Called when the database is created for the first time. This is where the * creation of tables and the initial population of the tables should * happen. * * @param db * The database. */ public abstract void onCreate(SQLiteDatabase db); /** * Called when the database needs to be upgraded. The implementation should * use this method to drop tables, add tables, or do anything else it needs * to upgrade to the new schema version. * * <p> * The SQLite ALTER TABLE documentation can be found <a * href="http://sqlite.org/lang_altertable.html">here</a>. If you add new * columns you can use ALTER TABLE to insert them into a live table. If you * rename or remove columns you can use ALTER TABLE to rename the old table, * then create the new table and then populate the new table with the * contents of the old table. * * @param db * The database. * @param oldVersion * The old database version. * @param newVersion * The new database version. */ public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); /** * Called when the database has been opened. Override method should check * {@link SQLiteDatabase#isReadOnly} before updating the database. * * @param db * The database. */ public void onOpen(SQLiteDatabase db) { } }
【3】我们做的是基于物联网的项目,所以要用到Android和wifi硬件模块的交互。那天模块直接发不出数据,嵌入式组也在积极排查错误,但是无论是程序还是wifi本身都没有障碍。真是丈二和尚摸不清头脑。结果换了一个USB口,将wifi模块的USB接口插入到机箱后面的端口,居然好了。
【总结】电脑插入WIFI模块 由于前置USB电流太小导致读取不了wifi模块的SSID,换成后置的USB接口就OK了
【4】Android端无法打开Socket,无法传输数据
【总结】原来是我将程序的Socket端口给弄错了,我们的项目有2个子项目,智能钥匙和手机支付。我做的是手机支付。结果把Socket的端口换成了钥匙的端口

【5】今天在做Android摇一摇功能优化,我想了很久很久。首先,对Android不同的机型和不同Android型号进行测试,发现决定手机摇一摇的灵敏度是由重力感应器的三个方向上的值 x ,y ,z的灵敏度所决定的。但是不同机型不同系统的每一个x , y ,z相关的灵敏度也完全不同。Android触发摇一摇功能的原理是这样的:当x y z达到一个阀值,如三星的是19.那就可以触发。于是我写了一个程序去测各个x y z的灵敏度。思路大概是这样:开一个定时器,当你手摇动的时候,就会触发onResume()方法,然后在一段时间内记录X Y Z变化的次数。固定时间内 数据变化的次数来测灵敏度。然后筛选一些有用的数据add进数据库,通过查询语句进行查询。虽然,最终没有达到非常好的用户体验。但是较之前有了一定改善。程序还有不恰当的地方,请各位博友指正!!
package com.example.test;
import java.util.Timer;
import java.util.TimerTask;
import com.example.test.ShakeListener.OnShakeListener;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.telephony.TelephonyManager;
import android.util.FloatMath;
import android.util.Log;
import android.widget.EditText;
import android.widget.Toast;
/**
* 安卓晃动手机监听--“摇一摇”
*
* @author jason0539
*
*/
public class Test extends Activity
{
private SensorManager sensorManager;
private Vibrator vibrator;
private ShakeListener mShakeListener = null;
private static final String TAG = "TestSensorActivity";
private static final String TAG2="Time";
private static final int SENSOR_SHAKE = 10;
private EditText editX = null;
private EditText editY = null;
private EditText editZ = null;
private EditText editDeviceId = null;
private EditText EditPhoneName = null;
private static final int start = 1;
private static int count = 0 ;
private long startTime;
private long lastUpdate;
static final int UPDATE_INTERVAL = 100;
private boolean flag = false;
private boolean isSensor = true;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.test_sensor);
editX = (EditText) findViewById(R.id.EditX);
editY = (EditText) findViewById(R.id.EditY);
editZ = (EditText) findViewById(R.id.EditZ);
editDeviceId =(EditText)findViewById(R.id.deviceId);
EditPhoneName =(EditText)findViewById(R.id.PhoneName);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
mShakeListener = new ShakeListener(this);
mShakeListener.setOnShakeListener(new OnShakeListener()
{
@Override
public void onShake()
{
mShakeListener.start();
}
});
}
private Timer timer;
@Override
protected void onResume()
{
super.onResume();
if (sensorManager != null)
{// 注册监听器
sensorManager.registerListener(sensorEventListener,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
// 第一个参数是Listener,第二个参数是所得传感器类型,第三个参数值获取传感器信息的频率
startTime = System.currentTimeMillis();
Log.d(TAG2,"startTime>>>"+String.valueOf(startTime));
count = 0;
if(timer != null)
{
if(timerTask!=null)
{
timerTask.cancel();
}
timer = new Timer();
timer.schedule(timerTask, 1500, 10000);
}
}
}
private TimerTask timerTask = new TimerTask(){
@Override
public void run()
{
// TODO Auto-generated method stub
if(!flag){
flag = true;
}else{
if(timerTask != null)
{
timer.cancel();
}
Log.d(TAG2,"10秒内摇动的次数count="+count);
}
}
};
@Override
protected void onStop()
{
super.onStop();
if (sensorManager != null)
{// 取消监听器
sensorManager.unregisterListener(sensorEventListener);
}
}
/**
* 重力感应监听
*/
private SensorEventListener sensorEventListener = new SensorEventListener()
{
private int temp;
@Override
public void onSensorChanged(SensorEvent event)
{
if(timer == null){
timer = new TimerTask();
timer.schedule(task, when);
}
float[] values = event.values;
float x = values[0]; // x轴方向的重力加速度,向右为正
float y = values[1]; // y轴方向的重力加速度,向前为正
float z = values[2]; // z轴方向的重力加速度,向上为正
int x1 = (int)x;
int y1 = (int)y;
int z1 = (int)z;
temp = x1;
if(x1 == temp){
return;
}else{
count ++;
temp = x1;
}
//当前加速度和前一次加速度差值不等于0 表示有晃动
count++;
// Log.d(TAG2,"每次摇动count="+String.valueOf(count));
// if( x1 == start){
if( x1 >= 10)
{
lastUpdate = System.currentTimeMillis();
Log.d(TAG2,"current X is "+x1+">>>lastUpdateTime>>>"+lastUpdate);
Log.d(TAG2,"The cost time is >>>>"+String.valueOf(lastUpdate-startTime));
if (sensorManager != null)
{
// 取消监听器
sensorManager.unregisterListener(sensorEventListener);
}
}
editX.setText(Integer.toString(x1));
editY.setText(Integer.toString(y1));
editZ.setText(Integer.toString(z1));
editDeviceId.setText(readDevice());
EditPhoneName.setText(readphoneName());
String Device = editDeviceId.getText().toString();
String PhoneName=EditPhoneName.getText().toString();
String X = editX.getText().toString();
String Y = editY.getText().toString();
String Z = editZ.getText().toString();
Log.d(TAG, "x轴方向的重力加速度" +X+ ";y轴方向的重力加速度" + Y+ ";z轴方向的重力加速度" + Z);
// 一般在这三个方向的重力加速度达到40就达到了摇晃手机的状态。
int medumx = 17;// 如果不敏感请自行调低该数值,低于10的话就不行了,因为z轴上的加速度本身就已经达到10了
int medumy =17;
int medumz = 19;
if(count <100)
{
if(readphoneName().equals("HUAWEI"))
{
medumx = 11;
medumy = 16;
medumz= 19;
}
else
{
medumx = 17;
medumy = 16;
medumz= 20;
}
}else if(count < 150)
{
medumx = 13;
medumy = 15;
medumz = 13;
}else if(count <190)
{
medumx = 19;
medumy = 19;
}else if(count <220 )
{
medumx = 20;
medumy = 16;
}
/*switch(x1)
{
case 19:
Log.d(TAG2, "medumx 19");
break;
case 18:
Log.d(TAG2, "medumx 18");
break;
case 17:
Log.d(TAG2,"medumx 17");
break;
case 16:
Log.d(TAG2,"medumx 16");
break;
case 15:
Log.d(TAG2, "medumx 15");
break;
case 14:
Log.d(TAG2,"medumx 14");
break;
case 13:
Log.d(TAG2,"medumx 13");
break;
case 12:
Log.d(TAG2,"medumx 12");
break;
case 11:
Log.d(TAG2,"medumx 11");
break;
case 10:
Log.d(TAG2,"medumx 10");
}
switch(y1)
{
case 19:
Log.d(TAG2, "medumy 19");
break;
case 18:
Log.d(TAG2, "medumy 18");
break;
case 17:
Log.d(TAG2,"medumy 17");
break;
case 16:
Log.d(TAG2,"medumy 16");
break;
case 15:
Log.d(TAG2, "medumy 15");
break;
case 14:
Log.d(TAG2,"medumy 14");
break;
case 13:
Log.d(TAG2,"medumy 13");
}
switch(z1)
{
case 19:
Log.d(TAG2, "medumz 19");
break;
case 18:
Log.d(TAG2, "medumz 18");
break;
case 17:
Log.d(TAG2,"medumz 17");
break;
case 16:
Log.d(TAG2,"medumz 16");
break;
case 15:
Log.d(TAG2, "medumz 15");
break;
case 14:
Log.d(TAG2,"medumz 14");
break;
case 13:
Log.d(TAG2,"medumz 13");
}*/
if(Math.abs(x)>medumx || Math.abs(y)>medumy || Math.abs(z)>medumz)
{
vibrator.vibrate(200);
Message msg = new Message();
msg.what = SENSOR_SHAKE;
handler.sendMessage(msg);
//isSensor = false;
Log.d(TAG2,"MedumX="+medumx);
Log.d(TAG2,"MedumX="+medumy);
Log.d(TAG2,"MedumX="+medumz);
}
/*if (Math.abs(x) > medumx || Math.abs(y) > medumy || Math.abs(z) > medumz)
{
vibrator.vibrate(200);
Message msg = new Message();
msg.what = SENSOR_SHAKE;
handler.sendMessage(msg);
}*/
/*long endTime = System.currentTimeMillis();
long time = endTime-startTime;*/
// 内容值实例
/* ContentValues values1 = new ContentValues();
values1.put("phoneName", PhoneName);
values1.put("X", X);
values1.put("Y", Y);
values1.put("Z", Z);
values1.put("time", System.currentTimeMillis());
DBService.getInstance(TestForSensor.this).insert(values1);*/
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
public String readDevice()
{
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
StringBuilder sb = new StringBuilder();
sb.append(tm.getDeviceId());
return sb.toString().substring(1,2);
}
public String readphoneName()
{
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
StringBuilder sb = new StringBuilder();
sb.append(android.os.Build.MODEL);
return sb.toString().substring(0,6);
}
};
public static float readNumber(float number)
{
return (float)(Math.round(number*10))/10;
}
/**
* 动作执行
*/
Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch (msg.what)
{
case SENSOR_SHAKE:
Toast.makeText(Test.this, "检测到摇晃,执行操作!",
Toast.LENGTH_SHORT).show();
Log.i(TAG, "检测到摇晃,执行操作!");
break;
}
}
};
}
【总结】在实施过程中遇到这个错误:java.lang.IllegalStateException: TimerTask is scheduled already错误的解决方法 解决办法是参考了CSDN一位博友的文章
原因是这样:
这里需要注意两个问题:
if (mTimerTask != null){
mTimerTask.cancel(); //将原任务从队列中移除
}
每次放定时任务前,确保之前任务已从定时器队列中移除
mTimerTask = new MyTimerTask(); // 新建一个任务
每次放任务都要新建一个对象,否则出现一下错误:
ERROR/AndroidRuntime(11761): java.lang.IllegalStateException: TimerTask is scheduled already
所以同一个定时器任务只能被放置一次
【注意】
要实现摇一摇功能需要加如下权限
<uses-permission android:name="android.permission.VIBRATE" />
【6】【SQLite错误】
百度了一下 说明:数据库的表名字错了。其中说没有这样的数据库表名,说明是写数据库的表的名称取错了

原因:手机的SD卡有问题 所以找不到指定的SD卡的路径

今天碰到的问题:数据库的ID 设置为主键的时候,要将其设置为自动增长 并且不为空 ,且数据类型是Integer才可以
【7】今天代码导入到eclipse 出现乱码了,搞的焦头烂额。
【解决办法:】
http://blog.csdn.net/zhthl20091003/article/details/6847704
在该工程上右击propertitys->Recourses->other-> 然后选择 UTF-8 全部搞定
【8】
2014-3-7 16:00
今天碰到2个问题
1 在同一个文件里 两个监听事件发生冲突的时候的解决方法
2 如何给一个EditText设置只能输入数字和字母
(1)在XML里面直接设置
<EditText android:singleLine="true" android:numeric="integer"
/> (2)
直接生成 DigitsKeyLinstener()
et_1.setKeyListener(new DigitsKeyListener(false,true)); 设置只允许输入数字的代码
cardnumber.setInputType(EditorInfo.TYPE_CLASS_PHONE);

浙公网安备 33010602011771号