手机卫士day08
day08
來電短信黑名單攔截
- 演示金山衛士相關功能
- 創建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="添加" />數據庫創建
public class BlackNumberOpenHelper extends SQLiteOpenHelper {
public BlackNumberOpenHelper(Context ctx) {super(ctx, "blacknumber.db", null, 1);//必須實現該構造方法}/*** 第一次創建數據庫*/@Overridepublic void onCreate(SQLiteDatabase db) {// 創建表, 三個字段,_id, number(電話號碼),mode(攔截模式:電話,短信,電話+短信)db.execSQL("create table blacknumber (_id integer primary key autoincrement, number varchar(20), mode integer)");}/*** 數據庫升級*/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} }單元測試
創建具備單元測試的Android項目, 拷貝清單文件的相關代碼
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)邏輯實現
/*** 黑名單數據庫封裝* @author Kevin**/ public class BlackNumberDao {private static BlackNumberDao sInstance;private BlackNumberOpenHelper mHelper;private BlackNumberDao(Context ctx) {mHelper = new BlackNumberOpenHelper(ctx);};/*** 獲取單例對象* @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;}/*** 查找號碼攔截模式* @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;}/*** 黑名單對象* @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 {/*** 測試數據創建*/public void testCreateDb() {BlackNumberOpenHelper helper = new BlackNumberOpenHelper(getContext());helper.getWritableDatabase();}/*** 測試增加黑名單*/public void testAdd() {//添加100個號碼,攔截模式隨機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);} }使用命令行查看數據庫文件
1. 運行adb shell進入linux環境 2. 切換至data/data/包名/databases 3. 運行sqlite3 *.db,進入數據庫 4. 編寫sql語句,進行相關操作.記得加分號(;)結束 5. .quit退出sqlite,切換到adb shell
- 介紹convertView的重用機制
介紹ViewHolder的使用方法
//使用static修飾內部類,系統只加載一份字節碼文件,節省內存 static class ViewHolder {public TextView tvNumber;public TextView tvMode; } }使用convertView和ViewHolder進行優化之后,重新使用traceview計算getView的執行時間,進行對比
最終優化結果
@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創建");// viewHolder類似一個容器,可以保存findViewById獲得的view對象holder = new ViewHolder();holder.tvNumber = (TextView) view.findViewById(R.id.tv_number);holder.tvMode = (TextView) view.findViewById(R.id.tv_mode);// 將viewHolder設置給view對象,保存起來view.setTag(holder);} else {view = convertView;holder = (ViewHolder) view.getTag();// 從view對象中得到之前設置好的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; }
啟動子線程在數據庫讀取數據
當數據量比較大時,讀取數據比較耗時,為了避免ANR,最好將該邏輯放在子線程中進行, 為了模擬數據量大時訪問比較慢的情況,可以讓線程休眠1-2秒后再加載數據- 加載中的進度條展示
數據分批加載
分批加載優勢:避免一次性加載過多內容, 節省時間和流量 sql語句: select * from blacknumber limit 20 offset 0, 表示起始位置是0,加載條數為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; }/*** 獲取黑名單數量* * @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; }--------------------------------------//監聽listview的滑動事件 lvList.setOnScrollListener(new OnScrollListener() {// 滑動狀態發生變化 // 1.靜止->滾動 2.滾動->靜止 3.慣性滑動 @Override public void onScrollStateChanged(AbsListView view, int scrollState) {if (scrollState == SCROLL_STATE_IDLE) {//獲取當前listview顯示的最后一個item的位置int lastVisiblePosition = lvList.getLastVisiblePosition();//判斷是否應該加載下一頁if (lastVisiblePosition >= mBlackNumberList.size() - 1&& !isLoading) {int totalCount = BlackNumberDao.getInstance(BlackNumberActivity.this).getTotalCount();//判斷是否已經到達最后一頁if (mStartIndex >= totalCount) {Toast.makeText(BlackNumberActivity.this, "沒有更多數據了",Toast.LENGTH_SHORT).show();return;}Toast.makeText(BlackNumberActivity.this, "加載更多數據...",Toast.LENGTH_SHORT).show();System.out.println("加載更多數據...");initData();}} }-----------------------------------//加載數據 private void initData() {pbLoading.setVisibility(View.VISIBLE);//顯示進度條isLoading = true;new Thread() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 第一頁數據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);//隱藏進度條// 第一頁數據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;// 根據當前選中的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;}// 保存數據庫BlackNumberDao.getInstance(getApplicationContext()).add(number, mode);// 向列表第一個位置增加黑名單對象,并刷新listview//注意: 分頁查詢時需要逆序排列,保證后添加的最新數據展示在最前面BlackNumberInfo info = new BlackNumberInfo();info.number = number;info.mode = mode;mBlackNumberList.add(0, info);mAdapter.notifyDataSetChanged();dialog.dismiss();} else {Toast.makeText(getApplicationContext(), "輸入內容不能為空!",Toast.LENGTH_SHORT).show();}}});btnCancel.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {dialog.dismiss();}});dialog.show(); }刪除黑名單
@Overridepublic void onClick(View v) {//從數據庫中刪除BlackNumberDao.getInstance(getApplicationContext()).delete(info.number);//從內存列表中刪除并刷新listviewmBlackNumberList.remove(info);mAdapter.notifyDataSetChanged();} });
holder.ivDelete.setOnClickListener(new OnClickListener() {創建黑名單攔截服務
攔截短信邏輯實現
邏輯類似手機防盜頁面攔截特殊短信指令的代碼, 只不過該廣播是動態注冊,不是靜態注冊. 動態注冊的好處是可以隨服務的開啟或關閉來決定是否監聽廣播,而且在同等優先級的前提下,動態注冊的廣播比靜態注冊的更先接收到廣播(可以通過打印日志進行驗證)
設置頁面增加黑名單攔截開關
通過此開關來開啟和關閉服務, 邏輯類似來電歸屬地顯示的開關短信攔截優化
通過關鍵詞智能攔截(介紹)
- 金山衛士智能攔截簡介
金山衛士關鍵詞數據庫
查看第四天資料,金山衛士apk解壓文件,assets目錄下找firewall_sys_rules.db, 該數據制定了短信和來電的攔截規則短信攔截規則: 根據關鍵詞對短信內容進行過濾. 比如fapiao//對短信內容進行關鍵詞過濾 String messageBody = msg.getMessageBody(); if (messageBody != null && messageBody.contains("fapiao")) {abortBroadcast(); }
分詞(介紹)
單純依靠關鍵詞進行過濾有時會出現一些問題, 比如: laogong, nikan,wode toufapiaobupiaoliang....所以有時候會對每一句話進行分詞處理,比如可以將上述語句先拆分成不同的詞語:laogong,nikan,wode,toufa,piaobupiaoliang, 然后在這些詞匯中對關鍵詞再進行過濾lucene分詞檢索框架短信攔截的兼容性處理
4.4以上系統手機對短信權限進一步限制,導致無法攔截短信,可以通過監聽短信數據庫的變化,及時刪除最新入庫的垃圾短信來實現短信攔截的目的. 為了避免誤刪舊的短信,需要和短信廣播結合起來使用
來電攔截
2. 很多服務都是獲取遠程服務的代理對象IBinder,再調用里面的方法的.例如:IBinder b = ServiceManager.getService(ALARM_SERVICE);IAlarmManager service = IAlarmManager.Stub.asInterface(b);return new AlarmManager(service); 3. 于是我們跟蹤TelephoneyManager,查看它的對象到底是如何創建的.我們跟蹤到了這樣一個方法: private ITelephony getITelephony() {return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));} 該方法返回一個ITelephony對象, 查看ITelephony對象的方法,發現有endCall方法 4. 于是我們將獲取ITelephony的代碼拷貝到自己的項目中,發現無法導包,因為根本有沒有ServiceManager這個類,但我們知道它肯定存在,因為TelephonyManager就引用了該類,只不過android系統隱藏了這個類, 5. 為了調用隱藏類的方法,我們想到了反射
1. 掛斷電話的API早期版本endCall()是可以使用的,現在不可以用了;但本身掛斷電話這個功能是存在的通過反射獲取endCall方法
/*** 掛斷電話 * 注意加權限: <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);// 方法時靜態的,不需要傳遞對象進去ITelephony telephony = ITelephony.Stub.asInterface(binder);// 獲取ITelephony對象,前提是要先配置好aidl文件telephony.endCall();//掛斷電話} catch (Exception e) {e.printStackTrace();} }注意加權限: <uses-permissionandroid:name="android.permission.CALL_PHONE"/>
總結
- 上一篇: 联想服务器ready卡位置,联想闪联任意
- 下一篇: 第九课预习任务