1. Handler的使用
1.Handler类是android.os下的一个类,handler.post(Runnable r)可以将一个线程添加到消息队列中;handler.postDelay(Runnable r, long delayTime)在延迟delayTime毫秒后,将线程添加到消息队列中;handler.removeCalbacks(Runnable r)将队列中挂起的Runnable移出。
2.Handler.obtainMessage()可以获得一条消息,这个消息有两个整型的参数arg1和arg2,利用它可以传递消息。在创建Handler的时候,如果用
1
2
3
4
5
6
|
Handler handler =
new
Handler(){
@Override
public
void
handleMessage(Message msg){
……
}
}
|
来创建的话,该handler可以从消息队列(MessageQueue)中取出消息并在handleMessage方法中处理。而在另一个地方,也可以使用handler.sendMessage(Message msg)来向消息队列中发送消息。
Handler对于线程的处理并不是另外开启一个线程,而是在原有线程的基础上调用另一个Runnable的run函数。HanlderThread类实现了Looper来处理消息队列的功能,这个类由Android操作系统提供。利用该类,可以使用getLooper()(在使用getLooper()方法之前,必须调用该类的start()方法,否则这个Looper是null)方法来获取这个Looper,之后,将这个Looper以参数的形式传递给Handler的构造函数,此时这个Handler的handleMessage()方法就可以由另一个线程来处理了,就不会阻塞Activity。
可以把Bundle看作一个特殊的Map,只是它的键固定为String类型,值也只有几种基本的数据类型。
在使用Message传递数据的时候,发送之前可以使用Message.obj来传递一个数据,这个数据是一个Object即可。处理消息的时候,也可以使用Message.obj来获取,此时需要向下转型。如果需要传递大量数据,可以使用Message.setData(Bundle data)。
Message.sendToTarget();方法可以讲这个message发送给创建这个Message的Handler,在这个Handler里面可以接收该Message。
2. SQLite的使用
1. SQLiteOpenHelper类的使用
public abstract class SQLiteOpenHelper类中有几个函数:
onCreate(SQLiteDatabase db);当数据库第一次创建的时候调用这个方法
onOpen(SQLiteDatabase db);当数据库被打开的时候调用这个方法
onUpgrade(SQLiteDatabase db);当数据库需要升级的时候调用这个方法
getReadableDatabase();获得可以读的数据库
getWritbaleDatabase();获得可以写的数据库
我们一般继承这个类,然后调用构造函数(父类没有默认构造函数)。数据库的版本为整数,从1开始,每次升级数据库版本的时候,这个值依次递增。当发现这个值变化的时候,就意味着数据库已经进行了版本升级。
使用SQLiteDatabase.execSQL(String sql)来执行SQL语句。
2.adb工具的使用
可以使用adb工具(在cmd中输入adb即可,在这之前需要为它配置环境变量,在sdk/platform-tools下面,另一个就是要保证模拟器AVD是打开的,否则会报错)来查看数据库中的变化:
adb shell:进入Linux命令行(android操作系统是以Linux为核心的)
ls –l:以完整的形式显示文件和文件夹
cd data:进入data
cd data:再进入data
这里面有很多文件夹(以应用程序的包名命名),每一个应用程序都有一个文件夹
cd com.example.sqlite3:进入我们当前的应用程序
创建数据库后,这里面会多一个叫databases的文件夹
cd databases:进入这个文件夹,这里面可以看到刚刚创建的数据库
sqlite3 mydb:使用SQLite打开数据库mydb
然后就可以像Mysql一样使用数据库了:
select * from table_name;……
3.插入数据
使用ContentValues类来帮助插入数据:
1
2
3
4
5
6
7
8
|
ContentValues values =
new
ContentValues();
values.put(“id”,
1
);
//第一个参数为列名,第二个参数为列的值
values.put(“name”,”alvis”);
//DatabaseHelper类继承于SQLiteOpenHelper类
DatabaseHelper dbHelper =
new
DatabaseHelper(MainActivity.
this
,”mydb”);
//得到一个数据库对象
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.insert(“user”,
null
,values);
//调用insert方法插入数据。
|
insert方法的第一个参数为表名,第二个直接填null即可。第三个为带有数据的ContentValues对象。
4.更新数据
1
2
3
4
5
6
|
DatabaseHelper dbHelper =
new
DatabaseHelper(MainActivity.
this
,”mydb”);
//得到一个数据库对象
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values =
new
ContentValues();
values.put(“name”,”alvis”);
db.update(“user”,values,”id=?”,
new
String[]{“
1
”});
//这里使用占位符来设置参数
|
整个这几句其实就组成了一句话:
update user set name=’alvis’ where id=1;
update方法第一个参数是要更新的表名,第二个参数是带有要更新值的ContentValues对象,第三个参数是where字句的条件(可以有多个),第四个参数为where字句的值,类型为String[]。
5.查询数据
1
2
3
4
5
6
7
8
|
DatabaseHelper dbHelper =
new
DatabaseHelper(MainActivity.
this
,”mydb”);
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query(“user”,
new
String[]{“id”,”name”}, ”id=?”,
new
String[]{“
1
”},
null
,
null
,”id” );
while
(cursor.moveToNext()){
//这里处理获得的数据
String result = cursor.getString(cursor.getColumnIndex(“name”));
System.out.println(result);
}
|
cursor.moveToNext()方法有两个作用: 将游标下移看是否有下一条记录,有就返回true,否则返回false,就是说把指针移到下一个位置的同时还判断是否还有值。
query方法的参数依次为(后面三个参数均为字符串):
表名,查询的字段,查询条件,条件的参数,groupBy,having,orderBy
其实就是把一条select语句分解成了很多部分。
使用cursor获取数据的时候,参数一定要是要查询所在列的索引,这个索引可以通过cursor.getColumn(String columnName)来获得,于是就有了上面的写法。
建议不要太多的依赖于数据库,也不要在SQLite数据库中存入太多的数据,因为在开发的时候可能会出现很多奇怪的问题。
3. Android程序调试
可以使用Log类来输出调试信息:
1
2
3
4
5
|
Log.i(String tag,String message);
//Info级别的
Log.v(String tag,String message);
//致命的
Log.w(String tag,String message);
//警告
Log.e(String tag,String message);
//错误
Log.d(String tag,String message);
//调试
|
4. 文件下载
1.下载文件的步骤:
1).创建一个URL对象
1
|
URL url =
new
URL(String urlStr);
|
2).创建一个HttpURLConnection
1
|
HttpURLConnection urlConn = (HttpURLConnection)url.openConnection();
|
3).获得一个InputStream对象
1
|
InputStream inputStream = urlConn.getInputStream();
|
然后就可以使用流操作读取这个inputStream了
4).访问网络的权限:在Manifest.xml中声明:
1
|
<
uses-permission
android:name
=
"android.permission.INTERNET"
/>
|
源代码:
1
2
3
4
5
6
7
|
class
DownloadMP3Listener
implements
OnClickListener{
@Override
public
void
onClick(View v) {
HttpDownloader httpDownloader =
new
HttpDownloader();
int
status = httpDownloader.downFile(
"http://192.168.2.104:8080/source/ yiwangeshebude.mp3"
,
"Download"
,
"一万个舍不得.mp3"
);
}
}
|
错误:android.os.NetworkOnMainThreadException
这个异常大概意思是在主线程访问网络时出的异常。 Android在4.0之前的版本 支持在主线程中访问网络,但是在4.0以后对这部分程序进行了优化,也就是说访问网络的代码不能写在主线程中了。
解决办法:重新开启一个线程去跑网络相关的代码。
1
2
3
4
5
6
7
8
9
10
11
12
|
class
DownloadMP3Listener
implements
OnClickListener{
@Override
public
void
onClick(View v) {
new
Thread(){
//重新开启一个线程去访问网络
@Override
public
void
run(){
HttpDownloader httpDownloader =
new
HttpDownloader();
int
status = httpDownloader.downFile(
"http://192.168.2.104:8080/source/ yiwangeshebude.mp3"
,
"Download"
,
"一万个舍不得.mp3"
);
}
}.start();
}
}
|
2.访问SD卡
1).得到当前设备SD卡的目录:一般就是 /SDCARD
1
|
String path = Environment.getExternalStorageDirectory();
|
2).在Manifest.xml文件中设置访问SD卡的权限(和application标签同级)
1
|
<
uses-permission
android:name
=
"android.permission.INTERNET"
/>
|
5. ContentProvider初步
1.每一个ContentProvider都有一个公共的URI,这个URI表示这个ContentProvider所提供的数据。Android提供的ContentProvider都在android.provider包中。
2.ContentProvider所提供的函数:
query():查询 insert():插入 update():更新
delete():删除 getType():得到数据类型 onCreate():创建时的回调函数
3.实现ContentProvider的过程:
1).定义一个CONTENT_URI常量
2).定义一个类,继承ContentProvider类
3).实现query,insert,update,delete,getType和onCreate方法
4).在AndroidManifest.xml中进行声明
6. XML文件解析
ContentHandler接口
ContentHandler是Java类包中一个特殊的SAX接口,位于org.xml.sax包中,该接口封装了一些对事件处理的方法,当XML解析器开始解析XML输入文档时,它会遇到某些特殊的事件,比如文档的开头和结束、元素的开头和结束、以及元素中的字符数据等事件。当遇到这些事件时,XML解析器会调用ContentHandler接口中相应的方法来响应该事件。
ContentHandler接口的方法有以下几种:
void startDocument()
void endDocument()
void startElement(String uri,String localName,String qName,Attributes atts)
void endElement(String uri,String localName,String qName)
void characters(char[] ch,int start,int length)读取到中间的字符的时候调用
通常的处理流程如下:
1).创建事件处理程序
2).创建SAX解析器
3).将事件处理程序分配给解析器
4).对文档进行解析,将每个时间发送给处理程序
如下面的例子:
//1.创建一个SAXParserFactory
1
|
SAXParserFactory factory = SAXParserFactory.newInstance();
|
//2.通过这个factory获得一个XMLReader
1
|
XMLReader reader = factory.newSAXParser().getXMLReader();
|
//3.为XMLReader设置内容处理器
//注意这个方法的参数是一个ContentHander,但是由于ContentHandler里面的
//方法太多,因此这里使用适配器模式,MyContentHandler是继承于DefaultHandler
//的,而DefaultHandler实现了ContentHanlder接口。
1
|
reader.setContentHandler(
new
MyContentHandler());
|
//4.开始解析文件
1
|
reader.parse(
new
InputSource(
new
StringReader(xml)));
|
6. 广播机制
如果要监听某一类事件,则需要在Manifest.xml文件中声明:
1
2
3
4
5
|
<
receiver
android:name=”.TestReceiver”>
<
inteng-filter
>
<
action
androi:name=”andriod.intent.action.EDIT”/>
</
intent-filter
>
</
receiver
>
|
同时接收方需要继承BroadcastReceiver类,并复写onReceiver方法:
1
2
3
4
5
6
|
class
TestReceiver
extends
BroadcastReceiver{
@Override
public
void
onReceiver(Context context, Intent intent){
//这里写处理流程
}
}
|
注意蓝色部分的匹配。
而在发送方,可以使用:
1
2
3
|
Intent intent =
new
Intent();
intent.setAction(Intent.ACTION_EDIT);
TestActivity.
this
.sendBrodcast(intent);
|
注意intent的ACTION需要匹配,否则接收不到这个消息。
这个程序的大体流程是:TestActivity广播了一个action为ACTION_EDIT的 intent,TestReceiver因为继承了BroadcastReceiver,因此是一个广播的接收器,但是至于接不接收由TestActivity广播的intent,就要看在Manifest.xml文件中对TestReceiver的配置了。
在Manifest.xml文件中对于TestReceiver的配置里,<intent-filter>的<action>标签指明了TestReceiver监听的哪种action,只有当TestActivity广播的intent中的action和这里的action相同的时候,才会接收到,此时会生成一个TestReceiver,并调用该类的onReceiver方法。
注意:TestReceiver只做一次处理,如果处理过了,那么这个对象就没用了,如果再有同样的广播发送出来,那么操作系统会再生成这样一个对象来处理。