天天看点

安卓学习第三天

事务:


银行转账
张三 给李四 200;

张三的账户 -200块钱 
//sql语句


李四的账户 +200块钱 
//sql语句 

android下使用数据库的事务 

1.打开数据库的事务

2.执行sql语句

3.设置事务的状态 (true)

4.提交数据 true 提交数据 false 回滚数据 

if (db.isOpen()) {

			try {
				// 开启数据库的事务
				db.beginTransaction();
				..............................
				db.setTransactionSuccessful();
			}
			// 显示的设置事务是否成功
			catch (Exception e) {
			} finally {
			//放在这里确保事务能够正常的结束
				db.endTransaction();
				db.close();
			}
}



ListView 一个重要的控件

把XML文件转换成一个View对象,安卓提供的有一个充气泵。
可以把某个扁的XML吹成一个饱满的View对象。

activity 是用来显示用户界面的,你可理解为是一个View容器。

布局转换成View对象?
inflater 是系统的一个服务 初始化服务
inflater = (LayoutInflater) this
				.getSystemService(LAYOUT_INFLATER_SERVICE);
// BaseAdapter 是 google的工程师 给ListAdapter的默认实现,因为直接实现ListAdapter方法太多了,所以我们用继承
 //这个抽象类的方法。这种方法是实现ListView适配器,最原始的方法。
    private class MyAdapter extends BaseAdapter{
    	/**
		 * 返回当前listview有多少个条目
		 */
		public int getCount() {
			return list.size();
		}

		/**
		 * 返回当前position位置对应的条目 的object对象
		 */
		public Object getItem(int position) {
			return list.get(position);
		}

		/**
		 * 返回当前position位置 某个条目的id
		 */
		public long getItemId(int position) {
			return position;
		}

		/**
		 * 返回每一个条目显示的具体内容
		 * 理论上讲有多少个条目就应该被调用多少次,其实是更多次。
		 * 因为首先它会计算当前这个界面会有多少个条目出现,根据下面的公式。
		 * 当滚屏的时间,每显示一次都需要调用一次getView的方法。
		 * 
		 * 计算当前界面 会有多少个条目出现
		 *  1.得到每一个textview的高度
		 *  2.得到listview的高度 
		 *  3. listview高度/textview高度 = 得到了一个屏幕显示的textview的个数
		 * 
		 * listview的每一个条目的显示 
		 * 都需要调用一次getview的方法 
		 * 屏幕上有多个item显示 就会调用多少getview的方法,
		 * 而且每显示一次就调用一次。
		 * 
		 * parent 代表的是 当前的这个listview
		 * convertView是listView对象的一个缓存。
		 */
		public View getView(int position, View convertView, ViewGroup parent) {
			/*TextView tv = new TextView(DbActivity.this);
			tv.setText("我是第"+position+"个");
			Log.i("aaaaa", "我被调用 了"+position);
			return tv;*/
			
			/*TextView tv = new TextView(DbActivity.this);
			Person person = list.get(position);
			tv.setText(person.getName()+"   |  " + person.getAccount());
			return tv;*/
			
			View view = inflater.inflate(R.layout.item, null);
			//接下来,我们就可以从这个view里面去寻找控件,因为我们之前是通过从当前的Acivity中找的空间。
			Person person = list.get(position);
			TextView name = (TextView) view.findViewById(R.id.name);
			TextView phone = (TextView) view.findViewById(R.id.phone);
			name.setText("姓名:"+person.getName());
			phone.setText("手机:"+person.getAccount());
			return view;
			
			

		}} 

点击事件
import android.widget.AdapterView.OnItemClickListener;
listView.setOnItemClickListener(new OnItemClickListener() {
        	/**
        	 * 通过Debug我们知道了这几个参数的含义
        	 * parent代表的是当前的ListView
        	 * view代表当前被点击条目的View对象
        	 * @param position The position of the view in the adapter.
        	 * @param id The row id of the item that was clicked.
        	 */
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				//方法一
				/*TextView name = (TextView) view.findViewById(R.id.name);
				TextView phone = (TextView) view.findViewById(R.id.phone);
				Toast.makeText(DbActivity.this,name.getText().toString(), Toast.LENGTH_SHORT).show();*/
				//方法二
				Person person = list.get(position);
				Toast.makeText(DbActivity.this,person.getAccount(), Toast.LENGTH_SHORT).show();
				//方法三
				Person p = (Person) parent.getItemAtPosition(position);
				
			}
		});


