黑马52期学后总结笔记(六)


51_黑名单号码数据库的创建&曾删改查_40

1、演示金山卫士的拦截效果;

2、创建手机防盗页面CallSmsSafeActivity,并在功能清单文件注册。

3、添加跳转逻辑,并实现布局文件,头部采用相对布局

 

4、在com.itheima.mobile.db目录下创建数据库打开帮助类BlackNumberDBOpenHelper,它是继承SQLiteOpenHelper的;

 

5、讲解构造方法的参数和OnCreate方法什么时候执行;

 

6、创建数据库的表结构

//创建表 blacknumber 主键_id自增长 ,number黑名单号码,mode拦截模式:1电话拦截 2短信拦截  3全部拦截

db.execSQL("create table blacknumber (_id integer primary keyautoincrement,number varchar(20),mode varchar(2))");

 

 

7、创建新包com.itheima.mobilesafe.test并创建测试数据类TestBlackNumberDB 继承AndroidTestCase

 

 

8、创建数据库方法testCreateDB()

public void testCreateDb(){

        BlackNumberDBOpenHelperhelper = newBlackNumberDBOpenHelper(getContext());

        helper.getWritableDatabase();

    }

 

9、测试报错后,添加测试框架相关参数;

测试框架(放在manifest根节点)

<instrumentation

        android:name="android.test.InstrumentationTestRunner"

        android:targetPackage="com.itheima.mobilesafe"/>

 

依赖库(放在application里面)

  <uses-library android:name="android.test.runner"/>

 

运行测试代码,查看是否已经成功创建数据库,导出用工具打开查看列表信息

 

10、数据的增、删、改、查的实现com.itheima.mobilesafe.db.dao

   创建类BlackNumberDao在里面实现增删改查;

   A:在构造方法里创建数据库

helper = new BlackNumberDBOpenHelper(context);

 

   B:数据库的添加add(Stringnumber,String mode)

     SQLiteDatabase db= helper.getWritableDatabase();

     ContentValuesvalues = new ContentValues();

      values.put("number", number);

      values.put("mode", mode);

      //第二个参数:当内容为空时

      db.insert("blacknumber", null, values);

      db.close();

 

nullColumnHack
当values参数为空或者里面没有内容的时候,insert是会失败的(底层数据库不允许插入一个空行),为了防止这种情况,要在这里指定一个列名,到时候如果发现将要插入的行为空行时,就会将你指定的这个列名的值设为null,然后再向数据库中插入。
通过观察源码的insertWithOnConflict方法可以看到当ContentValues类型的数据initialValues为null或size<=0时,就会在sql语句中添加nullColumnHack的设置。

 

  C:数据库的删除delete(String number)

SQLiteDatabase db= helper.getWritableDatabase();

        db.delete("blacknumber", "number=?", newString[]{number});

        db.close();

 

  D:数据的修改update(String number ,StringnewMode)

SQLiteDatabase db= helper.getWritableDatabase();

        ContentValuesvalues = new ContentValues();

        values.put("mode",newMode);

        db.update("blacknumber", values, "number=?", newString[]{number});

        db.close();

 

 

  E:查询黑名单是否存在该号码find(String number)

 

  boolean result = false;

    SQLiteDatabase db= helper.getWritableDatabase();

        Cursorcursor = db.query("blacknumber", null, "number=?", new String[]{number},null, null, null);

        if(cursor.moveToNext()){

            result = true;

        }

        cursor.close();

        db.close();

        return result;

  F:查询拦截模式findMode(String number)

 

   String mode = null;

    SQLiteDatabase db= helper.getWritableDatabase();

        Cursorcursor = db.query("blacknumber", new String[]{"mode"}, "number=?", new String[]{number},null, null, null);

        if(cursor.moveToNext()){

            mode = cursor.getString(0);

        }

        cursor.close();

        db.close();

        return mode;

 

11、写测试代码

 

