一.概述:
ContentProvider汉语为内容提供器,又叫数据内容提供器,差不多一个意思吧,因为他是android应用程序间非常通用的共享数据的一种方式。是推荐的应用程序间共享数据的一种方式。android中许多系统软件和应用软件都使用该方式实现数据共享,比如电话本,相片,音乐,短彩信等,刚开始研究android的人也许会感觉奇怪,直接读取数据库也许会更简单方便,搞一个内容提供器在数据和应用之间,装得高深莫测,故弄玄虚,其实这正体现了面象对象的优越性。通过ContentProvider对各类数据进行包装,包括数据库,文件,XML数据,从而提供统一的对外接口,这在某种程度上提高了软件的可维护性。在android中大量的使用了相类似的设计,比如数据适配器,为各种控件提供统一数据内容,数据观察者,为监测数据变化提供统一接口,这些设计,都是对设计模式的灵活应用,提升了应用程序的健壮性和可维护性。
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,ContentResolver提供了很多final或者static的方法,这些提供的方法和ContentProvider中需要实现的方法是对应的,要详细了解或者进一一点了解,可以查看包android.content.ContentResolver类的内容。
在Activity或者service中都能通过getContentResolver()可以得到当前应用的ContentResolver实例。然后通过ContentResolver接口访问ContentProvider提供的数据,从而对数据进行查询,修改,删除,添加等操作。要实现的一个内容提供器给其他应用使用,我们需要重载类ContentProvider类,并在该类中实现以下方法,这些方法经过ContentProvider类的转化,能够为第三方应用提供统一的对外服务接口:
查询数据 Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) 通过Uri进行查询,返回一个Cursor。 修改数据 int update(Uri uri, ContentValues values, String where, String[] selectionArgs) 更新Uri指定位置的数据,返回所影响的行数 添加数据 Uri insert(Uri url, ContentValues values) 将一组数据插入到Uri 指定的地方,返回新inserted的URI 删除数据int
delete
(Uri url, String where, String[] selectionArgs)
删除指定
Uri
并且符合一定条件的数据,返回所影响的行数
二。创建ContentProvider: 要创建我们自定义的ContentProvider,我们需要遵循以下几步:
1.创建一个继承了ContentProvider父类的类
2.定义一个名为CONTENT_URI,并且是publicstatic final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称,如: publicstatic final Uri CONTENT_URI = Uri.parse(“content://com.google.android.MyContentProvider”); 3.创建你的数据存储系统。大多数ContentProvider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。 4.定义你要返回给客户端的数据列名。如果你正在使用Android数据库,则数据列的使用方式就和你以往所熟悉的其他数据库一样。但是,你必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。 5.如果你要存储字节型数据,比如位图文件等,那保存该数据的数据列其实是一个表示实际保存文件的URI字符串,客户端通过它来读取对应的文件数据,处理这种数据类型的ContentProvider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源,如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。 6.声明publicstatic String型的变量,用于指定要从游标处返回的数据列。 7.查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(),update()以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。 8.在AndroidMenifest.xml中使用<provider>标签来设置ContentProvider。 9.如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。MIME类型有两种形式:一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式:
vnd.android.cursor.item/vnd.yourcompanyname.contenttype(单个记录的MIME类型) 比如,一个请求列车信息的URI如content://com.example.transportationprovider/trains/122可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。
vnd.android.cursor.dir/vnd.yourcompanyname.contenttype(多个记录的MIME类型) 比如,一个请求所有列车信息的URI如content://com.example.transportationprovider/trains可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME类型。
下列代码将创建一个ContentProvider,它仅仅是存储用户名称并显示所有的用户名称(使用SQLLite数据库存储这些数据):
package com.wissen.testApp; public class MyUsers { public static final String AUTHORITY = “com.wissen.MyContentProvider”; // BaseColumn类中已经包含了 _id字段 public static final class User implements BaseColumns { public static final Uri CONTENT_URI = Uri.parse(”content://com.wissen.MyContentProvider”); // 表数据列 public static final String USER_NAME = “USER_NAME”; } }
上面的类中定义了Content Provider的CONTENT_URI,以及数据列。下面我们将定义基于上面的类来定义实际的Content Provider类:
三。对第三方或者系统
public class MyContentProvider extends ContentProvider { private SQLiteDatabase sqlDB; private DatabaseHelper dbHelper; private static final String DATABASE_NAME = “Users.db”; private static final int DATABASE_VERSION = 1; private static final String TABLE_NAME = “User”; private static final String TAG = “MyContentProvider”; private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { //创建用于存储数据的表 db.execSQL(”Create table ” + TABLE_NAME + “( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);”); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(”DROP TABLE IF EXISTS ” + TABLE_NAME); onCreate(db); } } @Override public int delete(Uri uri, String s, String[] as) { return 0; } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues contentvalues) { sqlDB = dbHelper.getWritableDatabase(); long rowId = sqlDB.insert(TABLE_NAME, “”, contentvalues); if (rowId > 0) { Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build(); getContext().getContentResolver().notifyChange(rowUri, null); return rowUri; } throw new SQLException(”Failed to insert row into ” + uri); } @Override public boolean onCreate() { dbHelper = new DatabaseHelper(getContext()); return (dbHelper == null) ? false : true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteDatabase db = dbHelper.getReadableDatabase(); qb.setTables(TABLE_NAME); Cursor c = qb.query(db, projection, selection, null, null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { return 0; } } 一个名为MyContentProvider的Content Provider创建完成了,它用于从Sqlite数据库中添加和读取记录。 Content Provider的入口需要在AndroidManifest.xml中配置: <provider android:name=”MyContentProvider” android:authorities=”com.wissen.MyContentProvider” /> 之后,让我们来使用这个定义好的Content Provider,下面先向数据库中添加一条用户数据,然后显示数据库中所有的用户数据。: package com.wissen.testApp; public class MyContentDemo extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); insertRecord(”MyUser”); displayRecords(); } private void insertRecord(String userName) { ContentValues values = new ContentValues(); values.put(MyUsers.User.USER_NAME, userName); getContentResolver().insert(MyUsers.User.CONTENT_URI, values); } private void displayRecords() { String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME }; Uri myUri = MyUsers.User.CONTENT_URI; Cursor cur = managedQuery(myUri, columns,null, null, null ); if (cur.moveToFirst()) { String id = null; String userName = null; do { id = cur.getString(cur.getColumnIndex(MyUsers.User._ID)); userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME)); Toast.makeText(this, id + ” ” + userName, Toast.LENGTH_LONG).show(); } while (cur.moveToNext()); } } }
ContentProvider
数据库进行操作
ContentProvider
操作数据库不再使用标准的SQL
语言。select,add, delete, modify
等操作被转化为一种特殊的URI
来进行,
这种URI
由3
个部分组成,“content://”,
代表数据的路径,和一个可选的标识数据的ID
。 这种查询字符串格式很常见,
我会在随后的URI介绍中对其说明,android
调用第三方应用也会使用URI
。Android
在android.provider
包下提供一系列的帮助类,
里面包含了很多以类变量形式给出的查询字符串,比如比较老的联系人操作集中在
android.provider.Contacts
类中,
而新版本可以在android.provider
.
ContactsContract
类中得到。
查询记录:
Cursor cur = managedQuery(person, null, null, null);
这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:
下面代码演示一个如何依次读取联系人信息表中的指定数据列name和number。package com.wissen.testApp; public class ContentProviderDemo extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); displayRecords(); } private void displayRecords() { //该数组中包含了所有要返回的字段 String columns[] = new String[] { People.NAME, People.NUMBER }; Uri mContacts = People.CONTENT_URI; Cursor cur = managedQuery( mContacts, columns, // 要返回的数据字段 null, // WHERE子句 null, // WHERE 子句的参数 null // Order-by子句 ); if (cur.moveToFirst()) { String name = null; String phoneNo = null; do { // 获取字段的值 name = cur.getString(cur.getColumnIndex(People.NAME)); phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER)); Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show(); } while (cur.moveToNext()); } } }
修改记录:
我们可以使用ContentResolver.update()方法来修改数据,我们来写一个修改数据的方法:
private void updateRecord(int recNo, String name) { Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo); ContentValues values = new ContentValues(); values.put(People.NAME, name); getContentResolver().update(uri, values, null, null); }
现在你可以调用上面的方法来更新指定记录:updateRecord(10, ”XYZ”); //更改第10条记录的name字段值为“XYZ”
添加记录:
要增加记录,我们可以调用ContentResolver.insert()方法,该方法接受一个要增加的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。
上面的例子中我们都是基于联系人信息簿这个标准的Content Provider,现在我们继续来创建一个insertRecord() 方法以对联系人信息簿中进行数据的添加:private void insertRecords(String name, String phoneNo) {
ContentValues values =
new
ContentValues();
values.put(People.NAME, name);
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
Log.d(”ANDROID”, uri.toString());
Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.NUMBER, phoneNo);
getContentResolver().insert(numberUri, values);
}
这样我们就可以调用insertRecords(name, phoneNo)的方式来向联系人信息簿中添加联系人姓名和电话号码。
删除记录:
Content Provider中的getContextResolver.delete()方法可以用来删除记录,下面的记录用来删除设备上所有的联系人信息:
private void deleteRecords() { Uri uri = People.CONTENT_URI; getContentResolver().delete(uri, null, null); }你也可以指定WHERE条件语句来删除特定的记录:getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null);
这将会删除name为‘XYZ XYZ’的记录。
四。扩展阅读URI通用资源标志符uri是UniversalResource Identifier的英文缩写,
Uri代表要操作的数据和资源的方法路径,他不是android独特的内容,早在WEB时代已经存在,URI作为URL的子集,和URL一起构成了丰富多彩的 多媒体WEB时代。如果没有URI和URL,我们的网络将是一个纯文本时代,他使得网页应用能够找到图片,声音,动画能多媒体文件。如: http://www.baidu.com/img/bdlogo.gif 再比如: mailto:[email protected]
其一般由三部分组成:访问资源的命名机制。
存放资源的主机名。
资源自身的名称,由路径表示。
Android的Uri由以下三部分组成:"content://"、数据的路径、标示ID(可选)Android上可用的每种资源-图像、视频片段等都可以用Uri来表示。Android上可用的每种资源-图像、视频片段等都可以用Uri来表示。
举些例子,如:
所有联系人的Uri:content://contacts/people
某个联系人的Uri:content://contacts/people/5
所有图片Uri:content://media/external
某个图片的Uri:content://media/external/images/media/4
我们很经常需要解析Uri,并从Uri中获取数据。
Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris。
虽然这两类不是非常重要,但是掌握它们的使用,会便于我们的开发工作。
下面就一起看一下这两个类的作用。
2.UriMatcher
UriMatcher 类主要用于匹配Uri.
使用方法如下。
首先第一步,初始化:
view plain copy to clipboard print ?
- UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
第二步注册需要的Uri:
view plain copy to clipboard print ?
- matcher.addURI("com.yfz.Lesson", "people", PEOPLE);
- matcher.addURI("com.yfz.Lesson", "person/#", PEOPLE_ID);
第三部,与已经注册的Uri进行匹配:
Uri uri = Uri.parse("content://" + "com.yfz.Lesson" + "/people"); int match = matcher.match(uri); switch (match) { case PEOPLE: return "vnd.android.cursor.dir/people"; case PEOPLE_ID: return "vnd.android.cursor.item/people"; default: return null; }match方法匹配后会返回一个匹配码Code,即在使用注册方法addURI时传入的第三个参数。
上述方法会返回"vnd.android.cursor.dir/person".
总结:
--常量 UriMatcher.NO_MATCH表示不匹配任何路径的返回码
--# 号为通配符
--* 号为任意字符
另外说一下,官方SDK说明中关于Uri的注册是这样写的:
private static final UriMatcher sURIMatcher = new UriMatcher(); static { sURIMatcher.addURI("contacts", "/people", PEOPLE); sURIMatcher.addURI("contacts", "/people/#", PEOPLE_ID); sURIMatcher.addURI("contacts", "/people/#/phones", PEOPLE_PHONES); sURIMatcher.addURI("contacts", "/people/#/phones/#", PEOPLE_PHONES_ID); sURIMatcher.addURI("contacts", "/people/#/contact_methods", PEOPLE_CONTACTMETHODS); sURIMatcher.addURI("contacts", "/people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID); sURIMatcher.addURI("contacts", "/deleted_people", DELETED_PEOPLE); sURIMatcher.addURI("contacts", "/phones", PHONES); sURIMatcher.addURI("contacts", "/phones/filter/*", PHONES_FILTER); sURIMatcher.addURI("contacts", "/phones/#", PHONES_ID); sURIMatcher.addURI("contacts", "/contact_methods", CONTACTMETHODS); sURIMatcher.addURI("contacts", "/contact_methods/#", CONTACTMETHODS_ID); sURIMatcher.addURI("call_log", "/calls", CALLS); sURIMatcher.addURI("call_log", "/calls/filter/*", CALLS_FILTER); sURIMatcher.addURI("call_log", "/calls/#", CALLS_ID); }这个说明估计已经是Google官方没有更新,首先是初始化方法,没有传参,那么现在初始化时,实际是必须传参的。可以看一下Android2.2的源码,无参数的构造方法已经是private的了。
另外就是addURI这个方法,第二个参数开始时不需要"/", 否则是无法匹配成功的。
3.ContentUris
ContentUris 类用于获取Uri路径后面的ID部分
1)为路径加上ID: withAppendedId(uri, id)
比如有这样一个Uri
view plain copy to clipboard print ?
- Uri uri = Uri.parse("content://com.yfz.Lesson/people")
附上实验的代码通过withAppendedId方法,为该Uri加上ID
view plain copy to clipboard print ?
- Uri resultUri = ContentUris.withAppendedId(uri, 10);
最后resultUri为: content://com.yfz.Lesson/people/10
2)从路径中获取ID: parseId(uri)
view plain copy to clipboard print ?
- Uri uri = Uri.parse("content://com.yfz.Lesson/people/10")
- long personid = ContentUris.parseId(uri);
最后personid 为 :10
package com.yfz; import com.yfz.log.Logger; import android.app.Activity; import android.content.ContentUris; import android.content.UriMatcher; import android.net.Uri; import android.os.Bundle; public class Lesson_14 extends Activity { private static final String AUTHORITY = "com.yfz.Lesson"; private static final int PEOPLE = 1; private static final int PEOPLE_ID = 2; //NO_MATCH表示不匹配任何路径的返回码 private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { sURIMatcher.addURI(AUTHORITY, "people", PEOPLE); //这里的#代表匹配任意数字,另外还可以用*来匹配任意文本 sURIMatcher.addURI(AUTHORITY, "people/#", PEOPLE_ID); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Logger.d("------ Start Activity !!! ------"); Uri uri1 = Uri.parse("content://" + AUTHORITY + "/people"); Logger.e("Uri:" + uri1); Logger.d("Match 1" + getType(uri1)); Uri uri2 = Uri.parse("content://" + AUTHORITY + "/people" + "/2"); Logger.e("Uri:" + uri2); Logger.d("Match 2" + getType(uri2)); //拼接Uri Uri cUri = ContentUris.withAppendedId(uri1, 15); Logger.e("Uri:" + cUri); //获取ID long id = ContentUris.parseId(cUri); Logger.d("Uri ID: " + id); } private String getType(Uri uri) { int match = sURIMatcher.match(uri); switch (match) { case PEOPLE: return "vnd.android.cursor.dir/person"; case PEOPLE_ID: return "vnd.android.cursor.item/person"; default: return null; } } }五。扩展应用,通过ContextProvider监控数据和文件系统变化 不多解释,几个例子,上代码: 监控短信,彩信,电话数据库,当有新短信彩信未接电话时,获取新短信,彩信,电话数目并显示public class MainActivity extends Activity { private Mylayout ml = null; String mcallstr, msmsstr; int mcallcount, msmscount; final static int MSG_NEW_SMS_COUNT = 2; final static int MSG_NEW_CALL_COUNT = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); mcallstr = "未接电话:"; msmsstr = "未读短信:"; mcallcount = findMissedCallCount(); msmscount = findNewSmsCount() + findNewMmsCount(); // setContentView(R.layout.activity_main); ml = new Mylayout(this); ml.setBackgroundColor(Color.BLACK); ml.setLayoutParams(new RelativeLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); setContentView(ml); Intent mService = new Intent(MainActivity.this, MyhallServer.class);// �������� mService.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startService(mService); ((myapp) this.getApplication()).setact(MainActivity.this); getApplicationContext().getContentResolver().registerContentObserver( Uri.parse("content://mms-sms/"), true, new newMmsContentObserver(getApplicationContext(), myHandler)); getApplicationContext().getContentResolver().registerContentObserver( android.provider.CallLog.Calls.CONTENT_URI, /* * content://call_log * /calls */ true, new MissedCallContentObserver(getApplicationContext(), myHandler)); } @Override protected void onDestroy() { // TODO Auto-generated method stub getContentResolver().unregisterContentObserver( new newMmsContentObserver(this, myHandler)); getContentResolver().unregisterContentObserver( new MissedCallContentObserver(this, myHandler)); super.onDestroy(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } // 通过HANDLER修改电话短信条数 private Handler myHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MSG_NEW_SMS_COUNT: int sms = (Integer) msg.obj; Log.i("@@@@@", "msg.obj = " + msg.obj.toString()); Log.i("@@@@@", "sms = " + sms); msmscount = sms; break; case MSG_NEW_CALL_COUNT: int call = (Integer) msg.obj; Log.i("@@@@@", "msg.obj = " + msg.obj.toString()); Log.i("@@@@@", "call = " + call); mcallcount = call; break; default: break; } } }; // 监控短信,彩信数目变化 private int findNewSmsCount() { Cursor csr = null; int newSmsCount = 0; try { csr = getApplicationContext().getContentResolver().query( Uri.parse("content://sms"), null, "type = 1 and read = 0", null, null); newSmsCount = csr.getCount(); // 未读短信数目 } catch (Exception e) { e.printStackTrace(); } finally { if (csr != null) csr.close(); } return newSmsCount; } // 监控短信,短信数目变化 private int findNewMmsCount() { Cursor csr = null; int newMmsCount = 0; try { csr = getApplicationContext().getContentResolver().query( Uri.parse("content://mms/inbox"), null, "read = 0", null, null); newMmsCount = csr.getCount();// 未读彩信数目 } catch (Exception e) { e.printStackTrace(); } finally { if (csr != null) csr.close(); } return newMmsCount; } // 监控电话数目 private int findMissedCallCount() { int missedCallCount = 0; /* * Cursor csr = getContentResolver().query(Calls.CONTENT_URI, new * String[] { Calls.NUMBER, Calls.TYPE, Calls.NEW }, null, null, * Calls.DEFAULT_SORT_ORDER); * * if (csr != null) { if (csr.moveToFirst()) { int type = * csr.getInt(csr.getColumnIndex(Calls.TYPE)); switch (type) { case * Calls.MISSED_TYPE: if (csr.getInt(csr.getColumnIndex(Calls.NEW)) == * 1) { missedCallCount = csr.getCount(); } * * break; case Calls.INCOMING_TYPE: break; case Calls.OUTGOING_TYPE: * break; default: break; } } // release resource csr.close(); } */ StringBuilder where = new StringBuilder("type = "); where.append(Calls.MISSED_TYPE); where.append(" AND new = 1"); // start the query Cursor cur = null; try { cur = this.getContentResolver().query(Calls.CONTENT_URI, new String[] { Calls._ID }, where.toString(), null, Calls.DEFAULT_SORT_ORDER); if (cur != null) { missedCallCount = cur.getCount(); } } catch (Exception ex) { } finally { if (cur != null) { cur.close(); } } return missedCallCount; } // 监控信息数据库 public class newMmsContentObserver extends ContentObserver { private Context ctx; private Handler m_handler; int newMmsCount = 0; int newSmsCount = 0; public newMmsContentObserver(Context context, Handler handler) { super(handler); ctx = context; m_handler = handler; } @Override public void onChange(boolean selfChange) { newMmsCount = findNewSmsCount(); newSmsCount = findNewMmsCount(); Log.i("@@@@@", "newSmsCount = " + (newSmsCount + newMmsCount)); m_handler.obtainMessage(MSG_NEW_SMS_COUNT, (newMmsCount + newSmsCount)).sendToTarget(); } } // 监控电话数据库 public class MissedCallContentObserver extends ContentObserver { private Context ctx; int missedCallCount = 0; private Handler m_handler; private static final String TAG = "MissedCallContentObserver"; public MissedCallContentObserver(Context context, Handler handler) { super(handler); ctx = context; m_handler = handler; } @Override public void onChange(boolean selfChange) { missedCallCount = findMissedCallCount(); Log.i("@@@@@", "missedCallCount = " + missedCallCount); m_handler.obtainMessage(MSG_NEW_CALL_COUNT, missedCallCount) .sendToTarget(); } } public class Mylayout extends View { Context mContext = null; Bitmap tmpBitmap = null; boolean IMG_ENABLE = false; int w, h; Bitmap[] BmWeek = null; Bitmap[] BmTime = null; Bitmap[] BmDate = null; public Mylayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub mContext = context; WindowManager wManager = (WindowManager) mContext .getSystemService(Context.WINDOW_SERVICE); Display display = wManager.getDefaultDisplay(); w = display.getWidth(); h = display.getHeight(); tmpBitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); } public Mylayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub mContext = context; WindowManager wManager = (WindowManager) mContext .getSystemService(Context.WINDOW_SERVICE); Display display = wManager.getDefaultDisplay(); w = display.getWidth(); h = display.getHeight(); tmpBitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); } public Mylayout(Context context) { super(context); // TODO Auto-generated constructor stub mContext = context; WindowManager wManager = (WindowManager) mContext .getSystemService(Context.WINDOW_SERVICE); Display display = wManager.getDefaultDisplay(); w = display.getWidth(); h = display.getHeight(); try { tmpBitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); } catch (OutOfMemoryError e) { e.printStackTrace(); tmpBitmap.recycle(); System.gc(); System.runFinalization(); tmpBitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub super.onLayout(changed, l, t, r, b); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub5 super.onDraw(canvas); Canvas tmpCanvas = new Canvas(); tmpCanvas.setBitmap(tmpBitmap); Paint p1 = new Paint(); p1.setColor(Color.BLACK); p1.setStyle(Style.FILL); tmpCanvas.drawRect(0, 0, w, h, p1); // 设置字体 if (!IMG_ENABLE) { Paint p = new Paint(); p.setColor(Color.WHITE); p.setAntiAlias(true); p.setStrokeWidth(1); p.setStyle(Style.FILL); p.setTextSize(26); Typeface font = Typeface.create("黑体", Typeface.BOLD); p.setTypeface(font); // 显示时间 p.setTextSize(80); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String time = sdf.format(new Date()); time = time.substring(0, time.length() - 3); int fw = (int) p.measureText(time); FontMetrics fm = p.getFontMetrics(); int fh = (int) Math.ceil(fm.descent - fm.top) + 2; tmpCanvas.drawText(time, (w - fw) / 2, 200, p); p.measureText(time); // 显示星期 sdf = new SimpleDateFormat("EEEE"); String week = sdf.format(new Date()); // tmpCanvas.drawText(week, 100, 200, p); // 显示日期 p.setTextSize(30); sdf = new SimpleDateFormat("yyyy-MM-dd"); String date = sdf.format(new Date()); date = date.substring(5, date.length()); date = date + "," + week; fw = (int) p.measureText(date); tmpCanvas.drawText(date, (w - fw) / 2, 200 + 50, p); // 显示未接电话 // fw = (int) p.measureText(time); // FontMetrics fm = p.getFontMetrics(); // fh = (int)Math.ceil(fm.descent - fm.top) + 2; tmpCanvas.drawText(mcallstr + String.valueOf(mcallcount), (w - fw) / 2, 300, p); // 显示未读短信 tmpCanvas.drawText(msmsstr + String.valueOf(msmscount), (w - fw) / 2, 350, p); } else { drawDate(tmpCanvas, 100, 500); drawTime(tmpCanvas, 100, 400); drawWeek(tmpCanvas, 300, 500); } canvas.drawBitmap(tmpBitmap, 0, 0, null); this.invalidate(); } public void drawDate(Canvas canvas, int x, int y) { Bitmap bm = null; int[] imgIds = { R.drawable.r011, R.drawable.r111, R.drawable.r211, R.drawable.r311, R.drawable.r411, R.drawable.r511, R.drawable.r611, R.drawable.r711, R.drawable.r811, R.drawable.r911, R.drawable.hy11, R.drawable.h711, }; int pos = x; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String date = sdf.format(new Date()); date = date.substring(5, date.length()); // canvas.drawText(date, x, y, null); for (int i = 0; i < date.length(); i++) { if (date.charAt(i) == '-') { bm = BitmapFactory.decodeResource(mContext.getResources(), imgIds[10]); } else { bm = BitmapFactory.decodeResource(mContext.getResources(), imgIds[date.charAt(i) - '0']); } canvas.drawBitmap(bm, pos, y, null); pos += bm.getWidth(); } bm = BitmapFactory.decodeResource(mContext.getResources(), imgIds[11]); canvas.drawBitmap(bm, pos, y, null); } public void drawTime(Canvas canvas, int x, int y) { Bitmap bm = null; /* * int[] imgIds = {R.drawable.time_011, R.drawable.time_111, * R.drawable.time_211, R.drawable.time_311, R.drawable.time_411, * R.drawable.time_511, R.drawable.time_611, R.drawable.time_711, * R.drawable.time_811, R.drawable.time_911, R.drawable.maohao11 }; */ int[] imgIds = { R.drawable.time_0, R.drawable.time_1, R.drawable.time_2, R.drawable.time_3, R.drawable.time_4, R.drawable.time_5, R.drawable.time_6, R.drawable.time_7, R.drawable.time_8, R.drawable.time_9, R.drawable.time_dot }; int pos = x; SimpleDateFormat sdf = new SimpleDateFormat("HH-mm-ss"); String time = sdf.format(new Date()); time = time.substring(0, time.length() - 3); // canvas.drawText(time, x, y, null); for (int i = 0; i < time.length(); i++) { if (time.charAt(i) == '-') { bm = BitmapFactory.decodeResource(mContext.getResources(), imgIds[10]); } else { bm = BitmapFactory.decodeResource(mContext.getResources(), imgIds[time.charAt(i) - '0']); } canvas.drawBitmap(bm, pos, y, null); pos += bm.getWidth(); } } public void drawWeek(Canvas canvas, int x, int y) { Bitmap bm = null; String[] weeks = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日" }; int[] imgIds = { R.drawable.time_011, R.drawable.h211, R.drawable.h311, R.drawable.h411, R.drawable.h511, R.drawable.h611, R.drawable.h711, R.drawable.hxq11 }; int pos = x; SimpleDateFormat sdf = new SimpleDateFormat("EEEE"); String week = sdf.format(new Date()); // canvas.drawText(week, x, y, null); bm = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.hxq11); canvas.drawBitmap(bm, pos, y, null); pos += bm.getWidth(); for (int i = 0; i < weeks.length; i++) { if (weeks[i].equals(week)) { bm = BitmapFactory.decodeResource(mContext.getResources(), imgIds[i]); canvas.drawBitmap(bm, pos, y, null); break; } } } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; case MotionEvent.ACTION_MOVE: break; } return true; // super.onTouchEvent(event); } } }android 监听SD卡文件变化
(1)创建目录监听器: import android.os.FileObserver; import android.util.Log; /** * SD卡中的目录创建监听器。 * * @author mayingcai */ public class SDCardListener extends FileObserver { public SDCardListener(String path) { /* * 这种构造方法是默认监听所有事件的,如果使用 super(String,int)这种构造方法, * 则int参数是要监听的事件类型. */ super(path); } @Override public void onEvent(int event, String path) { switch(event) { case FileObserver.ALL_EVENTS: Log.d("all", "path:"+ path); break; case FileObserver.CREATE: Log.d("Create", "path:"+ path); break; } } } (2)给目录设置监听器: SDCardListener listener = new SDCardListener("目录"); //开始监听 listener.startWatching(); /* * 在这里做一些操作,比如创建目录什么的 */ //停止监听 listener.stopWatching();
参考:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/0821/367.html
,
http://blog.sina.com.cn/s/blog_821e2bb10100spxv.html
http://www.android-study.com/jichuzhishi/338.html
http://blog.csdn.net/sqk1988/article/details/7756507