手机卫士day08

来源:互联网 发布:搜狗排名优化软件 编辑:程序博客网 时间:2024/06/05 06:08

day08

  • 来电短信黑名单拦截

    • 演示金山卫士相关功能
    • 创建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="添加" />

    • 数据库创建

      public class BlackNumberOpenHelper extends SQLiteOpenHelper {

          public BlackNumberOpenHelper(Context ctx) {        super(ctx, "blacknumber.db", null, 1);//必须实现该构造方法    }    /**     * 第一次创建数据库     */    @Override    public void onCreate(SQLiteDatabase db) {        // 创建表, 三个字段,_id, number(电话号码),mode(拦截模式:电话,短信,电话+短信)        db.execSQL("create table blacknumber (_id integer primary key autoincrement, number varchar(20), mode integer)");    }    /**     * 数据库升级     */    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    }}
    • 单元测试

      • 创建具备单元测试的Android项目, 拷贝清单文件的相关代码

        File->New->Project->Android Test Project

        <instrumentation    android: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;        @Override        public 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/包名/databases3. 运行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的执行时间,进行对比

      • 最终优化结果

        @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创建");        // 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对象中得到之前设置好的viewHolder        System.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.惯性滑动@Overridepublic 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() {        @Override        public 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() {        @Override        public 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() {        @Override        public void onClick(View v) {            dialog.dismiss();        }    });    dialog.show();}
    • 删除黑名单
      holder.ivDelete.setOnClickListener(new OnClickListener() {

          @Override    public void onClick(View v) {        //从数据库中删除        BlackNumberDao.getInstance(getApplicationContext()).delete(                info.number);        //从内存列表中删除并刷新listview        mBlackNumberList.remove(info);        mAdapter.notifyDataSetChanged();    }});
    • 创建黑名单拦截服务

      • 拦截短信逻辑实现

        逻辑类似手机防盗页面拦截特殊短信指令的代码, 只不过该广播是动态注册,不是静态注册. 动态注册的好处是可以随服务的开启或关闭来决定是否监听广播,而且在同等优先级的前提下,动态注册的广播比静态注册的更先接收到广播(可以通过打印日志进行验证)
    • 设置页面增加黑名单拦截开关

      通过此开关来开启和关闭服务, 逻辑类似来电归属地显示的开关
    • 短信拦截优化

      • 通过关键词智能拦截(介绍)

        • 金山卫士智能拦截简介
        • 金山卫士关键词数据库

          查看第四天资料,金山卫士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以上系统手机对短信权限进一步限制,导致无法拦截短信,可以通过监听短信数据库的变化,及时删除最新入库的垃圾短信来实现短信拦截的目的. 为了避免误删旧的短信,需要和短信广播结合起来使用
    • 来电拦截
      1. 挂断电话的API早期版本endCall()是可以使用的,现在不可以用了;但本身挂断电话这个功能是存在的

      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. 为了调用隐藏类的方法,我们想到了反射
      • 通过反射获取endCall方法

        /** * 挂断电话  * 注意加权限: <uses-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);// 方法时静态的,不需要传递对象进去        ITelephony telephony = ITelephony.Stub.asInterface(binder);// 获取ITelephony对象,前提是要先配置好aidl文件        telephony.endCall();//挂断电话    } catch (Exception e) {        e.printStackTrace();    }}注意加权限: <uses-permission  android:name="android.permission.CALL_PHONE"/>
0 0
原创粉丝点击