public void testadd() {

        BlackNumberDaoblackNumberDao = newBlackNumberDao(getContext());

        blackNumberDao.add("119", "1");

    }

 

    public void testdelete() {

        BlackNumberDaoblackNumberDao = newBlackNumberDao(getContext());

        blackNumberDao.delete("119");

    }

 

    public void testupdate() {

        BlackNumberDaoblackNumberDao = newBlackNumberDao(getContext());

        blackNumberDao.update("119", "2");

 

    }

 

    public void testfind() {

        BlackNumberDaoblackNumberDao = newBlackNumberDao(getContext());

        boolean find = blackNumberDao.find("119");

        assertEquals(true, find);

    }

 

    public void testfindMode() {

        BlackNumberDaoblackNumberDao = newBlackNumberDao(getContext());

        Stringmode = blackNumberDao.findMode("119");

        System.out.println("拦截模式:" + mode);

 

    }

 知识拓展

assertEquals:用于判断实际值和期望值是否相同
assertSame:判断实际值和期望值是否为同一个对象

  

 

12.进入模拟器查看数据

  A:列出两个模拟器命令:adb devices

  B: 进入指导模拟器命令:adb -s emulator-5554 shell

  C:进入到data/data/com.ithiema.mobilesafe/databases目录下

  D:打开数据库命令:sqlite3 blacknumber.db

  F: 查询表内容SQL语句:select * from blacknumber;

 

 

 

知识拓展:

Android使用getWritableDatabase()和getReadableDatabase()方法都可以获取一个用于操作数据库的SQLiteDatabase实例。(getReadableDatabase()方法中会调用getWritableDatabase()方法)

其中getWritableDatabase() 方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用的是getWritableDatabase() 方法就会出错。

getReadableDatabase()方法则是先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。如果该问题成功解决,则只读数据库对象就会关闭,然后返回一个可读写的数据库对象。

 

 

 

52_黑名单号码界面的展现_10

1、便于展示先添加100条数据

 Random random = new Random();

        //13512340001

for(int i = 0 ;i<100;i++ ){

            blackNumberDao.add("1351234000"+i, String.valueOf(random.nextInt(3)+1));

}

 

2.增加到所有黑名单数据的接口List<BlackNumberInfo> findAll()

 

List<BlackNumberInfo>infos = newArrayList<BlackNumberInfo>();

    SQLiteDatabase database = helper.getWritableDatabase();

        Cursorcursor = database.query("blacknumber", new String[]{"number","mode"}, null, null, null, null, null);

        while(cursor.moveToNext()){

            BlackNumberInfo info = newBlackNumberInfo();

            String number = cursor.getString(0);

            String mode = cursor.getString(1);

            info.setMode(mode);

            info.setNumber(number);

            infos.add(info);

           

        }

        cursor.close();

        database.close();

       

        return infos;

 

3.初始化ListView 显示数据

       初始化数据

       daonew BlackNumberDao(this);

      list = dao.findAll();

   

 

       数据显示:

       public int getCount() {

         // TODO Auto-generated methodstub

         return list.size();

      }

     

      public View getView(int position, View convertView, ViewGroup parent) {

 

      TextView view = new TextView(getApplicationContext());

      view.setText(list.get(position).toString());

 

      return view;

   }

 

    运行演示测试效果

53_ListView的简单优化_35

1、自定义每条的样式创建相对布局文件list_callsmssafe_item.xml

  <?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content" >

 

    <TextView

        android:id="@+id/tv_number"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginLeft="5dip"

        android:layout_marginTop="5dip"

        android:text="5554"

        android:textColor="#000000"

        android:textSize="20sp" />

 

    <TextView

        android:id="@+id/tv_mode"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_below="@id/tv_number"

        android:layout_marginLeft="5dip"

        android:layout_marginTop="1dip"

        android:text="拦截模式"

        android:textColor="#99000000"

        android:textSize="16sp" />

 

    <ImageView

        android:layout_centerVertical="true"

        android:src="@drawable/delete_button"

        android:layout_alignParentRight="true"

        android:layout_width="40dip"

        android:layout_height="40dip" />

 

</RelativeLayou

 