其实上面的那个方法已经是谷歌工程师采用的优化过的适配器,但是到了具体的业务
它又提出了更为精细的适配器,供我们方便的调用,如SimpleAdapter,ArrayAdaper,SimpleCursorAdapter。
其实还是自定义的Adapter比较强大。

SimpleAdapter其实还是继承了BaseAdapter类。实现的还是我们自定义的那些方法。一定要看源码,我们可以
直接知道它是怎么实现的,其实跟我们自定义实现是一样的。

在TextView中,android:text="phone",是当没有为这个东西赋值的时间会显示这具值,如果为其赋值后,就不再
显示这个值,相当于一个默认值 。

 //SimpleAdpater
        List<Map<String, String>> mylist = new ArrayList<Map<String,String>>();
        for(Person p : list){
        	Map<String, String> maps = new HashMap<String, String>();
        	maps.put("姓名",p.getName());
        	maps.put("手机", p.getAccount());
        	mylist.add(maps);
        }
        listView.setAdapter(new SimpleAdapter(DbActivity.this, mylist, R.layout.item,
        		new String[]{"姓名","手机"}, new int[]{R.id.name,R.id.phone}));
        
  //ArrayAdapter
        String[] personsArr = new String[list.size()];
        for (int i = 0; i < personsArr.length; i++) {
			personsArr[i] = list.get(i).getName();
		}
        listView.setAdapter(new ArrayAdapter<String>(DbActivity.this, R.layout.item,
        		R.id.name,personsArr));
        
        //SimpleCursorAdapter
        Cursor c = personDao.getAllPersonsCursor();
        listView.setAdapter(new SimpleCursorAdapter(this, R.layout.item, c,
        		new String[]{"name","account"}, new int[]{R.id.name,R.id.phone}));
/**
	 *有两大问题需要解决。
	 *第一、db和cursor都不需要关闭因为它将要使用SimpleCursorAdapter,这里面会自动维护。
	 *第二、SimpleCursorAdapter只识别_id的,如果在数据库中没有,那么可以用这种别名的方式
	 *来改变它。或者你在新建的时间就做了它。
	 */
	public Cursor getAllPersonsCursor(){
		Cursor cursor = null;
		SQLiteDatabase db = myDBHelper.getWritableDatabase();
		if (db.isOpen()) {
			cursor = db.rawQuery("select personid as _id ,name,account from person", null);
		}
		return cursor;
	}		
		
sqlite 数据库 标准:
要求我们数据库的主键 最好要以_id 作为名字  


我们已经知道,数据库是私有的,不能被其它程序访问,
所以我们需要一个中间人,可以把一个应用程序私有的
数据暴露给另外一个程序,让它访问。

content://cn.itcast.db.provider/persons
// 代表获取数据库person表中的所有的内容 
content://cn.itcast.db.provider/person/10
// 代表person表中第10个元素 


content://cn.itcast.db.provider/haha




内容提供者的使用流程 (自定义内容提供者,详情见db项目。)

1.如果a应用想把自己的数据库暴露给别的应用程序使用
就必须实现contentprovider 
创建一个类 继承系统的ContentProvider
一定要在清单文件里面配置 
指定uri的主机名 cn.itcast.db.personprovider

2. 定义匹配规则
  根据数据库的表结构 定义匹配规则 
content://cn.itcast.db.provider/persons
content://cn.itcast.db.provider/person/10


也可以根据业务方法, 指定匹配规则 
content://cn.itcast.db.provider/delete/10

3.在别的应用里面 获取contentResolver

contentResolver.query()
               .delete()
               .insert();


例如:新浪微博的分享功能就需要把自己的数据库给 暴露出来,这样才能让其它人访问。


应用:
1. 把自己应用的数据暴露给别的应用.(因为数据库是私有的。)
屏蔽数据库 底层操作的细节.
暴露uri  和要更改的数据.


2.可以给内容提供者注册一个观察者.


03-12 02:22:10.861: E/AndroidRuntime(25973): java.lang.SecurityException: Permission Denial: reading com.android.providers.telephony.SmsProvider uri content://sms/outbox from pid=25973, uid=10039 requires android.permission.READ_SMS


