漫谈anroid软件设计中的contentprovider及其应用

.概述:


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 指定的地方,返回新insertedURI
删除数据
int delete(Uri url, String where, String[] selectionArgs) 删除指定Uri并且符合一定条件的数据,返回所影响的行数

二。创建ContentProvider:   要创建我们自定义的ContentProvider,我们需要遵循以下几步:

1.创建一个继承了ContentProvider父类的类

2.定义一个名为CONTENT_URI,并且是publicstatic finalUri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称,如: 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类型) 比如,一个请求列车信息的URIcontent://com.example.transportationprovider/trains/122可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。

vnd.android.cursor.dir/vnd.yourcompanyname.contenttype(多个记录的MIME类型) 比如,一个请求所有列车信息的URIcontent://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来进行,

这种URI3个部分组成,“content://”,代表数据的路径,和一个可选的标识数据的ID。 这种查询字符串格式很常见,

我会在随后的URI介绍中对其说明,android调用第三方应用也会使用URIAndroidandroid.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:daming@163.com

其一般由三部分组成:

访问资源的命名机制。 

存放资源的主机名。 

资源自身的名称,由路径表示。 

Android上可用的每种资源-图像、视频片段等都可以用Uri来表示。
AndroidUri由以下三部分组成:"content://"、数据的路径、标示ID(可选)

Android上可用的每种资源-图像、视频片段等都可以用Uri来表示。

举些例子,如: 

所有联系人的Uricontent://contacts/people

某个联系人的Uri:content://contacts/people/5

所有图片Uri:content://media/external

某个图片的Uricontent://media/external/images/media/4

我们很经常需要解析Uri,并从Uri中获取数据。

Android系统提供了两个用于操作Uri的工具类,分别为UriMatcherContentUris

虽然这两类不是非常重要,但是掌握它们的使用,会便于我们的开发工作。

下面就一起看一下这两个类的作用。



2.UriMatcher

UriMatcher 类主要用于匹配Uri.

 

使用方法如下。

首先第一步,初始化:

  1. UriMatcher matcher new UriMatcher(UriMatcher.NO_MATCH);  
 

第二步注册需要的Uri:

  1. matcher.addURI("com.yfz.Lesson", "people", PEOPLE);  
  2. 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

  1. Uri uri Uri.parse("content://com.yfz.Lesson/people")  
 

通过withAppendedId方法,为该Uri加上ID

  1. Uri resultUri ContentUris.withAppendedId(uri, 10);  
 

最后resultUri为: content://com.yfz.Lesson/people/10

 

2)从路径中获取ID: parseId(uri)

  1. Uri uri Uri.parse("content://com.yfz.Lesson/people/10")  
  2. 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




 




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值