2、在getView里面实现显示代码

         if("1".equals(info.getMode())){

                tv_mode.setText("拦截电话");

            }else if("2".equals(info.getMode())){

                tv_mode.setText("拦截短信");

            }else if("3".equals(info.getMode())){

                tv_mode.setText("拦截电话+短信");

            }

 

 

 

 

 

 3、ListView的简单优化

       View view;

            if(convertView != null){

                view= convertView;

                Log.i(TAG, "使用历史缓存的显示数据"+position);

            }else{

                Log.i(TAG, "创建新的View的显示数据"+position);

                view=View.inflate(CallSmsSafeActivity.this, R.layout.list_callsmssafe_item, null);

            }

 

4.进一步优化

view.findViewById(R.id.tv_number);该代码查找孩子的时候比较耗时,消耗资源,画图分析原理如下;

 

 

5、代码实现

 A:定义容器

  static class ViewHolder{

      TextView tv_number;

      TextView tv_mode;

   }

 

B:当创建View的时候实例化容器;

           ViewHolder holder;

         if(convertView != null){

            view = convertView;

            holder = (ViewHolder) view.getTag();

           

         }else{

           

            //初始化容器

            holder = newViewHolder();

设置与该视图相关联的标记。一个标签可以用来标记一个视图层次结构层次结构内不必是惟一的。标签也可以用来存储数据在一个视图没有求助于另一个数据结构

            view.setTag(holder);

         }

 

C:使用

         holder.tv_number.setText(info.getNumber());

            if("1".equals(info.getMode())){

                holder.tv_mode.setText("拦截电话");

            }else if("2".equals(info.getMode())){

                holder.tv_mode.setText("拦截短信");

            }else if("3".equals(info.getMode())){

                holder.tv_mode.setText("拦截电话+短信");

            }

 

性能提高百分之五左右。

54_ListView的加载UI效果优化_8

1、解决数据库内容很多的情形;

  findAll()在该方法休眠3秒演示效果

2、创建一个线程去读取数据:

  new Thread() {

    public void run() {

      list = dao.findAll();//查询数据库得到数据

      handler.sendEmptyMessage(0);//发消息给主线程更新数据

     };

   }.start();

3、更新数据

//更新数据

private Handler handler = new Handler() {

   public voidhandleMessage(android.os.Message msg) {

   lv_call_sms_safe.setAdapter(new CallSmsSafeAdapter());

      };

   };

 

4、没数据的时候加一个提醒效果,写布局文件

  <FrameLayout

        android:layout_width="match_parent"

        android:layout_height="match_parent" >

 

        <ListView

            android:id="@+id/lv_call_sms_safe"

            android:layout_width="match_parent"

            android:layout_height="match_parent" >

        </ListView>

 

        <LinearLayout

            android:id="@+id/ll_loading"

            android:gravity="center"

           android:visibility="invisible"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:orientation="vertical" >

 

            <ProgressBar

                android:layout_width="wrap_content"

                android:layout_height="wrap_content" />

 

            <TextView

                android:text="给力加载中..."

                android:layout_width="wrap_content"

                android:layout_height="wrap_content" />

        </LinearLayout>

</FrameLayout>

 

5、代码配合处理

ll_loading.setVisibility(View.VISIBLE);//没数据的时候显示

ll_loading.setVisibility(View.INVISIBLE);//数据加载好了隐藏

 

 

55_ListView的数据分批加载_41

1、讲解分批加载的好处:不用等待太久、节约流量、慢慢引导用户看感兴趣内容;

 

2、导出数据库,打开数据库的分批查询,写SQL语句:

  select number ,mode from blacknumber limit 10offset 10  //查询部分的数据

 

基于findAll()修改

publicList<BlackNumberInfo> findPart(int startIndex){

        try {

            Thread.sleep(600);

        } catch(InterruptedException e) {

            // TODO Auto-generatedcatch block

            e.printStackTrace();

        }

        List<BlackNumberInfo>infos = newArrayList<BlackNumberInfo>();

        SQLiteDatabasedatabase = helper.getWritableDatabase();

        Cursorcursor = database.rawQuery("select number ,mode from blacknumberlimit 20 offset ?", newString[]{startIndex+""});

        while(cursor.moveToNext()){

            BlackNumberInfo info = new BlackNumberInfo();

            String number = cursor.getString(0);

            String mode = cursor.getString(1);

            info.setMode(mode);

            info.setNumber(number);

            infos.add(info);

           

        }

        cursor.close();

        database.close();

       

        return infos;

       

    }