其实观察者(它就是内容改变的监听器)和监听器都是给某个方法注册一个回调函数,
然后在某个时间执行。
// 当数据发生改变的时候 就会通知baseuri
getContext().getContentResolver().notifyChange(baseuri, null);
然后我们在另外一个应用中去获得这个内容提供者的Uri,并且去实现一个类,
为ContentObserver,实现它的onchange回调方法,当改变的时间会调用这方法。

自定义内容提供者,并且监听它。
一、在内容提供者的程序中必须有一个继承ContentProvider的类,并且实现它的GRUD方法。
然后在这些GRUD方法中必须写这些东西
// 当数据发生改变的时候 ,会通知这个Uri.
getContext().getContentResolver().notifyChange(Uri.parse("content://cn.itcast.db.personprovider"), null);
二、然后在监听程序中,
  // 注册了一个内容观察者 
Uri uri = Uri.parse("content://cn.itcast.db.personprovider/");
getContentResolver().registerContentObserver(uri, true, new MyObserver(new Handler()));
public class MyObserver extends ContentObserver{
		public MyObserver(Handler handler) {
			super(handler);
		}
		/**
		 * 当内容观察者发现了数据发生改变的时候 调用的方法 
		 */
		@Override
		public void onChange(boolean selfChange) {
			System.out.println("数据发生改变了。");
			super.onChange(selfChange);
		}
}
在这种情况下,当第一个程序中的数据发生改变的时间就会触发第二个事件方法。其实是因为第二个程序中给这个URI注册
了一个方法,当第一个程序发生改变的时间会回调这个方法。

那么我们也就不理解在短信发送过程中,其实在短信程序中,它自己也为外部提供了数据的内容提供者,供其它程序来访问,
可以得到其内容提供者的URI,然后为这个URI注册一个回调函数,在安卓中叫做观察者。所以当它改变的时间我们也可以
获得其数据。所以关键是得到其内容提供者的URI.
如:
//为短信的URI注册一个事件。当这个被调用的时间,我们会发现它,并且调用我们自己方法。
Uri uri = Uri.parse("content://sms/");
getContentResolver().registerContentObserver(uri, true, new SmsObserver(new Handler()));

public class SmsObserver extends ContentObserver{

		public SmsObserver(Handler handler) {
			super(handler);
		}
		@Override
		public void onChange(boolean selfChange) {
			System.out.println("有新的短信产生 ");
			Cursor cursor = getContentResolver().query(Uri.parse("content://sms/outbox"),null, null, null, null); 
			while(cursor.moveToNext()){
				StringBuilder sb = new StringBuilder();
				sb.append("_id=").append(cursor.getInt(cursor.getColumnIndex("_id")));
				sb.append(";address=").append(cursor.getString(cursor.getColumnIndex("address")));
				sb.append(";body=").append(cursor.getString(cursor.getColumnIndex("body")));
				sb.append(";time=").append(cursor.getLong(cursor.getColumnIndex("date")));
				System.out.println(sb.toString());
			}
			super.onChange(selfChange);
		}
    	
    }

11-18 22:06:47.825: E/AndroidRuntime(31345): 
java.lang.SecurityException: 
Permission Denial:
 reading com.android.providers.telephony.SmsProvider uri content://sms/outbox from pid=31345, uid=10044 requires android.permission.READ_SMS
根据上面报的错,我们知道,我们缺失权限,这是获得缺失权限的重要方法。


由于很多软件都会读取通讯录,所以这是一个非常重要的方法。

三个表要了解,其实就是根据内容提供者去获取它的内容。


视频最后的一点思路。





https://github.com/android(包含所有安卓的源代码)


==============================
关于安卓自带的GRUD,你看下底层其实实现非常简单用的是StringBuilder组拼的SQL
语句,
 // Measurements show most sql lengths <= 152
StringBuilder sql = new StringBuilder(152);
为什么是152因为根据统计学上说,SQL语句一般不超过152个字符。
然后根据前台传输过来的集合,遍历它,组成一个正常的SQL语句,
你就会明白 ,其实非常简单。
SQLite是一个无类型的,其实都是按照字符串来的,所以你传参数的时间
可以都用new String[]{}.多看源码 。

事务: