android学习笔记一(基础部分)
2015-02-02 15:20
253 查看
工程:
Phone、Sms (短信、单元测试)、File(存储文件、sdcard文件存储)、Other(访问file项目中的文件、AccessOtherAppPreferenceTest访问Preferences)、XML、SoftPreferences
权限:
1、添加权限方式
AndroidManifest.xml中添加电话服务权限:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.nbchina.action"
android:versionCode="1"
android:versionName="1.0">
略....
<uses-sdk android:minSdkVersion=“6" />
<uses-permission android:name="android.permission.CALL_PHONE"/>
</manifest>
2、权限种类
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 电话服务权限 -->
<uses-permission android:name="android.permission.CALL_PHONE"/>
<!-- 短信服务权限 -->
<uses-permission android:name="android.permission.SEND_SMS"/>
<!-- 单元测试权限 -->
<application ....>
<uses-library android:name="android.test.runner" />
....
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="cn.nbchina.action" android:label="Tests for My App" />
三种通知方式:
1、 状态栏通知 ppt93页
2、 对话框通知 ppt94页
3、 土司通知(Toast)
Toast.makeText(SmsActivity.this, R.string.success, Toast.LENGTH_LONG).show();
信息输出:
show view-->
Devices 管理设备
LogCat 查看输出信息 android输出日志信息。
输出级别(i:info;d:debug;e:error;w:warn)
Log.i(tag,输出内容); tag标签,一般使用测试代码所在类的类名作为标签。
一般定一个常量 private static final String TAG = "PersonServiceTest";
Log.i(TAG,输出内容);
System.out.println 也可向控制台输出信息 级别info
界面布局:
LinearLayout (线性布局)、AbsoluteLayout(绝对布局)、RelativeLayout(相对布局)、TableLayout(表格布局)、FrameLayout(帧布局)
[html] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/inputmobile"/>
<EditText android:layout_width="fill_parent" android:layout_height="wrap_content"
android:id="@+id/mobile"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/button"
android:id="@+id/button"/>
</LinearLayout>
布局几种方式
1、
LinearLayout (线性布局)
2、 AbsoluteLayout(绝对布局) 不建议使用
3、 RelativeLayout(相对布局)
4、 TableLayout(表格布局)
5、 FrameLayout(帧布局) 以屏幕左上角坐标为叠加,一个个画面叠加,主要使用在动画场合。比如一个视频,鼠标移动到上面,就会出现一个画面。这就是利用了帧布局
布局实例参考file:///D:/android-sdk-windows/docs/guide/topics/ui/layout-objects.html
Dev Guide项 下的Framework Topics -->User interface -->Common Layout Objects
可查看到这几种布局的实例代码
列 相对布局
[html] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/blue"
android:padding="10px" >
<TextView android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type here:" />
<EditText android:id="@+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_below="@id/label" />
<Button android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/entry"
android:layout_alignParentRight="true"
android:layout_marginLeft="10px"
android:text="OK" />
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok"
android:layout_alignTop="@id/ok"
android:text="Cancel" />
</RelativeLayout>
单元测试:
AndroidManifest.xml文件中添加权限
[java] view
plaincopyprint?
package com.nbchina.service;
public class PersonService {
public int save() {
String in = "78";
int b = new Integer(in);
return b;
}
}
上面targetPackage指定的包要和应用的package相同。
第二步:编写单元测试代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” ):
[java] view
plaincopyprint?
package com.nbchina.test;
import junit.framework.Assert;
import com.nbchina.service.PersonService;
import android.test.AndroidTestCase;
import android.util.Log;
public class PersonServiceTest extends AndroidTestCase {
private static final String TAG = "PersonServiceTest";
public void testSave() throws Throwable{
PersonService service = new PersonService();
int b = service.save();//检验save()方法运行是否正常
Log.i(TAG, "result="+ b);
//System.out.println();
//System.err.println("result="+ b);
//Assert.assertEquals(738, b);
}
}
[plain] view
plaincopyprint?
MV
Service
C—Activity
V—main.xml
Android中的显示单位
px (pixels)像素
一般HVGA代表320x480像素,这个用的比较多。
dip或dp (device independent pixels)设备独立像素
这个和设备硬件有关,一般为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。
sp (scaled pixels — best for text size)比例像素
主要处理字体的大小,可以根据系统的字体自适应。
为了适应不同分辨率,不同的像素密度,推荐使用dip ,文字使用sp。
数据存储与访问:
以下五种:
1、文件
2、SharedPreferences(参数)
3、SQLite数据库
4、内容提供者(Content provider)
5、网络
文件的权限:
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入:
openFileOutput("nbchina.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录
SDCard存放文件:
要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。
注意:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限
[java] view
plaincopyprint?
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
File saveFile = new File(sdCardDir, “nbchina.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("笔记本中国".getBytes());
outStream.close();
}
Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。
在Android平台上可以使用Simple API for XML(SAX) 事件驱动、 Document Object Model(DOM)和Android附带的事件驱动 pull解析器解析XML文件。
推荐使用pull
具体使用方式见代码。
SharedPreferences
主要用于保存软件参数,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下
访问SharedPreferences中的数据代码如下:
SharedPreferences sharedPreferences = getSharedPreferences("nbchina", Context.MODE_PRIVATE);
//getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 1);
如果访问其他应用中的Preference,前提条件是:该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。如:有个<package name>为cn.nbchina.action的应用使用下面语句创建了preference。
getSharedPreferences("nbchina", Context.MODE_WORLD_READABLE);
其他应用要访问上面应用的preference,首先需要创建上面应用的Context,然后通过Context 访问preference ,访问preference时会在应用所在包下的shared_prefs目录找到preference :
Context otherAppsContext = createPackageContext("cn.nbchina.action", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("nbchina", Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
参考Other工程的AccessOtherAppPreferenceTest代码
嵌入式关系型SQLite数据库存储数据
为了实现对数据库版本进行管理,SQLiteOpenHelper类提供了两个重要的方法,分别是onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion),前者用于初次使用软件时生成数据库表,后者用于升级软件时更新数据库表结构。当调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法获取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,接着调用onCreate()方法,onCreate()方法在初次生成数据库时才会被调用,在onCreate()方法里可以生成数据库表结构及添加一些应用使用到的初始化数据。onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的,假设数据库现在的版本是1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为了实现这一目的,可以把原来的数据库版本设置为2(有同学问设置为3行不行?当然可以,如果你愿意,设置为100也行),并且在onUpgrade()方法里面实现表结构的更新。当软件的版本升级次数比较多,这时在onUpgrade()方法里面可以根据原版号和目标版本号进行判断,然后作出相应的表结构及数据更新。
SQLiteOpenHelper对数据库进行版本管理
[java] view
plaincopyprint?
public class DatabaseHelper extends SQLiteOpenHelper {
//类没有实例化,是不能用作父类构造器的参数,必须声明为静态
private static final String name = "itcast"; //数据库名称
private static final int version = 1; //数据库版本
public DatabaseHelper(Context context) {
//第三个参数CursorFactory指定在执行查询时获得一个游标实例的工厂类,设置为null,代表使用系统默认的工厂类
super(context, name, null, version);
}
@Override public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS person (personid integer primary key autoincrement, name varchar(20), age INTEGER)");
}
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS person");
onCreate(db);
}
}
上面onUpgrade()方法在数据库版本每次发生变化时都会把用户手机上的数据库表删除,然后再重新创建。一般在实际项目中是不能这样做的,正确的做法是在更新数据库表结构时,还要考虑用户存放于数据库中的数据不会丢失。
创建数据库与添删改查操作、事务操作参考实例代码
数据显示
使用ListView组件
Item.xml
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!-- android:orientation="horizontal"水平 -->
<!-- android:layout_width="fill_parent"宽度填充 -->
<!-- android:layout_height="wrap_content"高度包裹 -->
<TextView
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="435"
android:id="@+id/id"
/>
<TextView
android:layout_width="100dip"
android:layout_height="wrap_content"
android:text="liming"
android:id="@+id/name"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="45"
android:id="@+id/amount"
/>
</LinearLayout>
Main.xml
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加记录"
android:id="@+id/insertbutton"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="编号"
/>
<TextView
android:layout_width="100dip"
android:layout_height="wrap_content"
android:text="姓名"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="存款"
/>
</LinearLayout>
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/listView"
/>
</LinearLayout>
MainActivity.java(使用SimpleCursorAdapter方式获取数据,建议这个。)
[java] view
plaincopyprint?
package com.nbchina.db;
public class MainActivity extends Activity {
private PersonService personService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.personService = new PersonService(this);
ListView listView = (ListView) this.findViewById(R.id.listView);
Cursor cursor = (Cursor) personService.getCursorScrollData(0, 5);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item, cursor,
new String[]{"_id", "name", "amount"}, new int[]{R.id.id, R.id.name, R.id.amount});
//记录集游标必须包括_id这个字段,否则会出错
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ListView lView = (ListView)parent;
Cursor data = (Cursor)lView.getItemAtPosition(position);
int personid = data.getInt(data.getColumnIndex("_id"));
Toast.makeText(MainActivity.this, personid+"", 1).show();
}
});
}
}
PersonService.java
package com.nbchina.service;
public class PersonService {
private DBOpenHelper dbOpenHelper;
public PersonService(Context context) {
this.dbOpenHelper = new DBOpenHelper(context);
}
public Cursor getCursorScrollData(Integer offset, Integer maxResult){
//SimpleCursorAdapter cursor字段中必须有个_id。所以必须这么做
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
return db.rawQuery("select personid as _id,name,amount from person limit ?,?",
new String[]{offset.toString(), maxResult.toString()});
}
}
MainActivity.java(使用SimpleAdapter方式获取数据,不建议)
[java] view
plaincopyprint?
package com.nbchina.db;
public class MainActivity extends Activity {
private PersonService personService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.personService = new PersonService(this);
ListView listView = (ListView) this.findViewById(R.id.listView);
List<Person> persons = personService.getScrollData(0, 5);
List<HashMap<String, Object>> data = new ArrayList<HashMap<String, Object>>();
for(Person person : persons){
HashMap<String, Object> item = new HashMap<String, Object>();
item.put("id", person.getId());
item.put("name", person.getName());
item.put("amount", person.getAmount());
data.add(item);
}
SimpleAdapter adapter = new SimpleAdapter(this, data, R.layout.item,
new String[]{"id", "name", "amount"}, new int[]{R.id.id, R.id.name, R.id.amount});
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ListView lView = (ListView)parent;
HashMap<String, Object> item = (HashMap<String, Object>)lView.getItemAtPosition(position);
Toast.makeText(MainActivity.this, item.get("id").toString(), 1).show();
}
});
}
}
PersonService.java
package com.nbchina.service;
public class PersonService {
private DBOpenHelper dbOpenHelper;
public PersonService(Context context) {
this.dbOpenHelper = new DBOpenHelper(context);
}
public List<Person> getScrollData(Integer offset, Integer maxResult){
List<Person> persons = new ArrayList<Person>();
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from person limit ?,?",
new String[]{offset.toString(), maxResult.toString()});
while(cursor.moveToNext()){
int personid = cursor.getInt(cursor.getColumnIndex("personid"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int amount = cursor.getInt(cursor.getColumnIndex("amount"));
Person person = new Person(personid, name);
person.setAmount(amount);
persons.add(person);
}
cursor.close();
return persons;
}
}
ContentProvider对外共享数据
一、配置AndroidManifest.xml
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nbchina.db"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
<activity android:name=".MainActivity"
android:label="@string/app_name">
……
</activity>
<!-- ".PersonProvider" 其中点代表应用的包路径。配置内容提供者 -->
<provider android:name=".PersonProvider" android:authorities="com.nbchina.providers.personprovider"/>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>
二、建立PersonProvider.java 继承ContentProvider
[java] view
plaincopyprint?
package com.nbchina.db;
public class PersonProvider extends ContentProvider {
//主件必须放入应用的包或者子包下。本项目person对外共享数据
private DBOpenHelper dbOpenHelper;
//UriMatcher.NO_MATCH 代表用户传进来的uri和里面所有的都不匹配的话,则返回NO_MATCH-1.
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private static final int PERSONS = 1;
private static final int PERSON = 2;
static{
MATCHER.addURI("com.nbchina.providers.personprovider", "person", PERSONS);
MATCHER.addURI("com.nbchina.providers.personprovider", "person/#", PERSON);
}
@Override
public boolean onCreate() {
//当内容提供者被实例化之后调用。生命周期只调用一次。
this.dbOpenHelper = new DBOpenHelper(this.getContext());
return false;
}
@Override
public String getType(Uri uri) {//返回当前所操作数据的类型
switch (MATCHER.match(uri)) {
case PERSONS:
return "vnd.android.cursor.dir/person";
case PERSON:
return "vnd.android.cursor.item/person";
default:
throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
}
/* 该方法用于返回当前Url所代表数据的MIME类型。
如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
要得到所有person记录的Uri为content://com.nbchina.provider.personprovider/person,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
得到id为10的person记录,Uri为content://com.nbchina.provider.personprovider/person/10,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。*/
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
switch (MATCHER.match(uri)) {
case PERSONS:
//如用字符串作为主键,则返回的是记录行号。若用数字作为主键,则返回的是主键值
long rowid = db.insert("person", "personid", values);
<span style="background-color: rgb(102, 51, 51);"><span style="color:#ffffff;">//返回Uri,构造新的uri,把新id附加进去
//ContentUris类使用介绍
//ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
//withAppendedId(uri, id)用于为路径加上ID部分:
//Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")
//Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://cn.itcast.provider.personprovider/person/10
//parseId(uri)方法用于从路径中获取ID部分:
//Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person/10")
//long personid = ContentUris.parseId(uri);//获取的结果为:10</span></span>
Uri insertUri = ContentUris.withAppendedId(uri, rowid);
<span style="background-color: rgb(0, 0, 102);"><span style="color:#ffffff;">this.getContext().getContentResolver().notifyChange(uri, null);//通知Other项目中监听</span></span>
return insertUri;
default:
throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
}
}
……其他操作代码见实例
}
Other项目中
[java] view
plaincopyprint?
package com.nbchina.other;
public class AccessContentProviderTest extends AndroidTestCase {
private static final String TAG = "AccessContentProviderTest";
/**
* 往内容提供者添加数据
* @throws Throwable
*/
public void testInsert() throws Throwable{
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。
ContentResolver contentResolver = this.getContext().getContentResolver();
Uri insertUri = Uri.parse("content://com.nbchina.providers.personprovider/person");
ContentValues values = new ContentValues();
values.put("name", "zhangxiaoxiao");
values.put("amount", 90);
Uri uri = contentResolver.insert(insertUri, values);
Log.i(TAG, uri.toString());
}
…其他操作见实例代码
}
监听ContentProvider数据的变化
一、
注册监听ContentProvider,在other项目中建立如下文件
[java] view
plaincopyprint?
package com.nbchina.other;
public class OtherActivity extends Activity {
//注册监听ContentProvider
private static final String TAG = "OtherActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Uri insertUri = Uri.parse("content://com.nbchina.providers.personprovider/person");
ContentResolver contentResolver = this.getContentResolver();
//对指定uri进行监听,如果该uri代表的数据发生变化,就会调用PersonObserver中的onChange()
<span style="color:#ffffff;background-color: rgb(51, 0, 153);">contentResolver.registerContentObserver(insertUri, true, new PersonObserver(new Handler()));</span>
}
private final class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {//一旦数据发生改变,则查询所有内容
ContentResolver contentResolver = getContentResolver();
Uri selectUri = Uri.parse("content://com.nbchina.providers.personprovider/person");
Cursor cursor = contentResolver.query(selectUri, null, null, null, "personid desc");
while(cursor.moveToNext()){
int id = cursor.getInt(cursor.getColumnIndex("personid"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int amount = cursor.getInt(cursor.getColumnIndex("amount"));
Log.i(TAG, "id="+ id + ",name="+ name+ ",amount="+ amount);
}
}
}
}
PersonProvider 的insert方法内加入如下代码
this.getContext().getContentResolver().notifyChange(uri, null);//通知
通信录中的联系人操作
手机内项目都通过内容提供者进行操作。
联系人内容提供者
D:\android-sdk-windows\platforms\android-8\sources\ContactsProvider\src\com\android\providers\contacts\ContactsProvider2.java
呼叫记录内容提供者
D:\android-sdk-windows\platforms\android-8\sources\ContactsProvider\src\com\android\providers\contacts\CallLogProvider.java
短信内容提供者
D:\android-sdk-windows\platforms\android-8\sources\TelephonyProvider\src\com\android\providers\telephony\SmsProvider.java
彩信内容提供者
D:\android-sdk-windows\platforms\android-8\sources\TelephonyProvider\src\com\android\providers\telephony\MmsProvider.java
电话内容提供者
D:\android-sdk-windows\platforms\android-8\sources\TelephonyProvider\src\com\android\providers\telephony\ TelephonyProvider.java
建立 Content项目
AndroidManifest.xml内加入权限
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nbchina.contact"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
</application>
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.nbchina.contact" android:label="Tests for My App" />
</manifest>
手机内容提供者操作代码如下
[java] view
plaincopyprint?
ContactTest.java
package com.nbchina.contact;
public class ContactTest extends AndroidTestCase {
private static final String TAG = "ContactTest";
public void testGetAllContact() throws Throwable{
//content://com.android.contacts/contacts
Uri uri = ContactsContract.Contacts.CONTENT_URI;
//访问内容提供者
ContentResolver contentResolver = this.getContext().getContentResolver();
//查询所有数据
//contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
Cursor cursor = contentResolver.query(uri, null, null, null, null);
while(cursor.moveToNext()){
StringBuilder sb = new StringBuilder();
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
sb.append("contactId=").append(contactId).append(",name=").append(name);
Cursor phones = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,
null, null);
while(phones.moveToNext()){
String phoneNumber = phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
sb.append(",phone=").append(phoneNumber);
}
phones.close();
Cursor emails = contentResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId,
null, null);
while (emails.moveToNext()) {
String emailAddress = emails.getString(emails.getColumnIndex(
ContactsContract.CommonDataKinds.Email.DATA));
sb.append(",emailAddress=").append(emailAddress);
}
emails.close();
Log.i(TAG, sb.toString());
}
cursor.close();
}
/**
* 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
* 这时后面插入data表的依据,只有执行空值插入,才能使插入的联系人在通讯录里面可见
*/
public void testInsert() {
ContentValues values = new ContentValues();
//首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri = this.getContext().getContentResolver().insert(RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
//往data表入姓名数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);//内容类型
values.put(StructuredName.GIVEN_NAME, "李天山");
this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
//往data表入电话数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
values.put(Phone.NUMBER, "13921009789");
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
//往data表入Email数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
values.put(Email.DATA, "liming@itcast.cn");
values.put(Email.TYPE, Email.TYPE_WORK);
this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
}
//批量添加,处于同一个事务中.一般添加最好使用此方式。删除修改等自己研究。
public void testSave() throws Throwable{
//文档位置:reference\android\provider\ContactsContract.RawContacts.html
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
int rawContactInsertIndex = ops.size();
ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, null)
.withValue(RawContacts.ACCOUNT_NAME, null)
.build());
//文档位置:reference\android\provider\ContactsContract.Data.html
ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
.withValue(StructuredName.GIVEN_NAME, "赵薇")
.build());
ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, "13671323809")
.withValue(Phone.TYPE, Phone.TYPE_MOBILE)
.withValue(Phone.LABEL, "手机号")
.build());
ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.DATA, "liming@nbchina.cn")
.withValue(Email.TYPE, Email.TYPE_WORK)
.build());
ContentProviderResult[] results = this.getContext().getContentResolver()
.applyBatch(ContactsContract.AUTHORITY, ops);
for(ContentProviderResult result : results){
Log.i(TAG, result.uri.toString());
}
}
}
Phone、Sms (短信、单元测试)、File(存储文件、sdcard文件存储)、Other(访问file项目中的文件、AccessOtherAppPreferenceTest访问Preferences)、XML、SoftPreferences
权限:
1、添加权限方式
AndroidManifest.xml中添加电话服务权限:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.nbchina.action"
android:versionCode="1"
android:versionName="1.0">
略....
<uses-sdk android:minSdkVersion=“6" />
<uses-permission android:name="android.permission.CALL_PHONE"/>
</manifest>
2、权限种类
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 电话服务权限 -->
<uses-permission android:name="android.permission.CALL_PHONE"/>
<!-- 短信服务权限 -->
<uses-permission android:name="android.permission.SEND_SMS"/>
<!-- 单元测试权限 -->
<application ....>
<uses-library android:name="android.test.runner" />
....
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="cn.nbchina.action" android:label="Tests for My App" />
三种通知方式:
1、 状态栏通知 ppt93页
2、 对话框通知 ppt94页
3、 土司通知(Toast)
Toast.makeText(SmsActivity.this, R.string.success, Toast.LENGTH_LONG).show();
信息输出:
show view-->
Devices 管理设备
LogCat 查看输出信息 android输出日志信息。
输出级别(i:info;d:debug;e:error;w:warn)
Log.i(tag,输出内容); tag标签,一般使用测试代码所在类的类名作为标签。
一般定一个常量 private static final String TAG = "PersonServiceTest";
Log.i(TAG,输出内容);
System.out.println 也可向控制台输出信息 级别info
界面布局:
LinearLayout (线性布局)、AbsoluteLayout(绝对布局)、RelativeLayout(相对布局)、TableLayout(表格布局)、FrameLayout(帧布局)
[html] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/inputmobile"/>
<EditText android:layout_width="fill_parent" android:layout_height="wrap_content"
android:id="@+id/mobile"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/button"
android:id="@+id/button"/>
</LinearLayout>
布局几种方式
1、
LinearLayout (线性布局)
2、 AbsoluteLayout(绝对布局) 不建议使用
3、 RelativeLayout(相对布局)
4、 TableLayout(表格布局)
5、 FrameLayout(帧布局) 以屏幕左上角坐标为叠加,一个个画面叠加,主要使用在动画场合。比如一个视频,鼠标移动到上面,就会出现一个画面。这就是利用了帧布局
布局实例参考file:///D:/android-sdk-windows/docs/guide/topics/ui/layout-objects.html
Dev Guide项 下的Framework Topics -->User interface -->Common Layout Objects
可查看到这几种布局的实例代码
列 相对布局
[html] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/blue"
android:padding="10px" >
<TextView android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type here:" />
<EditText android:id="@+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_below="@id/label" />
<Button android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/entry"
android:layout_alignParentRight="true"
android:layout_marginLeft="10px"
android:text="OK" />
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok"
android:layout_alignTop="@id/ok"
android:text="Cancel" />
</RelativeLayout>
单元测试:
AndroidManifest.xml文件中添加权限
[java] view
plaincopyprint?
package com.nbchina.service;
public class PersonService {
public int save() {
String in = "78";
int b = new Integer(in);
return b;
}
}
上面targetPackage指定的包要和应用的package相同。
第二步:编写单元测试代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” ):
[java] view
plaincopyprint?
package com.nbchina.test;
import junit.framework.Assert;
import com.nbchina.service.PersonService;
import android.test.AndroidTestCase;
import android.util.Log;
public class PersonServiceTest extends AndroidTestCase {
private static final String TAG = "PersonServiceTest";
public void testSave() throws Throwable{
PersonService service = new PersonService();
int b = service.save();//检验save()方法运行是否正常
Log.i(TAG, "result="+ b);
//System.out.println();
//System.err.println("result="+ b);
//Assert.assertEquals(738, b);
}
}
[plain] view
plaincopyprint?
MV
Service
C—Activity
V—main.xml
Android中的显示单位
px (pixels)像素
一般HVGA代表320x480像素,这个用的比较多。
dip或dp (device independent pixels)设备独立像素
这个和设备硬件有关,一般为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。
sp (scaled pixels — best for text size)比例像素
主要处理字体的大小,可以根据系统的字体自适应。
为了适应不同分辨率,不同的像素密度,推荐使用dip ,文字使用sp。
数据存储与访问:
以下五种:
1、文件
2、SharedPreferences(参数)
3、SQLite数据库
4、内容提供者(Content provider)
5、网络
文件的权限:
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入:
openFileOutput("nbchina.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录
SDCard存放文件:
要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。
注意:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限
[java] view
plaincopyprint?
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
File saveFile = new File(sdCardDir, “nbchina.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("笔记本中国".getBytes());
outStream.close();
}
Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。
在Android平台上可以使用Simple API for XML(SAX) 事件驱动、 Document Object Model(DOM)和Android附带的事件驱动 pull解析器解析XML文件。
推荐使用pull
具体使用方式见代码。
SharedPreferences
主要用于保存软件参数,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下
访问SharedPreferences中的数据代码如下:
SharedPreferences sharedPreferences = getSharedPreferences("nbchina", Context.MODE_PRIVATE);
//getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 1);
如果访问其他应用中的Preference,前提条件是:该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。如:有个<package name>为cn.nbchina.action的应用使用下面语句创建了preference。
getSharedPreferences("nbchina", Context.MODE_WORLD_READABLE);
其他应用要访问上面应用的preference,首先需要创建上面应用的Context,然后通过Context 访问preference ,访问preference时会在应用所在包下的shared_prefs目录找到preference :
Context otherAppsContext = createPackageContext("cn.nbchina.action", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("nbchina", Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
参考Other工程的AccessOtherAppPreferenceTest代码
嵌入式关系型SQLite数据库存储数据
为了实现对数据库版本进行管理,SQLiteOpenHelper类提供了两个重要的方法,分别是onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion),前者用于初次使用软件时生成数据库表,后者用于升级软件时更新数据库表结构。当调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法获取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,接着调用onCreate()方法,onCreate()方法在初次生成数据库时才会被调用,在onCreate()方法里可以生成数据库表结构及添加一些应用使用到的初始化数据。onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的,假设数据库现在的版本是1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为了实现这一目的,可以把原来的数据库版本设置为2(有同学问设置为3行不行?当然可以,如果你愿意,设置为100也行),并且在onUpgrade()方法里面实现表结构的更新。当软件的版本升级次数比较多,这时在onUpgrade()方法里面可以根据原版号和目标版本号进行判断,然后作出相应的表结构及数据更新。
SQLiteOpenHelper对数据库进行版本管理
[java] view
plaincopyprint?
public class DatabaseHelper extends SQLiteOpenHelper {
//类没有实例化,是不能用作父类构造器的参数,必须声明为静态
private static final String name = "itcast"; //数据库名称
private static final int version = 1; //数据库版本
public DatabaseHelper(Context context) {
//第三个参数CursorFactory指定在执行查询时获得一个游标实例的工厂类,设置为null,代表使用系统默认的工厂类
super(context, name, null, version);
}
@Override public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS person (personid integer primary key autoincrement, name varchar(20), age INTEGER)");
}
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS person");
onCreate(db);
}
}
上面onUpgrade()方法在数据库版本每次发生变化时都会把用户手机上的数据库表删除,然后再重新创建。一般在实际项目中是不能这样做的,正确的做法是在更新数据库表结构时,还要考虑用户存放于数据库中的数据不会丢失。
创建数据库与添删改查操作、事务操作参考实例代码
数据显示
使用ListView组件
Item.xml
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!-- android:orientation="horizontal"水平 -->
<!-- android:layout_width="fill_parent"宽度填充 -->
<!-- android:layout_height="wrap_content"高度包裹 -->
<TextView
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="435"
android:id="@+id/id"
/>
<TextView
android:layout_width="100dip"
android:layout_height="wrap_content"
android:text="liming"
android:id="@+id/name"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="45"
android:id="@+id/amount"
/>
</LinearLayout>
Main.xml
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加记录"
android:id="@+id/insertbutton"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="编号"
/>
<TextView
android:layout_width="100dip"
android:layout_height="wrap_content"
android:text="姓名"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="存款"
/>
</LinearLayout>
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/listView"
/>
</LinearLayout>
MainActivity.java(使用SimpleCursorAdapter方式获取数据,建议这个。)
[java] view
plaincopyprint?
package com.nbchina.db;
public class MainActivity extends Activity {
private PersonService personService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.personService = new PersonService(this);
ListView listView = (ListView) this.findViewById(R.id.listView);
Cursor cursor = (Cursor) personService.getCursorScrollData(0, 5);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item, cursor,
new String[]{"_id", "name", "amount"}, new int[]{R.id.id, R.id.name, R.id.amount});
//记录集游标必须包括_id这个字段,否则会出错
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ListView lView = (ListView)parent;
Cursor data = (Cursor)lView.getItemAtPosition(position);
int personid = data.getInt(data.getColumnIndex("_id"));
Toast.makeText(MainActivity.this, personid+"", 1).show();
}
});
}
}
PersonService.java
package com.nbchina.service;
public class PersonService {
private DBOpenHelper dbOpenHelper;
public PersonService(Context context) {
this.dbOpenHelper = new DBOpenHelper(context);
}
public Cursor getCursorScrollData(Integer offset, Integer maxResult){
//SimpleCursorAdapter cursor字段中必须有个_id。所以必须这么做
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
return db.rawQuery("select personid as _id,name,amount from person limit ?,?",
new String[]{offset.toString(), maxResult.toString()});
}
}
MainActivity.java(使用SimpleAdapter方式获取数据,不建议)
[java] view
plaincopyprint?
package com.nbchina.db;
public class MainActivity extends Activity {
private PersonService personService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.personService = new PersonService(this);
ListView listView = (ListView) this.findViewById(R.id.listView);
List<Person> persons = personService.getScrollData(0, 5);
List<HashMap<String, Object>> data = new ArrayList<HashMap<String, Object>>();
for(Person person : persons){
HashMap<String, Object> item = new HashMap<String, Object>();
item.put("id", person.getId());
item.put("name", person.getName());
item.put("amount", person.getAmount());
data.add(item);
}
SimpleAdapter adapter = new SimpleAdapter(this, data, R.layout.item,
new String[]{"id", "name", "amount"}, new int[]{R.id.id, R.id.name, R.id.amount});
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ListView lView = (ListView)parent;
HashMap<String, Object> item = (HashMap<String, Object>)lView.getItemAtPosition(position);
Toast.makeText(MainActivity.this, item.get("id").toString(), 1).show();
}
});
}
}
PersonService.java
package com.nbchina.service;
public class PersonService {
private DBOpenHelper dbOpenHelper;
public PersonService(Context context) {
this.dbOpenHelper = new DBOpenHelper(context);
}
public List<Person> getScrollData(Integer offset, Integer maxResult){
List<Person> persons = new ArrayList<Person>();
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from person limit ?,?",
new String[]{offset.toString(), maxResult.toString()});
while(cursor.moveToNext()){
int personid = cursor.getInt(cursor.getColumnIndex("personid"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int amount = cursor.getInt(cursor.getColumnIndex("amount"));
Person person = new Person(personid, name);
person.setAmount(amount);
persons.add(person);
}
cursor.close();
return persons;
}
}
ContentProvider对外共享数据
一、配置AndroidManifest.xml
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nbchina.db"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
<activity android:name=".MainActivity"
android:label="@string/app_name">
……
</activity>
<!-- ".PersonProvider" 其中点代表应用的包路径。配置内容提供者 -->
<provider android:name=".PersonProvider" android:authorities="com.nbchina.providers.personprovider"/>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>
二、建立PersonProvider.java 继承ContentProvider
[java] view
plaincopyprint?
package com.nbchina.db;
public class PersonProvider extends ContentProvider {
//主件必须放入应用的包或者子包下。本项目person对外共享数据
private DBOpenHelper dbOpenHelper;
//UriMatcher.NO_MATCH 代表用户传进来的uri和里面所有的都不匹配的话,则返回NO_MATCH-1.
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private static final int PERSONS = 1;
private static final int PERSON = 2;
static{
MATCHER.addURI("com.nbchina.providers.personprovider", "person", PERSONS);
MATCHER.addURI("com.nbchina.providers.personprovider", "person/#", PERSON);
}
@Override
public boolean onCreate() {
//当内容提供者被实例化之后调用。生命周期只调用一次。
this.dbOpenHelper = new DBOpenHelper(this.getContext());
return false;
}
@Override
public String getType(Uri uri) {//返回当前所操作数据的类型
switch (MATCHER.match(uri)) {
case PERSONS:
return "vnd.android.cursor.dir/person";
case PERSON:
return "vnd.android.cursor.item/person";
default:
throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
}
/* 该方法用于返回当前Url所代表数据的MIME类型。
如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
要得到所有person记录的Uri为content://com.nbchina.provider.personprovider/person,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
得到id为10的person记录,Uri为content://com.nbchina.provider.personprovider/person/10,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。*/
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
switch (MATCHER.match(uri)) {
case PERSONS:
//如用字符串作为主键,则返回的是记录行号。若用数字作为主键,则返回的是主键值
long rowid = db.insert("person", "personid", values);
<span style="background-color: rgb(102, 51, 51);"><span style="color:#ffffff;">//返回Uri,构造新的uri,把新id附加进去
//ContentUris类使用介绍
//ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
//withAppendedId(uri, id)用于为路径加上ID部分:
//Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")
//Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://cn.itcast.provider.personprovider/person/10
//parseId(uri)方法用于从路径中获取ID部分:
//Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person/10")
//long personid = ContentUris.parseId(uri);//获取的结果为:10</span></span>
Uri insertUri = ContentUris.withAppendedId(uri, rowid);
<span style="background-color: rgb(0, 0, 102);"><span style="color:#ffffff;">this.getContext().getContentResolver().notifyChange(uri, null);//通知Other项目中监听</span></span>
return insertUri;
default:
throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
}
}
……其他操作代码见实例
}
Other项目中
[java] view
plaincopyprint?
package com.nbchina.other;
public class AccessContentProviderTest extends AndroidTestCase {
private static final String TAG = "AccessContentProviderTest";
/**
* 往内容提供者添加数据
* @throws Throwable
*/
public void testInsert() throws Throwable{
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。
ContentResolver contentResolver = this.getContext().getContentResolver();
Uri insertUri = Uri.parse("content://com.nbchina.providers.personprovider/person");
ContentValues values = new ContentValues();
values.put("name", "zhangxiaoxiao");
values.put("amount", 90);
Uri uri = contentResolver.insert(insertUri, values);
Log.i(TAG, uri.toString());
}
…其他操作见实例代码
}
监听ContentProvider数据的变化
一、
注册监听ContentProvider,在other项目中建立如下文件
[java] view
plaincopyprint?
package com.nbchina.other;
public class OtherActivity extends Activity {
//注册监听ContentProvider
private static final String TAG = "OtherActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Uri insertUri = Uri.parse("content://com.nbchina.providers.personprovider/person");
ContentResolver contentResolver = this.getContentResolver();
//对指定uri进行监听,如果该uri代表的数据发生变化,就会调用PersonObserver中的onChange()
<span style="color:#ffffff;background-color: rgb(51, 0, 153);">contentResolver.registerContentObserver(insertUri, true, new PersonObserver(new Handler()));</span>
}
private final class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {//一旦数据发生改变,则查询所有内容
ContentResolver contentResolver = getContentResolver();
Uri selectUri = Uri.parse("content://com.nbchina.providers.personprovider/person");
Cursor cursor = contentResolver.query(selectUri, null, null, null, "personid desc");
while(cursor.moveToNext()){
int id = cursor.getInt(cursor.getColumnIndex("personid"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int amount = cursor.getInt(cursor.getColumnIndex("amount"));
Log.i(TAG, "id="+ id + ",name="+ name+ ",amount="+ amount);
}
}
}
}
PersonProvider 的insert方法内加入如下代码
this.getContext().getContentResolver().notifyChange(uri, null);//通知
通信录中的联系人操作
手机内项目都通过内容提供者进行操作。
联系人内容提供者
D:\android-sdk-windows\platforms\android-8\sources\ContactsProvider\src\com\android\providers\contacts\ContactsProvider2.java
呼叫记录内容提供者
D:\android-sdk-windows\platforms\android-8\sources\ContactsProvider\src\com\android\providers\contacts\CallLogProvider.java
短信内容提供者
D:\android-sdk-windows\platforms\android-8\sources\TelephonyProvider\src\com\android\providers\telephony\SmsProvider.java
彩信内容提供者
D:\android-sdk-windows\platforms\android-8\sources\TelephonyProvider\src\com\android\providers\telephony\MmsProvider.java
电话内容提供者
D:\android-sdk-windows\platforms\android-8\sources\TelephonyProvider\src\com\android\providers\telephony\ TelephonyProvider.java
建立 Content项目
AndroidManifest.xml内加入权限
[java] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nbchina.contact"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
</application>
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.nbchina.contact" android:label="Tests for My App" />
</manifest>
手机内容提供者操作代码如下
[java] view
plaincopyprint?
ContactTest.java
package com.nbchina.contact;
public class ContactTest extends AndroidTestCase {
private static final String TAG = "ContactTest";
public void testGetAllContact() throws Throwable{
//content://com.android.contacts/contacts
Uri uri = ContactsContract.Contacts.CONTENT_URI;
//访问内容提供者
ContentResolver contentResolver = this.getContext().getContentResolver();
//查询所有数据
//contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
Cursor cursor = contentResolver.query(uri, null, null, null, null);
while(cursor.moveToNext()){
StringBuilder sb = new StringBuilder();
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
sb.append("contactId=").append(contactId).append(",name=").append(name);
Cursor phones = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,
null, null);
while(phones.moveToNext()){
String phoneNumber = phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
sb.append(",phone=").append(phoneNumber);
}
phones.close();
Cursor emails = contentResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId,
null, null);
while (emails.moveToNext()) {
String emailAddress = emails.getString(emails.getColumnIndex(
ContactsContract.CommonDataKinds.Email.DATA));
sb.append(",emailAddress=").append(emailAddress);
}
emails.close();
Log.i(TAG, sb.toString());
}
cursor.close();
}
/**
* 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
* 这时后面插入data表的依据,只有执行空值插入,才能使插入的联系人在通讯录里面可见
*/
public void testInsert() {
ContentValues values = new ContentValues();
//首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri = this.getContext().getContentResolver().insert(RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
//往data表入姓名数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);//内容类型
values.put(StructuredName.GIVEN_NAME, "李天山");
this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
//往data表入电话数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
values.put(Phone.NUMBER, "13921009789");
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
//往data表入Email数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
values.put(Email.DATA, "liming@itcast.cn");
values.put(Email.TYPE, Email.TYPE_WORK);
this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
}
//批量添加,处于同一个事务中.一般添加最好使用此方式。删除修改等自己研究。
public void testSave() throws Throwable{
//文档位置:reference\android\provider\ContactsContract.RawContacts.html
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
int rawContactInsertIndex = ops.size();
ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, null)
.withValue(RawContacts.ACCOUNT_NAME, null)
.build());
//文档位置:reference\android\provider\ContactsContract.Data.html
ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
.withValue(StructuredName.GIVEN_NAME, "赵薇")
.build());
ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, "13671323809")
.withValue(Phone.TYPE, Phone.TYPE_MOBILE)
.withValue(Phone.LABEL, "手机号")
.build());
ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.DATA, "liming@nbchina.cn")
.withValue(Email.TYPE, Email.TYPE_WORK)
.build());
ContentProviderResult[] results = this.getContext().getContentResolver()
.applyBatch(ContactsContract.AUTHORITY, ops);
for(ContentProviderResult result : results){
Log.i(TAG, result.uri.toString());
}
}
}
相关文章推荐
- android学习笔记一(基础部分)
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- Java动画编程基础第二部分
- C#中使用异步Socket编程实现TCP网络服务的CS的通讯构架(一)----基础类库部分
- C#基础部分之语法和基础知识
- 大型多玩家在线游戏,第 1 部分: 一种基于性能的基础结构规模评估方法
- C#基础部分之语法和基础知识
- 读书笔记 《软件测试》 第二部分 基础测试-第5章 戴上眼罩测试
- 网站制作初步 第一部分 基础知识
- C#基础部分之语法和基础知识
- Java动画编程基础第四部分
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- 《软件测试管理》第14章 软件测试常见问题——(一)基础知识部分
- 2007年教育学专业基础综合考试大纲(重要部分) ——下载地址
- 《C++捷径教程》读书笔记--Chapter 7--函数,第一部分:基础知识(第一部分)
- Java动画编程基础第三部分
- C++ FAQS 翻译3 (第一部分 预备知识 第二章 C++语法和语义基础)
- JDBC编程基础 第二部分
- 搜索引擎优化基础,第 1 部分: 提高站点在搜索引擎中的排名
- 搜索引擎优化基础,第 1 部分: 提高站点在搜索引擎中的排名