然后使用,运行演示;

 

3、监听拖动到末尾

lv_call_sms_safe.setOnScrollListener(newOnScrollListener() {

            /**

             * 当滚动状态发生改变的时候调用这个方法

             * 静止--->滚动

             * 滚动--->静止

             * 手指触摸滑动--->惯性滚动

             */

            @Override

            public voidonScrollStateChanged(AbsListView view, int scrollState) {

                switch (scrollState) {

                case OnScrollListener.SCROLL_STATE_IDLE://空闲状态

                    int position = lv_call_sms_safe.getLastVisiblePosition();//19最后一条显示的位置

                    int total =list.size();//20总的数据有多少

                   

                    if(position ==(total-1)){

                        //最后一条了,该去加数据了

                        Toast.makeText(getApplicationContext(), "加载更多数据", 0).show();

                    }

                   

                    break;

                case OnScrollListener.SCROLL_STATE_FLING://惯性滑动

                    break;

                case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://触摸滚动

 

                default:

                    break;

                }

               

            }

            /**

             * 当滚动的时候调用该方法

             */

            @Override

            public voidonScroll(AbsListView view, int firstVisibleItem,

                    int visibleItemCount,int totalItemCount) {

               

            }

        });

 

 

4、加载数据,抽取线程代码为方法fillData()、指定加载位置定义成类的成员变量startIndex,然后在监听到末尾处加载数据。

 

定义类的成员变量:

private int startIndex = 0;

 

   加载数据:

   if(position == (total-1)){

   //最后一条了,该去加重数据了

Toast.makeText(getApplicationContext(),“加载, 0).show();

//    startIndex = startIndex +20;

       ll_loading.setVisibility(View.VISIBLE);//显示加载

      startIndex += 20;

      fillData();

                 

}

 

5、处理数据被覆盖的问题

在fillData()方法里面代码修改:

if(list == null){

      list = dao.findPart(startIndex);//查询数据库得到数据

      }else{

         //已经有数据了

      list.addAll(dao.findPart(startIndex));

}

  

6、让数据继续停留在当前位置

  两种实现方式

 第一种:lv_call_sms_safe.setSelection(startIndex);//不推荐

 第二种:重复利用适配器,数据变化通知一下就行了

  private CallSmsSafeAdapter adapter;

 

 Handler里面修改成:

   if(adapter == null){

         adapter = newCallSmsSafeAdapter();

         lv_call_sms_safe.setAdapter(adapter);

   }else{

         //通知数据适配器更新一下界面

      adapter.notifyDataSetChanged();

   }

 

 

7、防止重复加载

 定义成员变量

 private boolean isLoading  = false;

 

 在加载的过程中

  if(isLoading){

    return;

  }

 

  isLoading = true;

 

  加载好后 handler里面处理 

 isLoading = false

 

 

8、处理拖动到所有数据的最后一条时的处理

  得到数据库一共有多少条数据

  /**得到数据的总数

     * @return

     */

    public int  getTotalCount(){

        int count = 0;

        SQLiteDatabasedatabase = helper.getWritableDatabase();

        Cursorcursor = database.rawQuery("select count(*) fromblacknumber", null);

        if(cursor.moveToNext()){

             count= cursor.getInt(0);

        }

        cursor.close();

        database.close();

        return count;

    }

 

代码处理

if(startIndex >= total){

   Toast.makeText(getApplicationContext(), "已经最后一条了", 0).show();

                     return;

   }

 

 

总结分批处理:

分批处理 解决的时候时间等待的问题

不能解决内存占用的问题

要想解决内存占用问题,可以采用分页方式;

  

  

56_黑名单号码的添加和删除_32

1、画图添加黑名单号码对话框

 

2、创建布局文件dialog_add_blacknumber.xml(添加黑名单号码)

  <?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="300dip"

    android:layout_height="wrap_content"

    android:gravity="center_horizontal"

    android:orientation="vertical" >

 

    <TextView

        android:layout_width="300dip"

        android:layout_height="50dip"

        android:background="#66ff6600"

        android:gravity="center"

        android:text="添加黑名单号码"

        android:textColor="#000000"

        android:textSize="22sp" />

 

    <EditText

        android:layout_width="280dip"

        android:layout_height="wrap_content"

        android:hint="请输入电话号码"

        android:inputType="phone" />

 

    <RadioGroup

        android:id="@+id/radioGroup1"

        android:layout_width="280dip"

        android:layout_height="wrap_content"

        android:gravity="center_horizontal"

        android:orientation="horizontal" >

 

        <RadioButton

            android:id="@+id/radio0"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:checked="true"

            android:text="电话" />

 

        <RadioButton

            android:id="@+id/radio1"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="短信" />

 

        <RadioButton

            android:id="@+id/radio2"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="全部" />

    </RadioGroup>

 

    <LinearLayout

        android:layout_width="280dip"

        android:layout_height="wrap_content" >

 

        <Button

            android:layout_width="140dip"

            android:layout_height="wrap_content"

            android:background="@drawable/button_bg"

            android:text="确定" />

 

        <Button

            android:layout_width="140dip"

            android:layout_height="wrap_content"

            android:background="@drawable/button_bg"

            android:text="取消" />

    </LinearLayout>

 

</LinearLayout>

 

3、添加点击事件addBlackNumber

 

public voidaddBlackNumber(View view){

        AlertDialog.Builderbuilder = new Builder(this);

        AlertDialogdialog = builder.create();

        ViewcontentView = View.inflate(this, R.layout.dialog_add_blacknumber, null);

        dialog.setView(contentView,0, 0, 0, 0);

        dialog.show();

       

    }

运行演示

4、处理按钮点击事件

定义id :et_blacknumber、rg_mode、rb_all、rb_phone、rb_sms、ok、cancel

点击事件确定

// 判断是否有号码

    String number =et_blacknumber.getText().toString().trim();

                if (TextUtils.isEmpty(number)) {

                    Toast.makeText(CallSmsSafeActivity.this, "号码为空", 0).show();

                    return;

                }

 

                int id = rg_mode.getCheckedRadioButtonId();

                Stringmode = "3";//拦截模式

                switch (id) {

                case R.id.rb_all:

                    mode = "3";

                    break;

                case R.id.rb_phone:

                    mode = "1";

 

                    break;

                case R.id.rb_sms:

                    mode = "2";

                    break;

 

                }

               

                dao.add(number,mode);

                dialog.dismiss();

                totalCount = dao.getTotalCount();//得到总数

 

 

5、倒序显示数据

 修改SQL语句

selectnumber ,mode from blacknumber order by _id desc limit 20 offset ?

 

 刷新界面

 直接把当前对象添加到当前集合列表里面

   BlackNumberInfoinfo = new BlackNumberInfo();

   info.setMode(mode);

   info.setNumber(number);

           

   //直接加到当前的列表里面去

   list.add(0, info);

   adapter.notifyDataSetChanged();//更新数据   

 

6、删除数据

  listView的item默认是有点击效果的

  画图回顾事件传递过程

  

 

在getView()方法里点击删除的实现

holder.iv_delete.setOnClickListener(new OnClickListener(){

               

                @Override

            public void onClick(View v) {

                System.out.println("点击位置:"+position);

        BlackNumberInfoblackNumberInfo = list.get(position);

                Stringnumber = blackNumberInfo.getNumber();

                    //删除该号码数据库里面的黑名单信息

                   dao.delete(number);

                    //把要删除的黑名单在当前列表移除

                    list.remove(blackNumberInfo);

                    //更新界面

                    adapter.notifyDataSetChanged();

                   

                }

            });

 

 优化建议:写对话框去提示是否确定删除;

 

 

57_拦截短信_22

1、回顾之前做过的短信拦截

 

2、为了支持手动开启和关闭拦截短信,在服务(CallSmsSafeService)里面创建广播接收者(InnerSmsReceiver),并在功能清单文件注册;

 

 

