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);

posted @ 2014-09-29 07:20  思捻如枫  阅读(352)  评论(0)    收藏  举报