Activitypublic class MainActivity extends ListActivity { PRivate TextView tv_info; private SMSContentObserver smsContentObserver; private CallLogObserver callLogObserver; private PhoneStateReceiver myReceiver; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { public void handleMessage(Message msg) { String msgBody = (String) msg.obj; tv_info.setText(msg.obj + ":" + msgBody); } }; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String[] array = { "注冊短信數據庫變化的觀察者", "收件箱數據庫……", "刪除新來電的通話記錄", "監聽新來電通話記錄的詳細信息", "取消注冊Observer",// "注冊電話狀態改變的廣播,當有來電時立即掛斷電話", "取消注冊廣播", }; for (int i = 0; i < array.length; i++) { array[i] = i + "、" + array[i]; } ListAdapter mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))); tv_info = new TextView(this);// 將內容顯示在TextView中 tv_info.setTextColor(Color.BLUE); tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); tv_info.setPadding(20, 10, 20, 10); getListView().addFooterView(tv_info); setListAdapter(mAdapter); myReceiver = new PhoneStateReceiver(); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { switch (position) { case 0: smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_WHAT); getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, smsContentObserver); // boolean notifyForDescendents(后裔):若為true,則監視所有以指定的Uri開頭的Uri;若為false,則只精確的監視指定的URI break; case 1: smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_INBOX_WHAT); getContentResolver().registerContentObserver(Uri.parse("content://sms/inbox"), true, smsContentObserver); break; case 2: callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_DELETE_WHAT); getContentResolver().registerContentObserver(Uri.parse("content://call_log/calls"), true, callLogObserver); break; case 3: callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_QUERY_WHAT); getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, callLogObserver);//等價于【Uri.parse("content://call_log/calls")】 break; case 4: if (smsContentObserver != null) getContentResolver().unregisterContentObserver(smsContentObserver); if (callLogObserver != null) getContentResolver().unregisterContentObserver(callLogObserver); break; case 5: registerReceiver(myReceiver, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED)); break; case 6: try { unregisterReceiver(myReceiver); } catch (Exception e) { } break; } } /** * 利用aidl及反射自動掛斷來電。注意,不能通過ContentResolver監聽通話記錄數據庫來掛斷電話,估計是因為電話已接通,不能再掛掉了 */ public void endCall() { // IBinder iBinder = ServiceManager.getService(TELEPHONY_SERVICE);//希望調用的方法,但此方法被系統隱藏了 try { Class<?> clazz = Class.forName("android.os.ServiceManager");//利用反射拿到其字節碼文件 Method method = clazz.getDeclaredMethod("getService", String.class);//獲取ServiceManager類的getService(String s)方法 IBinder ibinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);//參數為:調用此方法的對象,此方法的參數 ITelephony telephony = ITelephony.Stub.asInterface(ibinder);//把上面getService(String s)得到的IBinder對象轉化成【ITelephony】對象 boolean isSuccess = telephony.endCall();//調用ITelephony掛斷電話的方法 mHandler.sendMessage(Message.obtain(mHandler, 5, "是否成功掛斷電話:" + isSuccess)); } catch (Exception e) { mHandler.sendMessage(Message.obtain(mHandler, 5, "異常啦" + e.getMessage())); e.printStackTrace(); } } /**監聽來電狀態的廣播*/ class PhoneStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent != null) { if (TelephonyManager.EXTRA_STATE_RINGING.equalsIgnoreCase(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {//來電狀態 endCall(); } } } }}
短信數據庫的ContentObserver/**監聽或獲取手機短信內容的兩種方式 * 方式一:通過注冊廣播監聽短信 * 這種方式只對新收到的短消息有效,并且系統的這個廣播是有序廣播,現在在一些定制的系統或是有安全軟件的情況下,往往短消息都被截取到,并被干掉。 * 方法二:通過監聽短信數據庫的變化獲取短信 * 這種方式可以獲取手機上所有的短信,包括已讀未讀的短信,并且不受其它程序干擾 * ContentObserver的使用類似與設計模式中的觀察者模式,ContentObserver是觀察者,被觀察的ContentProvider是被觀察者。 * 當被觀察者ContentProvider的數據發生了增刪改的變化,就會及時的通知給ContentProvider,ContentObsserver做出相應的處理。*/public class SMSContentObserver extends ContentObserver { private Handler mHandler; private Context mContext; /**觀察類型:所有內容或僅收件箱*/ private int observerType; /**觀察所有內容*/ public static final int MSG_SMS_WHAT = 1; /**僅觀察收件箱*/ public static final int MSG_SMS_INBOX_WHAT = 2; public SMSContentObserver(Handler handler, Context context, int observerType) { super(handler); this.mHandler = handler; this.mContext = context; this.observerType = observerType; } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); if (observerType == MSG_SMS_WHAT) { Uri uri = Uri.parse("content://sms"); Cursor cursor = mContext.getContentResolver().query(uri, new String[] { "_id", "address", "body", "type", "date" }, null, null, "date desc"); if (cursor != null) { if (cursor.moveToFirst()) { //最后收到的短信在第一條. This method will return false if the cursor is empty int msgId = cursor.getInt(cursor.getColumnIndex("_id")); String msgAddr = cursor.getString(cursor.getColumnIndex("address")); String msgBody = cursor.getString(cursor.getColumnIndex("body")); String msgType = cursor.getString(cursor.getColumnIndex("type")); String msgDate = cursor.getString(cursor.getColumnIndex("date")); String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date(Long.parseLong(msgDate))); String msgObj = "收件箱/nId:" + msgId + "/n號碼:" + msgAddr + "/n內容:" + msgBody + "/n類型:" + msgType + "/n時間:" + date + "/n"; mHandler.sendMessage(Message.obtain(mHandler, MSG_SMS_WHAT, msgObj)); } cursor.close(); } } else if (observerType == MSG_SMS_INBOX_WHAT) { Uri uri = Uri.parse("content://sms/inbox"); Cursor cursor = mContext.getContentResolver().query(uri, null, "read = 0", null, "date desc");//Passing null will return all columns, which is inefficient. //等價于附加條件 if (cursor.getInt(cursor.getColumnIndex("read")) == 0) //表示短信未讀。這種方式不靠譜啊,建議用上面的方式! if (cursor != null) { StringBuilder sb = new StringBuilder("未讀短信/n"); while (cursor.moveToNext()) { String sendNumber = cursor.getString(cursor.getColumnIndex("address")); String body = cursor.getString(cursor.getColumnIndex("body")); sb.append("號碼:" + sendNumber + "/n內容:" + body + "/n"); } mHandler.obtainMessage(MSG_SMS_INBOX_WHAT, sb.toString()).sendToTarget(); cursor.close(); } } }}利用反射及aidl調用系統隱藏的方法目的: 利用反射及aidl調用系統隱藏的ServiceManager的getService方法,獲取ITelephony后調用其掛電話的方法步驟:1、copy android源代碼【com.android.internal.telephony】包中的【ITelephony.aidl】到自己的項目 為什么要copy這個文件呢?這是因為接聽/掛斷電話的方法在接口ITelephony.java里面,而這個接口是隱藏的(@hide),我們沒權限調用。2、由于ITelephony.aidl關聯了【android.telephony】包下的【NeighboringCellInfo.aidl】,所以也需把它拷貝過來。 上面完成之后,就會在你的gen目錄下自動生成 ITelephony.java接口文件![]()
3、然后我們就可以利用反射機制來取得ITelephony對象。 為什么要用反射呢? 因為 ITelephony是一個系統服務,要通過【ServiceManager】來獲取,但是ServiceManager同樣也是隱藏的。 所以,我們首先要通過反射機制拿到系統隱藏的ServiceManager對象 然后調用ServiceManager的【getService(String)】方法來取得遠程的【ITelephony】對象, 最后調用ITelephony的endCall()方法掛掉電話權限: <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" />
通話記錄數據庫的Observer/** * 撥號記錄的內容觀察者。 */public class CallLogObserver extends ContentObserver { /**觀察到記錄改變后的處理方式*/ private int type; /**刪除最近的一條通話記錄*/ public static final int MSG_CALLLOG_DELETE_WHAT = 3; /**查詢某一個聯系人最近的通話記錄*/ public static final int MSG_CALLLOG_QUERY_WHAT = 4; public static final String NUMBER = "17084143285"; private Handler mHandler; private Uri uri = CallLog.Calls.CONTENT_URI;//等價于【Uri.parse("content://call_log/calls")】 private ContentResolver resolver; public CallLogObserver(Handler handler, Context context, int type) { super(handler); this.mHandler = handler; this.type = type; resolver = context.getContentResolver(); } @Override public void onChange(boolean selfChange) { Cursor cursor; switch (type) { case MSG_CALLLOG_DELETE_WHAT://刪除最近的一條通話記錄 resolver.unregisterContentObserver(this);//注意:增刪改通話記錄后由于數據庫發生變化,所以系統會在修改后再發一條廣播,這時會重新回調onChange方法 //最終導致的結果就是:一次來電后刪除了多條甚至全部通話記錄。為防止這種循環啟發,必須在更改前就取消注冊!事實上,注冊的代碼應該放在廣播接收者中。 cursor = resolver.query(uri, null, null, null, "_id desc limit 1");//按_id倒序排序后取第一個,即:查詢結果按_id從大到小排序,然后取最上面一個(最近的通話記錄) if (cursor != null) { if (cursor.moveToFirst()) { int num = resolver.delete(uri, "_id=?", new String[] { cursor.getInt(cursor.getColumnIndex("_id")) + "" }); mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_DELETE_WHAT, "刪除的記錄數量:" + num)); } cursor.close(); } break; case MSG_CALLLOG_QUERY_WHAT://查詢某一個聯系人最近的通話記錄 String[] projection = new String[] { "_id", CallLog.Calls.TYPE, CallLog.Calls.NUMBER, CallLog.Calls.CACHED_NAME, CallLog.Calls.DATE, CallLog.Calls.DURATION }; String selection = "number=? and (type=1 or type=3)"; String[] selectionArgs = new String[] { NUMBER }; String sortOrder = CallLog.Calls.DEFAULT_SORT_ORDER;//按時間排序【date DESC】 cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder); if (cursor != null) { if (cursor.moveToFirst()) { int _id = cursor.getInt(cursor.getColumnIndex("_id")); int type = cursor.getInt(cursor.getColumnIndex("type"));//通話類型,1 來電 .INCOMING_TYPE;2 已撥 .OUTGOING_;3 未接 .MISSED_ String number = cursor.getString(cursor.getColumnIndex("number"));// 電話號碼 String name = cursor.getString(cursor.getColumnIndex("name"));//聯系人 long date = cursor.getLong(cursor.getColumnIndex("date"));//通話時間,即可以用getString接收,也可以用getLong接收 String formatDate = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault()).format(new Date(date)); int duration = cursor.getInt(cursor.getColumnIndex("duration"));//通話時長,單位:秒 String msgObj = "/nID:" + _id + "/n類型:" + type + "/n號碼:" + number + "/n名稱:" + name + "/n時間:" + formatDate + "/n時長:" + duration; mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_QUERY_WHAT, msgObj)); } cursor.close(); } break; } }}清單文件<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.itheima.ipdail" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.WRITE_CALL_LOG" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/APPTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>本文固定鏈接: http://www.ithtw.com/624.html轉載請注明: leehom 2015年01月05日 于 IT十萬個為什么 發表
ContentObserver——內容觀察者,目的是觀察(捕捉)特定Uri引起的數據庫的變化,繼而做一些相應的處理,它類似于數據庫技術中的觸發器(Trigger),當ContentObserver所觀察的Uri發生變化時,便會觸發它。觸發器分為表觸發器、行觸發器,相應地ContentObserver也分為“表“ContentObserver、“行”ContentObserver,當然這是與它所監聽的Uri MIME Type有關的。熟悉Content Provider(內容提供者)的應該知道,我們可以通過UriMatcher類注冊不同類型的Uri,我們可以通過這些不同的Uri來查詢不同的結果。根據Uri返回的結果,Uri Type可以分為:返回多條數據的Uri、返回單條數據的Uri。
注冊/取消注冊ContentObserver方法,抽象類ContentResolver類中的方法原型如下:public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)功能:為指定的Uri注冊一個ContentObserver派生類實例,當給定的Uri發生改變時,回調該實例對象去處理。參數:uri 需要觀察的Uri(需要在UriMatcher里注冊,否則該Uri也沒有意義了)notifyForDescendents 為false 表示精確匹配,即只匹配該Uri
觀察系統里短消息的數據庫變化的ContentObserver派生類,SMSContentObserver.java
雙擊【CTRL+C】復制 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 import
android.content.Context;
import
android.database.ContentObserver;
import
android.database.Cursor;
import
android.net.Uri;
import
android.os.Handler;
import
android.util.Log;
//用來觀察系統里短消息的數據庫變化 ”表“內容觀察者,只要信息數據庫發生變化,都會觸發該ContentObserver 派生類
public
class
SMSContentObserver
extends
ContentObserver {
private
static
String TAG =
"SMSContentObserver"
;
private
int
MSG_OUTBOXCONTENT =
2
;
private
Context mContext ;
private
Handler mHandler ;
//更新UI線程
public
SMSContentObserver(Context context,Handler handler) {
super
(handler);
mContext = context ;
mHandler = handler ;
}
/**
* 當所監聽的Uri發生改變時,就會回調此方法
*
* @param selfChange 此值意義不大 一般情況下該回調值false
*/
@Override
public
void
onChange(
boolean
selfChange){
Log.i(TAG,
"the sms table has changed"
);
//查詢發件箱里的內容
Uri outSMSUri = Uri.parse(
"content://sms/sent"
) ;
Cursor c = mContext.getContentResolver().query(outSMSUri,
null
,
null
,
null
,
"date desc"
);
if
(c !=
null
){
Log.i(TAG,
"the number of send is"
+c.getCount()) ;
StringBuilder sb =
new
StringBuilder() ;
//循環遍歷
while
(c.moveToNext()){
sb.append(
"發件人手機號碼: "
+c.getInt(c.getColumnIndex(
"address"
)))
.append(
"信息內容: "
+c.getString(c.getColumnIndex(
"body"
)))
.append(
"/n"
);
}
c.close();
mHandler.obtainMessage(MSG_OUTBOXCONTENT, sb.toString()).sendToTarget();
}
}
}
主工程邏輯為MainActivity.java,對短消息的觀察Uri,通過測試我發現只能監聽此Uri “content://sms” (等同于"content://sms/"),而不能監聽其他的Uri,比如"content://sms/outbox"等。
雙擊【CTRL+C】復制 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 import
android.app.Activity;
import
android.database.Cursor;
import
android.net.Uri;
import
android.os.Bundle;
import
android.os.Handler;
import
android.os.Message;
import
android.provider.*;
import
android.util.Log;
import
android.widget.EditText;
import
android.widget.TextView;
public
class
MainActivity
extends
Activity {
private
TextView tvAirplane;
private
EditText etSmsoutbox;
private
SMSContentObserver smsContentObserver;
/** Called when the activity is first created. */
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
etSmsoutbox = (EditText) findViewById(R.id.smsoutboxContent);
smsContentObserver =
new
SMSContentObserver(
this
, mHandler);
//注冊內容觀察者
registerContentObservers() ;
}
private
void
registerContentObservers() {
// ”表“內容觀察者 ,通過測試我發現只能監聽此Uri -----> content://sms
// 監聽不到其他的Uri 比如說 content://sms/outbox
Uri smsUri = Uri.parse(
"content://sms"
);
getContentResolver().registerContentObserver(smsUri,
true
,smsContentObserver);
}
private
Handler mHandler =
new
Handler() {
public
void
handleMessage(Message msg) {
String outbox = (String) msg.obj;
etSmsoutbox.setText(outbox);
}
}
};
}
Activitypublic class MainActivity extends ListActivity { private TextView tv_info; private SMSContentObserver smsContentObserver; private CallLogObserver callLogObserver; private PhoneStateReceiver myReceiver; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { public void handleMessage(Message msg) { String msgBody = (String) msg.obj; tv_info.setText(msg.obj + ":" + msgBody); } }; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String[] array = { "注冊短信數據庫變化的觀察者", "收件箱數據庫……", "刪除新來電的通話記錄", "監聽新來電通話記錄的詳細信息", "取消注冊Observer",// "注冊電話狀態改變的廣播,當有來電時立即掛斷電話", "取消注冊廣播", }; for (int i = 0; i < array.length; i++) { array[i] = i + "、" + array[i]; } ListAdapter mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))); tv_info = new TextView(this);// 將內容顯示在TextView中 tv_info.setTextColor(Color.BLUE); tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); tv_info.setPadding(20, 10, 20, 10); getListView().addFooterView(tv_info); setListAdapter(mAdapter); myReceiver = new PhoneStateReceiver(); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { switch (position) { case 0: smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_WHAT); getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, smsContentObserver); // boolean notifyForDescendents(后裔):若為true,則監視所有以指定的Uri開頭的Uri;若為false,則只精確的監視指定的URI break; case 1: smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_INBOX_WHAT); getContentResolver().registerContentObserver(Uri.parse("content://sms/inbox"), true, smsContentObserver); break; case 2: callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_DELETE_WHAT); getContentResolver().registerContentObserver(Uri.parse("content://call_log/calls"), true, callLogObserver); break; case 3: callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_QUERY_WHAT); getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, callLogObserver);//等價于【Uri.parse("content://call_log/calls")】 break; case 4: if (smsContentObserver != null) getContentResolver().unregisterContentObserver(smsContentObserver); if (callLogObserver != null) getContentResolver().unregisterContentObserver(callLogObserver); break; case 5: registerReceiver(myReceiver, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED)); break; case 6: try { unregisterReceiver(myReceiver); } catch (Exception e) { } break; } } /** * 利用aidl及反射自動掛斷來電。注意,不能通過ContentResolver監聽通話記錄數據庫來掛斷電話,估計是因為電話已接通,不能再掛掉了 */ public void endCall() { // IBinder iBinder = ServiceManager.getService(TELEPHONY_SERVICE);//希望調用的方法,但此方法被系統隱藏了 try { Class<?> clazz = Class.forName("android.os.ServiceManager");//利用反射拿到其字節碼文件 Method method = clazz.getDeclaredMethod("getService", String.class);//獲取ServiceManager類的getService(String s)方法 IBinder ibinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);//參數為:調用此方法的對象,此方法的參數 ITelephony telephony = ITelephony.Stub.asInterface(ibinder);//把上面getService(String s)得到的IBinder對象轉化成【ITelephony】對象 boolean isSuccess = telephony.endCall();//調用ITelephony掛斷電話的方法 mHandler.sendMessage(Message.obtain(mHandler, 5, "是否成功掛斷電話:" + isSuccess)); } catch (Exception e) { mHandler.sendMessage(Message.obtain(mHandler, 5, "異常啦" + e.getMessage())); e.printStackTrace(); } } /**監聽來電狀態的廣播*/ class PhoneStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent != null) { if (TelephonyManager.EXTRA_STATE_RINGING.equalsIgnoreCase(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {//來電狀態 endCall(); } } } }}
短信數據庫的ContentObserver/**監聽或獲取手機短信內容的兩種方式 * 方式一:通過注冊廣播監聽短信 * 這種方式只對新收到的短消息有效,并且系統的這個廣播是有序廣播,現在在一些定制的系統或是有安全軟件的情況下,往往短消息都被截取到,并被干掉。 * 方法二:通過監聽短信數據庫的變化獲取短信 * 這種方式可以獲取手機上所有的短信,包括已讀未讀的短信,并且不受其它程序干擾 * ContentObserver的使用類似與設計模式中的觀察者模式,ContentObserver是觀察者,被觀察的ContentProvider是被觀察者。 * 當被觀察者ContentProvider的數據發生了增刪改的變化,就會及時的通知給ContentProvider,ContentObsserver做出相應的處理。*/public class SMSContentObserver extends ContentObserver { private Handler mHandler; private Context mContext; /**觀察類型:所有內容或僅收件箱*/ private int observerType; /**觀察所有內容*/ public static final int MSG_SMS_WHAT = 1; /**僅觀察收件箱*/ public static final int MSG_SMS_INBOX_WHAT = 2; public SMSContentObserver(Handler handler, Context context, int observerType) { super(handler); this.mHandler = handler; this.mContext = context; this.observerType = observerType; } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); if (observerType == MSG_SMS_WHAT) { Uri uri = Uri.parse("content://sms"); Cursor cursor = mContext.getContentResolver().query(uri, new String[] { "_id", "address", "body", "type", "date" }, null, null, "date desc"); if (cursor != null) { if (cursor.moveToFirst()) { //最后收到的短信在第一條. This method will return false if the cursor is empty int msgId = cursor.getInt(cursor.getColumnIndex("_id")); String msgAddr = cursor.getString(cursor.getColumnIndex("address")); String msgBody = cursor.getString(cursor.getColumnIndex("body")); String msgType = cursor.getString(cursor.getColumnIndex("type")); String msgDate = cursor.getString(cursor.getColumnIndex("date")); String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date(Long.parseLong(msgDate))); String msgObj = "收件箱/nId:" + msgId + "/n號碼:" + msgAddr + "/n內容:" + msgBody + "/n類型:" + msgType + "/n時間:" + date + "/n"; mHandler.sendMessage(Message.obtain(mHandler, MSG_SMS_WHAT, msgObj)); } cursor.close(); } } else if (observerType == MSG_SMS_INBOX_WHAT) { Uri uri = Uri.parse("content://sms/inbox"); Cursor cursor = mContext.getContentResolver().query(uri, null, "read = 0", null, "date desc");//Passing null will return all columns, which is inefficient. //等價于附加條件 if (cursor.getInt(cursor.getColumnIndex("read")) == 0) //表示短信未讀。這種方式不靠譜啊,建議用上面的方式! if (cursor != null) { StringBuilder sb = new StringBuilder("未讀短信/n"); while (cursor.moveToNext()) { String sendNumber = cursor.getString(cursor.getColumnIndex("address")); String body = cursor.getString(cursor.getColumnIndex("body")); sb.append("號碼:" + sendNumber + "/n內容:" + body + "/n"); } mHandler.obtainMessage(MSG_SMS_INBOX_WHAT, sb.toString()).sendToTarget(); cursor.close(); } } }}利用反射及aidl調用系統隱藏的方法目的: 利用反射及aidl調用系統隱藏的ServiceManager的getService方法,獲取ITelephony后調用其掛電話的方法步驟:1、copy android源代碼【com.android.internal.telephony】包中的【ITelephony.aidl】到自己的項目 為什么要copy這個文件呢?這是因為接聽/掛斷電話的方法在接口ITelephony.java里面,而這個接口是隱藏的(@hide),我們沒權限調用。2、由于ITelephony.aidl關聯了【android.telephony】包下的【NeighboringCellInfo.aidl】,所以也需把它拷貝過來。 上面完成之后,就會在你的gen目錄下自動生成 ITelephony.java接口文件![]()
3、然后我們就可以利用反射機制來取得ITelephony對象。 為什么要用反射呢? 因為 ITelephony是一個系統服務,要通過【ServiceManager】來獲取,但是ServiceManager同樣也是隱藏的。 所以,我們首先要通過反射機制拿到系統隱藏的ServiceManager對象 然后調用ServiceManager的【getService(String)】方法來取得遠程的【ITelephony】對象, 最后調用ITelephony的endCall()方法掛掉電話權限: <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" />
通話記錄數據庫的Observer/** * 撥號記錄的內容觀察者。 */public class CallLogObserver extends ContentObserver { /**觀察到記錄改變后的處理方式*/ private int type; /**刪除最近的一條通話記錄*/ public static final int MSG_CALLLOG_DELETE_WHAT = 3; /**查詢某一個聯系人最近的通話記錄*/ public static final int MSG_CALLLOG_QUERY_WHAT = 4; public static final String NUMBER = "17084143285"; private Handler mHandler; private Uri uri = CallLog.Calls.CONTENT_URI;//等價于【Uri.parse("content://call_log/calls")】 private ContentResolver resolver; public CallLogObserver(Handler handler, Context context, int type) { super(handler); this.mHandler = handler; this.type = type; resolver = context.getContentResolver(); } @Override public void onChange(boolean selfChange) { Cursor cursor; switch (type) { case MSG_CALLLOG_DELETE_WHAT://刪除最近的一條通話記錄 resolver.unregisterContentObserver(this);//注意:增刪改通話記錄后由于數據庫發生變化,所以系統會在修改后再發一條廣播,這時會重新回調onChange方法 //最終導致的結果就是:一次來電后刪除了多條甚至全部通話記錄。為防止這種循環啟發,必須在更改前就取消注冊!事實上,注冊的代碼應該放在廣播接收者中。 cursor = resolver.query(uri, null, null, null, "_id desc limit 1");//按_id倒序排序后取第一個,即:查詢結果按_id從大到小排序,然后取最上面一個(最近的通話記錄) if (cursor != null) { if (cursor.moveToFirst()) { int num = resolver.delete(uri, "_id=?", new String[] { cursor.getInt(cursor.getColumnIndex("_id")) + "" }); mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_DELETE_WHAT, "刪除的記錄數量:" + num)); } cursor.close(); } break; case MSG_CALLLOG_QUERY_WHAT://查詢某一個聯系人最近的通話記錄 String[] projection = new String[] { "_id", CallLog.Calls.TYPE, CallLog.Calls.NUMBER, CallLog.Calls.CACHED_NAME, CallLog.Calls.DATE, CallLog.Calls.DURATION }; String selection = "number=? and (type=1 or type=3)"; String[] selectionArgs = new String[] { NUMBER }; String sortOrder = CallLog.Calls.DEFAULT_SORT_ORDER;//按時間排序【date DESC】 cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder); if (cursor != null) { if (cursor.moveToFirst()) { int _id = cursor.getInt(cursor.getColumnIndex("_id")); int type = cursor.getInt(cursor.getColumnIndex("type"));//通話類型,1 來電 .INCOMING_TYPE;2 已撥 .OUTGOING_;3 未接 .MISSED_ String number = cursor.getString(cursor.getColumnIndex("number"));// 電話號碼 String name = cursor.getString(cursor.getColumnIndex("name"));//聯系人 long date = cursor.getLong(cursor.getColumnIndex("date"));//通話時間,即可以用getString接收,也可以用getLong接收 String formatDate = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault()).format(new Date(date)); int duration = cursor.getInt(cursor.getColumnIndex("duration"));//通話時長,單位:秒 String msgObj = "/nID:" + _id + "/n類型:" + type + "/n號碼:" + number + "/n名稱:" + name + "/n時間:" + formatDate + "/n時長:" + duration; mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_QUERY_WHAT, msgObj)); } cursor.close(); } break; } }}清單文件<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.itheima.ipdail" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.WRITE_CALL_LOG" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
新聞熱點
疑難解答