public classCallSmsSafeService extends Service {

 

    public static final String TAG = "CallSmsSafeService";

   

    private BlackNumberDao dao;

    private InnerSmsReceiver receiver;

 

    @Override

    public IBinderonBind(Intent intent) {

        // TODO Auto-generatedmethod stub

        return null;

    }

    class InnerSmsReceiver extendsBroadcastReceiver{

 

        @Override

        public void onReceive(Contextcontext, Intent intent) {

            Log.i(TAG, "内部类的广播接收者--收到一条短信了");

            //接收短信

            Object [] objs = (Object[])intent.getExtras().get("pdus");

            for(Object obj :objs){

                SmsMessagesms = SmsMessage.createFromPdu((byte[]) obj);

                Stringsender = sms.getOriginatingAddress();//得到一个电话号码

                //看一看这个电话号码是否是黑名单里面的

                String mode =dao.findMode(sender);

                if("2".equals(mode)||"3".equals(mode)){

                    abortBroadcast();//把这个广播终止掉

                   

                }

               

            }

        }

    }

   

    @Override

public void onCreate() {

        // TODO Auto-generatedmethod stub

    super.onCreate();

    dao = new BlackNumberDao(this);

        //用代码注册广播接收者

    receiver = newInnerSmsReceiver();

    IntentFilter filter = new IntentFilter();

    filter.addAction("android.provider.Telephony.SMS_RECEIVED");

        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);

        registerReceiver(receiver, filter);

    }

 

    @Override

    public void onDestroy() {

        // TODO Auto-generatedmethod stub

        super.onDestroy();

        unregisterReceiver(receiver);

        receiver = null;

    }

 

}

 

3、在设置中心里添加开启和关闭短信拦截服务的功能

 在activity_setting.xml里增加开启拦截服务的勾选框

 <com.itheima.mobilesafe.ui.SettingItemView

        android:id="@+id/siv_callsmssafe"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        itheima:desc_off="设置黑名单拦截已经关闭"

        itheima:desc_on="设置黑名单拦截已经开启"

        itheima:title="设置黑名单拦截" >

</com.itheima.mobilesafe.ui.SettingItemView>

 

 

  在SettingActvity代码处理点击事件,开启服务和关闭服务

运行演示

4、智能拦截模式原理简介

 打开金山手机卫士,查看智能拦截模式;

 解压金山手机卫士APK ,进入assets目录 ,找到数据库firewall_sys_rules.db

 

 

 拦截短信内容带广告的

 

   String body =sms.getMessageBody();

   //查询数据库

   if(body.contains("fapiao")){

      Log.i(TAG, "拦截到卖发票的短信");

         abortBroadcast();

   }

 

  拦截到的短信如何处理?

  一般会用数据库保存

  A:便于再次查看;

  B:有些人喜欢看垃圾短信;

  C:防止拦截误判 例如--你看我头发票不漂亮;luncene分词检索框架

 

 

 

5、讲解功能清单文件注册接收短信和代码注册接收短信谁优先收到;

 A、打日志演示,优先级一样高的情况下代码注册的比功能清单文件的快

 B、根据系统漏掉设置最大Int值

  filter.setPriority(Integer.MAX_VALUE);

 C、在布局文件也加上2147483647;

 

一般手机卫士类似软件,一开机就启动服务把广播注册好。

 

 

 

 

 

 

 

58_拦截电话的原理_13

在arm类型的手机里演示电话拦截;

 

1、电话拦截原理

当电话来的时候,立刻挂掉并把来电记录清除掉;如果手机速度快的话无法看到这个效果,就达到拦截的效果了。

 

2、监听当前电话呼叫的状态(TelephonyManager)

 

监听来电代码

 

tm =(TelephonyManager) getSystemService(TELEPHONY_SERVICE);

        listener = newMyPhoneListener();

        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

 

 

具体监听实现代码

     switch (state) {

            case TelephonyManager.CALL_STATE_RINGING://电话铃声响起来了

                String mode =dao.findMode(incomingNumber);

               

                if("1".equals(mode) || "3".equals(mode)){

                     Log.i(TAG, "黑名单的电话号码,马上挂断");

                }

                break;

            default:

                break;

            }

取消监听来电代码

tm.listen(listener,PhoneStateListener.LISTEN_NONE);

        listener = null;

 

 

59_利用反射调用系统隐藏API挂断电话_35

