手机卫士day08
day08
來電短信黑名單攔截
- 演示金山衛(wèi)士相關(guān)功能
- 創(chuàng)建BlackNumberActivity
布局文件
<RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#8866ff00" ><TextViewandroid: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" /><Buttonandroid: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ù)庫創(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ù)庫*/@Overridepublic void onCreate(SQLiteDatabase 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ù)庫升級(jí)*/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} }單元測試
創(chuàng)建具備單元測試的Android項(xiàng)目, 拷貝清單文件的相關(guān)代碼
File->New->Project->Android Test Project
<instrumentationandroid:name="android.test.InstrumentationTestRunner"android:targetPackage="com.itheima.mobilesafeteach" /><application><uses-library android:name="android.test.runner" /> </application>
增刪改查(crud)邏輯實(shí)現(xiàn)
/*** 黑名單數(shù)據(jù)庫封裝* @author Kevin**/ 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) {SQLiteDatabase db = mHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put("number", number);values.put("mode", mode);db.insert("blacknumber", null, values);db.close();}/*** 刪除黑名單* @param number*/public void delete(String number) {SQLiteDatabase db = mHelper.getWritableDatabase();db.delete("blacknumber", "number=?", new String[] { number });db.close();}/*** 更新黑名單* @param number* @param mode*/public void update(String number, int mode) {SQLiteDatabase db = mHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put("mode", mode);db.update("blacknumber", values, "number=?", new String[] { number });db.close();}/*** 查找黑名單* @param number* @return*/public boolean find(String number) {SQLiteDatabase db = mHelper.getWritableDatabase();Cursor cursor = db.query("blacknumber",new String[] { "number", "mode" }, "number=?",new String[] { number }, null, null, null);boolean result = false;if (cursor.moveToFirst()) {result = true;}cursor.close();db.close();return result;}/*** 查找號(hào)碼攔截模式* @param number* @return*/public int findMode(String number) {SQLiteDatabase db = mHelper.getWritableDatabase();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.close();db.close();return mode;}/*** 查找黑名單列表* @return*/public ArrayList<BlackNumberInfo> findAll() {SQLiteDatabase db = mHelper.getWritableDatabase();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.close();db.close();return list;}/*** 黑名單對(duì)象* @author Kevin**/public class BlackNumberInfo {public String number;public int mode;@Overridepublic String toString() {return "BlackNumberInfo [number=" + number + ", mode=" + mode + "]";}} }增刪改查單元測試
public class TestBlackNumberDao extends AndroidTestCase {/*** 測試數(shù)據(jù)創(chuàng)建*/public void testCreateDb() {BlackNumberOpenHelper helper = new BlackNumberOpenHelper(getContext());helper.getWritableDatabase();}/*** 測試增加黑名單*/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);} else {BlackNumberDao.getInstance(getContext()).add("138123456" + i,mode);}}}/*** 測試刪除黑名單*/public void testDelete() {BlackNumberDao.getInstance(getContext()).delete("13812345601");}/*** 測試更新黑名單*/public void testUpdate() {BlackNumberDao.getInstance(getContext()).update("13812345600", 2);}/*** 測試查找黑名單*/public void testFind() {boolean find = BlackNumberDao.getInstance(getContext()).find("13812345600");assertEquals(true, find);}/*** 測試查找黑名單攔截模式*/public void testFindMode() {int mode = BlackNumberDao.getInstance(getContext()).findMode("13812345600");System.out.println("攔截模式:" + mode);} }使用命令行查看數(shù)據(jù)庫文件
1. 運(yùn)行adb shell進(jìn)入linux環(huán)境 2. 切換至data/data/包名/databases 3. 運(yùn)行sqlite3 *.db,進(jìn)入數(shù)據(jù)庫 4. 編寫sql語句,進(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é)果
@Override public 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ì)象,保存起來view.setTag(holder);} else {view = convertView;holder = (ViewHolder) view.getTag();// 從view對(duì)象中得到之前設(shè)置好的viewHolderSystem.out.println("listview重用了");}BlackNumberInfo info = mBlackNumberList.get(position);holder.tvNumber.setText(info.number);switch (info.mode) {case 1:holder.tvMode.setText("攔截電話");break;case 2:holder.tvMode.setText("攔截短信");break;case 3:holder.tvMode.setText("攔截電話+短信");break;}return view; }static class ViewHolder {public TextView tvNumber;public TextView tvMode; }
啟動(dòng)子線程在數(shù)據(jù)庫讀取數(shù)據(jù)
當(dāng)數(shù)據(jù)量比較大時(shí),讀取數(shù)據(jù)比較耗時(shí),為了避免ANR,最好將該邏輯放在子線程中進(jìn)行, 為了模擬數(shù)據(jù)量大時(shí)訪問比較慢的情況,可以讓線程休眠1-2秒后再加載數(shù)據(jù)- 加載中的進(jìn)度條展示
數(shù)據(jù)分批加載
分批加載優(yōu)勢:避免一次性加載過多內(nèi)容, 節(jié)省時(shí)間和流量 sql語句: select * from blacknumber limit 20 offset 0, 表示起始位置是0,加載條數(shù)為20, 等同于limit 0,20/*** 分頁查找黑名單列表* * @return*/ public ArrayList<BlackNumberInfo> findPart(int startIndex) {SQLiteDatabase db = mHelper.getWritableDatabase();Cursor cursor = db.rawQuery("select number,mode from blacknumber order by _id desc limit 20 offset ?",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.close();db.close();return list; }/*** 獲取黑名單數(shù)量* * @return*/ public int getTotalCount() {SQLiteDatabase db = mHelper.getWritableDatabase();Cursor cursor = db.rawQuery("select count(*) from blacknumber", null);int count = 0;if (cursor.moveToNext()) {count = cursor.getInt(0);}cursor.close();db.close();return count; }--------------------------------------//監(jiān)聽listview的滑動(dòng)事件 lvList.setOnScrollListener(new OnScrollListener() {// 滑動(dòng)狀態(tài)發(fā)生變化 // 1.靜止->滾動(dòng) 2.滾動(dòng)->靜止 3.慣性滑動(dòng) @Override public void onScrollStateChanged(AbsListView view, int scrollState) {if (scrollState == SCROLL_STATE_IDLE) {//獲取當(dāng)前l(fā)istview顯示的最后一個(gè)item的位置int lastVisiblePosition = lvList.getLastVisiblePosition();//判斷是否應(yīng)該加載下一頁if (lastVisiblePosition >= mBlackNumberList.size() - 1&& !isLoading) {int totalCount = BlackNumberDao.getInstance(BlackNumberActivity.this).getTotalCount();//判斷是否已經(jīng)到達(dá)最后一頁if (mStartIndex >= totalCount) {Toast.makeText(BlackNumberActivity.this, "沒有更多數(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.setVisibility(View.VISIBLE);//顯示進(jìn)度條isLoading = true;new Thread() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 第一頁數(shù)據(jù)if (mBlackNumberList == null) {mBlackNumberList = BlackNumberDao.getInstance(BlackNumberActivity.this).findPart(mStartIndex);} else {mBlackNumberList.addAll(BlackNumberDao.getInstance(BlackNumberActivity.this).findPart(mStartIndex));}mHandler.sendEmptyMessage(0);}}.start(); }-----------------------------------private int mStartIndex;//下一頁的起始位置 private boolean isLoading;// 表示是否正在加載private Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {pbLoading.setVisibility(View.GONE);//隱藏進(jìn)度條// 第一頁數(shù)據(jù)if (mAdapter == null) {mAdapter = new BlackNumberAdapter();lvList.setAdapter(mAdapter);} else {mAdapter.notifyDataSetChanged();//刷新adapter}mStartIndex = mBlackNumberList.size();isLoading = false;}; };添加黑名單
/*** 添加黑名單* * @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.setView(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.setOnClickListener(new OnClickListener() {@Overridepublic 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來判斷是哪種攔截模式switch (checkedRadioButtonId) {case R.id.rb_call:mode = 1;break;case R.id.rb_sms:mode = 2;break;case R.id.rb_all:mode = 3;break;default:break;}// 保存數(shù)據(jù)庫BlackNumberDao.getInstance(getApplicationContext()).add(number, mode);// 向列表第一個(gè)位置增加黑名單對(duì)象,并刷新listview//注意: 分頁查詢時(shí)需要逆序排列,保證后添加的最新數(shù)據(jù)展示在最前面BlackNumberInfo info = new BlackNumberInfo();info.number = number;info.mode = mode;mBlackNumberList.add(0, info);mAdapter.notifyDataSetChanged();dialog.dismiss();} else {Toast.makeText(getApplicationContext(), "輸入內(nèi)容不能為空!",Toast.LENGTH_SHORT).show();}}});btnCancel.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {dialog.dismiss();}});dialog.show(); }刪除黑名單
@Overridepublic void onClick(View v) {//從數(shù)據(jù)庫中刪除BlackNumberDao.getInstance(getApplicationContext()).delete(info.number);//從內(nèi)存列表中刪除并刷新listviewmBlackNumberList.remove(info);mAdapter.notifyDataSetChanged();} });
holder.ivDelete.setOnClickListener(new OnClickListener() {創(chuàng)建黑名單攔截服務(wù)
攔截短信邏輯實(shí)現(xiàn)
邏輯類似手機(jī)防盜頁面攔截特殊短信指令的代碼, 只不過該廣播是動(dòng)態(tài)注冊,不是靜態(tài)注冊. 動(dòng)態(tài)注冊的好處是可以隨服務(wù)的開啟或關(guān)閉來決定是否監(jiān)聽廣播,而且在同等優(yōu)先級(jí)的前提下,動(dòng)態(tài)注冊的廣播比靜態(tài)注冊的更先接收到廣播(可以通過打印日志進(jìn)行驗(yàn)證)
設(shè)置頁面增加黑名單攔截開關(guān)
通過此開關(guān)來開啟和關(guān)閉服務(wù), 邏輯類似來電歸屬地顯示的開關(guān)短信攔截優(yōu)化
通過關(guān)鍵詞智能攔截(介紹)
- 金山衛(wèi)士智能攔截簡介
金山衛(wèi)士關(guān)鍵詞數(shù)據(jù)庫
查看第四天資料,金山衛(wèi)士apk解壓文件,assets目錄下找firewall_sys_rules.db, 該數(shù)據(jù)制定了短信和來電的攔截規(guī)則短信攔截規(guī)則: 根據(jù)關(guān)鍵詞對(duì)短信內(nèi)容進(jìn)行過濾. 比如fapiao//對(duì)短信內(nèi)容進(jìn)行關(guān)鍵詞過濾 String messageBody = msg.getMessageBody(); if (messageBody != null && messageBody.contains("fapiao")) {abortBroadcast(); }
分詞(介紹)
單純依靠關(guān)鍵詞進(jìn)行過濾有時(shí)會(huì)出現(xiàn)一些問題, 比如: laogong, nikan,wode toufapiaobupiaoliang....所以有時(shí)候會(huì)對(duì)每一句話進(jìn)行分詞處理,比如可以將上述語句先拆分成不同的詞語:laogong,nikan,wode,toufa,piaobupiaoliang, 然后在這些詞匯中對(duì)關(guān)鍵詞再進(jìn)行過濾lucene分詞檢索框架短信攔截的兼容性處理
4.4以上系統(tǒng)手機(jī)對(duì)短信權(quán)限進(jìn)一步限制,導(dǎo)致無法攔截短信,可以通過監(jiān)聽短信數(shù)據(jù)庫的變化,及時(shí)刪除最新入庫的垃圾短信來實(shí)現(xiàn)短信攔截的目的. 為了避免誤刪舊的短信,需要和短信廣播結(jié)合起來使用
來電攔截
2. 很多服務(wù)都是獲取遠(yuǎn)程服務(wù)的代理對(duì)象IBinder,再調(diào)用里面的方法的.例如:IBinder b = ServiceManager.getService(ALARM_SERVICE);IAlarmManager service = IAlarmManager.Stub.asInterface(b);return new AlarmManager(service); 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)無法導(dǎo)包,因?yàn)楦居袥]有ServiceManager這個(gè)類,但我們知道它肯定存在,因?yàn)門elephonyManager就引用了該類,只不過android系統(tǒng)隱藏了這個(gè)類, 5. 為了調(diào)用隱藏類的方法,我們想到了反射
1. 掛斷電話的API早期版本endCall()是可以使用的,現(xiàn)在不可以用了;但本身掛斷電話這個(gè)功能是存在的通過反射獲取endCall方法
/*** 掛斷電話 * 注意加權(quán)限: <uses-permission* android:name="android.permission.CALL_PHONE"/>*/ public void endCall() {try {// 獲取ServiceManagerClass clazz = BlackNumberService.class.getClassLoader().loadClass("android.os.ServiceManager");Method method = clazz.getDeclaredMethod("getService", String.class);// 獲取方法getServiceIBinder 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)限: <uses-permissionandroid:name="android.permission.CALL_PHONE"/>
總結(jié)
- 上一篇: 联想服务器ready卡位置,联想闪联任意
- 下一篇: 第九课预习任务