來(lái)電短信黑名單攔截演示手機(jī)衛(wèi)士相關(guān)功能創(chuàng)建BlackNumberActivity布局文件
<RelativeLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:background="#8866ff00" ><TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="5dp" android:text="黑名單管理" android:textColor="#000" android:textSize="22sp" /><Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="5dp" android:text="添加" />數(shù)據(jù)庫(kù)創(chuàng)建public class BlackNumberOpenHelper extends SQLiteOpenHelper {
public BlackNumberOpenHelper(Context ctx) { super(ctx, "blacknumber.db", null, 1);//必須實(shí)現(xiàn)該構(gòu)造方法 } /** * 第一次創(chuàng)建數(shù)據(jù)庫(kù) */ @Override public void onCreate(SQLiteDataba db) { // 創(chuàng)建表, 三個(gè)字段,_id, number(電話號(hào)碼),mode(攔截模式:電話,短信,電話+短信) db.execSQL("create table blacknumber (_id integer primary key autoincrement, number varchar(20), mode integer)"); } /** * 數(shù)據(jù)庫(kù)升級(jí) */ @Override public void onUpgrade(SQLiteDataba db, int oldVersion, int newVersion) { }}單元測(cè)試創(chuàng)建具備單元測(cè)試的Android項(xiàng)目, 拷貝清單文件的相關(guān)代碼File->New->Project->Android Test Project
<instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.itheima.mobilesafeteach" /> <application> <us-library android:name="android.test.runner" /> </application>增刪改查(crud)邏輯實(shí)現(xiàn)
public class BlackNumberDao { private static BlackNumberDao sInstance; private BlackNumberOpenHelper mHelper; private BlackNumberDao(Context ctx) { mHelper = new BlackNumberOpenHelper(ctx); }; /** * 獲取單例對(duì)象 * @param ctx * @return */ public static BlackNumberDao getInstance(Context ctx) { if (sInstance == null) { synchronized (BlackNumberDao.class) { if (sInstance == null) { sInstance = new BlackNumberDao(ctx); } } } return sInstance; } /** * 增加黑名單 * @param number * @param mode */ public void add(String number, int mode) { SQLiteDataba db = mHelper.getWritableDataba(); ContentValues values = new ContentValues(); values.put("number", number); values.put("mode", mode); db.inrt("blacknumber", null, values); db.clo(); } /** * 刪除黑名單 * @param number */ public void delete(String number) { SQLiteDataba db = mHelper.getWritableDataba(); db.delete("blacknumber", "number=?", new String[] { number }); db.clo(); } /** * 更新黑名單 * @param number * @param mode */ public void update(String number, int mode) { SQLiteDataba db = mHelper.getWritableDataba(); ContentValues values = new ContentValues(); values.put("mode", mode); db.update("blacknumber", values, "number=?", new String[] { number }); db.clo(); } /** * 查找黑名單 * @param number * @return */ public boolean find(String number) { SQLiteDataba db = mHelper.getWritableDataba(); Cursor cursor = db.query("blacknumber", new String[] { "number", "mode" }, "number=?", new String[] { number }, null, null, null); boolean result = fal; if (cursor.moveToFirst()) { result = true; } cursor.clo(); db.clo(); return result; } /** * 查找號(hào)碼攔截模式 * @param number * @return */ public int findMode(String number) { SQLiteDataba db = mHelper.getWritableDataba(); Cursor cursor = db.query("blacknumber", new String[] { "mode" }, "number=?", new String[] { number }, null, null, null); int mode = -1; if (cursor.moveToFirst()) { mode = cursor.getInt(0); } cursor.clo(); db.clo(); return mode; } /** * 查找黑名單列表 * @return */ public ArrayList<BlackNumberInfo> findAll() { SQLiteDataba db = mHelper.getWritableDataba(); Cursor cursor = db .query("blacknumber", new String[] { "number", "mode" }, null, null, null, null, null); ArrayList<BlackNumberInfo> list = new ArrayList<BlackNumberDao.BlackNumberInfo>(); while (cursor.moveToNext()) { String number = cursor.getString(0); int mode = cursor.getInt(1); BlackNumberInfo info = new BlackNumberInfo(); info.number = number; info.mode = mode; list.add(info); } cursor.clo(); db.clo(); return list; } /** * 黑名單對(duì)象 * @author Kevin * */ public class BlackNumberInfo { public String number; public int mode; @Override public String toString() { return "BlackNumberInfo [number=" + number + ", mode=" + mode + "]"; } }}增刪改查單元測(cè)試
public class TestBlackNumberDao extends AndroidTestCa { /** * 測(cè)試數(shù)據(jù)創(chuàng)建 */ public void testCreateDb() { BlackNumberOpenHelper helper = new BlackNumberOpenHelper(getContext()); helper.getWritableDataba(); } /** * 測(cè)試增加黑名單 */ public void testAdd() { //添加100個(gè)號(hào)碼,攔截模式隨機(jī) Random random = new Random(); for (int i = 0; i < 100; i++) { int mode = random.nextInt(3) + 1; if (i < 10) { BlackNumberDao.getInstance(getContext()).add("1381234560" + i, mode); } el { BlackNumberDao.getInstance(getContext()).add("138123456" + i, mode); } } } /** * 測(cè)試刪除黑名單 */ public void testDelete() { BlackNumberDao.getInstance(getContext()).delete("13812345601"); } /** * 測(cè)試更新黑名單 */ public void testUpdate() { BlackNumberDao.getInstance(getContext()).update("13812345600", 2); } /** * 測(cè)試查找黑名單 */ public void testFind() { boolean find = BlackNumberDao.getInstance(getContext()).find( "13812345600"); asrtEquals(true, find); } /** * 測(cè)試查找黑名單攔截模式 */ public void testFindMode() { int mode = BlackNumberDao.getInstance(getContext()).findMode( "13812345600"); System.out.println("攔截模式:" + mode); }}使用命令行查看數(shù)據(jù)庫(kù)文件
1. 運(yùn)行adb shell進(jìn)入linux環(huán)境2. 切換至data/data/包名/databas3. 運(yùn)行sqlite3 *.db,進(jìn)入數(shù)據(jù)庫(kù)4. 編寫sql語(yǔ)句,進(jìn)行相關(guān)操作.記得加分號(hào)(;)結(jié)束5. .quit退出sqlite,切換到adb shell介紹convertView的重用機(jī)制和ViewHolder的使用方法
//使用static修飾內(nèi)部類,系統(tǒng)只加載一份字節(jié)碼文件,節(jié)省內(nèi)存static class ViewHolder { public TextView tvNumber; public TextView tvMode;}使用convertView和ViewHolder進(jìn)行優(yōu)化之后,重新使用traceview計(jì)算getView的執(zhí)行時(shí)間,進(jìn)行對(duì)比最終優(yōu)化結(jié)果
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { View view = null; ViewHolder holder = null; if (convertView == null) { view = View.inflate(BlackNumberActivity.this, R.layout.list_black_number_item, null); System.out.println("listview創(chuàng)建"); // viewHolder類似一個(gè)容器,可以保存findViewById獲得的view對(duì)象 holder = new ViewHolder(); holder.tvNumber = (TextView) view.findViewById(R.id.tv_number); holder.tvMode = (TextView) view.findViewById(R.id.tv_mode); // 將viewHolder設(shè)置給view對(duì)象,保存起來(lái) view.tTag(holder); } el { view = convertView; holder = (ViewHolder) view.getTag();// 從view對(duì)象中得到之前設(shè)置好的viewHolder System.out.println("listview重用了"); } BlackNumberInfo info = mBlackNumberList.get(position); holder.tvNumber.tText(info.number); switch (info.mode) { ca 1: holder.tvMode.tText("攔截電話"); break; ca 2: holder.tvMode.tText("攔截短信"); break; ca 3: holder.tvMode.tText("攔截電話+短信"); break; } return view;}static class ViewHolder { public TextView tvNumber; public TextView tvMode;}啟動(dòng)子線程在數(shù)據(jù)庫(kù)讀取數(shù)據(jù)
當(dāng)數(shù)據(jù)量比較大時(shí),讀取數(shù)據(jù)比較耗時(shí),為了避免ANR,最好將該邏輯放在子線程中進(jìn)行, 為了模擬數(shù)據(jù)量大時(shí)訪問(wèn)比較慢的情況,可以讓線程休眠1-2秒后再加載數(shù)據(jù)
加載中的進(jìn)度條展示數(shù)據(jù)分批加載分批加載優(yōu)勢(shì):避免一次性加載過(guò)多內(nèi)容, 節(jié)省時(shí)間和流量sql語(yǔ)句: lect * from blacknumber limit 20 offt 0, 表示起始位置是0,加載條數(shù)為20, 等同于limit 0,20/** * 分頁(yè)查找黑名單列表 * * @return */public ArrayList<BlackNumberInfo> findPart(int startIndex) { SQLiteDataba db = mHelper.getWritableDataba(); Cursor cursor = db.rawQuery( "lect number,mode from blacknumber order by _id desc limit 20 offt ?", new String[] { String.valueOf(startIndex) }); ArrayList<BlackNumberInfo> list = new ArrayList<BlackNumberDao.BlackNumberInfo>(); while (cursor.moveToNext()) { String number = cursor.getString(0); int mode = cursor.getInt(1); BlackNumberInfo info = new BlackNumberInfo(); info.number = number; info.mode = mode; list.add(info); } cursor.clo(); db.clo(); return list;}/** * 獲取黑名單數(shù)量 * * @return */public int getTotalCount() { SQLiteDataba db = mHelper.getWritableDataba(); Cursor cursor = db.rawQuery("lect count(*) from blacknumber", null); int count = 0; if (cursor.moveToNext()) { count = cursor.getInt(0); } cursor.clo(); db.clo(); return count;}--------------------------------------//監(jiān)聽listview的滑動(dòng)事件lvList.tOnScrollListener(new OnScrollListener() {// 滑動(dòng)狀態(tài)發(fā)生變化// 1.靜止->滾動(dòng) 2.滾動(dòng)->靜止 3.慣性滑動(dòng)@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE) { //獲取當(dāng)前l(fā)istview顯示的最后一個(gè)item的位置 int lastVisiblePosition = lvList.getLastVisiblePosition(); //判斷是否應(yīng)該加載下一頁(yè) if (lastVisiblePosition >= mBlackNumberList.size() - 1 && !isLoading) { int totalCount = BlackNumberDao.getInstance( BlackNumberActivity.this).getTotalCount(); //判斷是否已經(jīng)到達(dá)最后一頁(yè) if (mStartIndex >= totalCount) { Toast.makeText(BlackNumberActivity.this, "沒(méi)有更多數(shù)據(jù)了", Toast.LENGTH_SHORT).show(); return; } Toast.makeText(BlackNumberActivity.this, "加載更多數(shù)據(jù)...", Toast.LENGTH_SHORT).show(); System.out.println("加載更多數(shù)據(jù)..."); initData(); } }}-----------------------------------//加載數(shù)據(jù)private void initData() { pbLoading.tVisibility(View.VISIBLE);//顯示進(jìn)度條 isLoading = true; new Thread() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // 第一頁(yè)數(shù)據(jù) if (mBlackNumberList == null) { mBlackNumberList = BlackNumberDao.getInstance( BlackNumberActivity.this).findPart(mStartIndex); } el { mBlackNumberList.addAll(BlackNumberDao.getInstance( BlackNumberActivity.this).findPart(mStartIndex)); } mHandler.ndEmptyMessage(0); } }.start();}-----------------------------------private int mStartIndex;//下一頁(yè)的起始位置private boolean isLoading;// 表示是否正在加載private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { pbLoading.tVisibility(View.GONE);//隱藏進(jìn)度條 // 第一頁(yè)數(shù)據(jù) if (mAdapter == null) { mAdapter = new BlackNumberAdapter(); lvList.tAdapter(mAdapter); } el { mAdapter.notifyDataSetChanged();//刷新adapter } mStartIndex = mBlackNumberList.size(); isLoading = fal; };};添加黑名單
/** * 添加黑名單 * * @param view */public void addBlackNumber(View v) { AlertDialog.Builder builder = new AlertDialog.Builder(this); View view = View.inflate(this, R.layout.dialog_add_black_number, null); final AlertDialog dialog = builder.create(); dialog.tView(view, 0, 0, 0, 0); final EditText etBlackNumber = (EditText) view .findViewById(R.id.et_black_number); final RadioGroup rgMode = (RadioGroup) view.findViewById(R.id.rg_mode); Button btnOK = (Button) view.findViewById(R.id.btn_ok); Button btnCancel = (Button) view.findViewById(R.id.btn_cancel); btnOK.tOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String number = etBlackNumber.getText().toString().trim(); if (!TextUtils.isEmpty(number)) { int checkedRadioButtonId = rgMode.getCheckedRadioButtonId(); int mode = 1; // 根據(jù)當(dāng)前選中的RadioButtonId來(lái)判斷是哪種攔截模式 switch (checkedRadioButtonId) { ca R.id.rb_call: mode = 1; break; ca R.id.rb_sms: mode = 2; break; ca R.id.rb_all: mode = 3; break; default: break; } // 保存數(shù)據(jù)庫(kù) BlackNumberDao.getInstance(getApplicationContext()).add( number, mode); // 向列表第一個(gè)位置增加黑名單對(duì)象,并刷新listview //注意: 分頁(yè)查詢時(shí)需要逆序排列,保證后添加的最新數(shù)據(jù)展示在最前面 BlackNumberInfo info = new BlackNumberInfo(); info.number = number; info.mode = mode; mBlackNumberList.add(0, info); mAdapter.notifyDataSetChanged(); dialog.dismiss(); } el { Toast.makeText(getApplicationContext(), "輸入內(nèi)容不能為空!", Toast.LENGTH_SHORT).show(); } } }); btnCancel.tOnClickListener(new OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); dialog.show();}刪除黑名單
holder.ivDelete.tOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //從數(shù)據(jù)庫(kù)中刪除 BlackNumberDao.getInstance(getApplicationContext()).delete( info.number); //從內(nèi)存列表中刪除并刷新listview mBlackNumberList.remove(info); mAdapter.notifyDataSetChanged(); }});創(chuàng)建黑名單攔截服務(wù)攔截短信邏輯實(shí)現(xiàn)
邏輯類似手機(jī)防盜頁(yè)面攔截特殊短信指令的代碼, 只不過(guò)該廣播是動(dòng)態(tài)注冊(cè),不是靜態(tài)注冊(cè). 動(dòng)態(tài)注冊(cè)的好處是可以隨服務(wù)的開啟或關(guān)閉來(lái)決定是否監(jiān)聽廣播,而且在同等優(yōu)先級(jí)的前提下,動(dòng)態(tài)注冊(cè)的廣播比靜態(tài)注冊(cè)的更先接收到廣播(可以通過(guò)打印日志進(jìn)行驗(yàn)證)
設(shè)置頁(yè)面增加黑名單攔截開關(guān)通過(guò)此開關(guān)來(lái)開啟和關(guān)閉服務(wù), 邏輯類似來(lái)電歸屬地顯示的開關(guān)
來(lái)電攔截1. 掛斷電話的API早期版本endCall()是可以使用的,現(xiàn)在不可以用了;但本身掛斷電話這個(gè)功能是存在的
2. 很多服務(wù)都是獲取遠(yuǎn)程服務(wù)的代理對(duì)象IBinder,再調(diào)用里面的方法的.例如:
IBinder b = ServiceManager.getService(ALARM_SERVICE);
IAlarmManager rvice = IAlarmManager.Stub.asInterface(b);
return new AlarmManager(rvice);
3. 于是我們跟蹤TelephoneyManager,查看它的對(duì)象到底是如何創(chuàng)建的.我們跟蹤到了這樣一個(gè)方法:
private ITelephony getITelephony() {
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
該方法返回一個(gè)ITelephony對(duì)象, 查看ITelephony對(duì)象的方法,發(fā)現(xiàn)有endCall方法
4. 于是我們將獲取ITelephony的代碼拷貝到自己的項(xiàng)目中,發(fā)現(xiàn)無(wú)法導(dǎo)包,因?yàn)楦居袥](méi)有ServiceManager這個(gè)類,但我們知道它肯定存在,因?yàn)門elephonyManager就引用了該類,只不過(guò)android系統(tǒng)隱藏了這個(gè)類,
5. 為了調(diào)用隱藏類的方法,我們想到了反射
通過(guò)反射獲取endCall方法/** * 掛斷電話 * 注意加權(quán)限: <us-permission * android:name="android.permission.CALL_PHONE"/> */public void endCall() { try { // 獲取ServiceManager Class clazz = BlackNumberService.class.getClassLoader().loadClass( "android.os.ServiceManager"); Method method = clazz.getDeclaredMethod("getService", String.class);// 獲取方法getService IBinder binder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);// 方法時(shí)靜態(tài)的,不需要傳遞對(duì)象進(jìn)去 ITelephony telephony = ITelephony.Stub.asInterface(binder);// 獲取ITelephony對(duì)象,前提是要先配置好aidl文件 telephony.endCall();//掛斷電話 } catch (Exception e) { e.printStackTrace(); }}注意加權(quán)限: <us-permission android:name="android.permission.CALL_PHONE"/>
本文發(fā)布于:2023-02-28 19:58:00,感謝您對(duì)本站的認(rèn)可!
本文鏈接:http://www.newhan.cn/zhishi/a/167764652272924.html
版權(quán)聲明:本站內(nèi)容均來(lái)自互聯(lián)網(wǎng),僅供演示用,請(qǐng)勿用于商業(yè)和其他非法用途。如果侵犯了您的權(quán)益請(qǐng)與我們聯(lián)系,我們將在24小時(shí)內(nèi)刪除。
本文word下載地址:radiogroup(radiogroup什么意思).doc
本文 PDF 下載地址:radiogroup(radiogroup什么意思).pdf
| 留言與評(píng)論(共有 0 條評(píng)論) |