1、挂断电话的API早期版本endCall()是可以使用的,现在不可以用了;但本身挂断电话这个功能是存在的。

2、读getSystemService()源代码;

3、如何查看真面目?到内存中去看,运行起来看,打断点。例如

 

Context context = getApplication();//看他里面的---mBase---ContextImpl.java

TelephonyManager tm = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);

 

 搜索:ContextImpl.java,看看源代码

  ContextImpl继承了Context---->  搜索getSystemService

 --->看看各种服务

 

 

 在基础课的时候我们也可以写一个服务在后台运行,调用服务的时候可以获取远程服务的代理对象(Ibinder),得到代理对象后就可以调用里面的方法了。

 

 

4、重新启动模拟器看日志

 Zygote-->sysem_process---->batteryService---->SensorService....

 

开机过程其实就是各种服务加载的过程。

 

 

getSystemService()得到的服务把相关API隐藏了,只提供常用的方法,那么想要得到原生的TelephonyManager的方法就得绕开使用这个方法;

 

 

 

5、挂断电话具体实现步骤

A:创建endCall()方法,里面代码实现如下;

public void endCall() {

//      ServiceManager.getService(TELEPHONY_SERVICE);

        try {

            //得到ServiceManager的字节码

            Class clazz = CallSmsSafeService.class.getClassLoader().loadClass("android.os.ServiceManager");

            //得到字节码的方法

            Method method =clazz.getDeclaredMethod("getService", String.class);

            //调用方法得到远程服务代理类

        IBinder b =(IBinder) method.invoke(null, TELEPHONY_SERVICE);

            //获取到原生未经包装的系统电话的管理服务

        } catch (Exception e) {

            // TODO Auto-generatedcatch block

            e.printStackTrace();

        }

       

    }

 

 

远程服务代理对象IBinder 需要一个.aidl文件去生成方法去管理服务

 

ITelephony.aidl

接口用于与手机交互。主要使用的
TelephonyManager类。一些地方仍然是直接使用这个。
请清理他们如果可能的话,使用TelephonyManagerinsteadl

 

把ITelephony.aidl拷贝到com.android.internal.telephony

 

把NeighboringCellInfo.aidl拷贝到android.telephony

 

不报错后看一下gen目录下

 

在endCall()方法加上如下代码

ITelephony telephony = ITelephony.Stub.asInterface(b);

telephony.endCall();//挂断电话

 

 

需要加上权限

 <uses-permission android:name="android.permission.CALL_PHONE"/>

 

 

 

知识拓展

60_利用内容观察者和内容提供者删除呼叫记录_25

 

1、增加方法deleteCalllog();

  导出呼叫记录数据库data/data/com.android.proveders.contacts/databases/contacts2.db

 

  数据看里面的内容 number 电话号码 、 date时间、 type 1 打进来 2 打出去 3 未接

 

 

2、使用内容解析者去删除电话记录

 

ContentResolver resolver = getContentResolver();

      Uri url = Uri.parse("content://call_log/calls");

      resolver.delete(url, "number=?", new String[]{incomingNumber});

 

Uri路径如何写可以参照源代码

运行演示,会报错;

3、需要加两个权限

 <uses-permission android:name="android.permission.READ_CALL_LOG"/>

<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>

 

 

4、解释删除有时成功,有时不成功的情况;

立刻把电话挂断了,但呼叫的生成并不是同步的代码;它是一个异步的代码

5、用观察者去监听日志产生后再去删除

 

 注册监听

getContentResolver().registerContentObserver(url,true, new MyContentObserver(new Handler(),incomingNumber));

 

自定义内容观察者

private class MyContentObserver extends ContentObserver{

        private String incomingNumber;

        publicMyContentObserver(Handler handler,String incomingNumber) {

            super(handler);

            this.incomingNumber = incomingNumber;

        }

        @Override

        public void onChange(boolean selfChange) {

            super.onChange(selfChange);

            //删除呼叫记录

            deleteCalllog(incomingNumber);

            //取消注册内容观察者

            getContentResolver().unregisterContentObserver(this);

        }

    }

 

 

补充Android2.3模拟器上需要多加权限

 

 <uses-permission android:name="android.permission.WRITE_CONTACTS"/>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值