[安卓]手机管家(十四)通讯卫士之工具类及listView的分批加载

来源:互联网 发布:怀孕四个月饭量知男女 编辑:程序博客网 时间:2024/04/29 18:34

每次进去都要加载list,导致耗时较高,体验不好

//这是一个耗时操作,不应该在UI线程,需要一个thread//要先初始化listdao = new BlackNumberDao(this);        list = dao.queryAllBlackNum();    //再去设置Adapter,否则空指针异常    adapter = new MyListViewAdapter();    lv_callsmsguard_numlist.setAdapter(new MyListViewAdapter());}

//这是一个耗时操作,不应该在UI线程,需要一个threadnew Thread(new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stublist = dao.queryAllBlackNum();}}).start();;//要先初始化listdao = new BlackNumberDao(this);            //再去设置Adapter,否则空指针异常    adapter = new MyListViewAdapter();

这时候的问题,子线程放在oncreate里,他是一个耗时操作,代码继续执行到list.size,此时子线程还未走完,adapter已经走完,size要获取他的counter,但这个时候list的初始值是默认的空,声明的成员变量,线程里还没执行完,没有返回值,这就会有空指针异常

必须等子线程执行完才能继续别的,需要一个handler发消息,发了消息再去执行adapter的初始化

//这是一个耗时操作,不应该在UI线程,需要一个threadnew Thread(new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stublist = dao.queryAllBlackNum();MyHandler.sendEmptyMessage(0);}}).start();//要先初始化listdao = new BlackNumberDao(this);        }Handler MyHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {//等上面发了消息,再执行//再去设置Adapter,否则空指针异常    adapter = new MyListViewAdapter();    lv_callsmsguard_numlist.setAdapter(new MyListViewAdapter());}};

每次进来有一个空白页面的时间,这是因为没有数据需要加载,可以加一个ProgressBar在list上

<ProgressBar        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/pb_callsms_load"        android:layout_gravity="center"        android:layout_marginTop="80dp"/>


pb = (ProgressBar)findViewById(R.id.pb_callsms_load);/*lv_callsmsguard_numlist.setAdapter(new MyListViewAdapter());*///要先初始化listdao = new BlackNumberDao(this);//设为可见,在dao执行的后面pb.setVisibility(View.VISIBLE);//这是一个耗时操作,不应该在UI线程,需要一个threadnew Thread(new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stublist = dao.queryAllBlackNum();MyHandler.sendEmptyMessage(0);}}).start();       }Handler MyHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {//加载好了不可见pb.setVisibility(View.INVISIBLE);//等上面发了消息,再执行//再去设置Adapter,否则空指针异常    adapter = new MyListViewAdapter();    lv_callsmsguard_numlist.setAdapter(new MyListViewAdapter());}};

这个也可以弄成一个工具,之前的都是工具都是静态方法,这里不同,可以写成一个类,里面有一个方法,这里是一个异步框架

让pb可见;子线程加载数据,完成发消息给UI线程;pb不可见刷UI

utils下新建MyAsyncTask类

public abstract class MyAsyncTask {    //需要保证以下三个步骤的执行顺序    //1.主线程内设置pb可见 --> pretast()    //2.子线程内加载数据,完成发消息给主线程v -->intask()    //3.主线程收到消息后,更新ui -->posttast()//让他抽象起来,这样只能继承实现,而不能去客制化public abstract void pretast();public abstract void intask();public abstract void posttast();public void execute(){pretast();//子线程加载,通知UI线程new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubintask();handler.sendEmptyMessage(0);}}).start();}Handler handler = new Handler(){public void handleMessage(android.os.Message msg) {posttast();};};}


原activity里重新来过

new AsyncTask().execute();}/*//主线程刷UIHandler MyHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {//加载好了不可见pb.setVisibility(View.INVISIBLE);//等上面发了消息,再执行//再去设置Adapter,否则空指针异常    adapter = new MyListViewAdapter();    lv_callsmsguard_numlist.setAdapter(new MyListViewAdapter());}};*/class AsyncTask extends MyAsyncTask{@Overridepublic void pretast() {// TODO Auto-generated method stubpb.setVisibility(View.VISIBLE);}@Overridepublic void intask() {// TODO Auto-generated method stublist = dao.queryAllBlackNum();}@Overridepublic void posttast() {// TODO Auto-generated method stubpb.setVisibility(View.INVISIBLE);adapter = new MyListViewAdapter();    lv_callsmsguard_numlist.setAdapter(new MyListViewAdapter());}} 

系统本身也有个方法

class AsyncTask2 extends AsyncTask<String,String,String>{@Overrideprotected String doInBackground(String... params) {// TODO Auto-generated method stublist = dao.queryAllBlackNum();return null;}@Overrideprotected void onPreExecute() {// TODO Auto-generated method stubsuper.onPreExecute();pb.setVisibility(View.VISIBLE);}@Overrideprotected void onPostExecute(String result) {// TODO Auto-generated method stubsuper.onPostExecute(result);pb.setVisibility(View.INVISIBLE);    adapter = new MyListViewAdapter();    lv_callsmsguard_numlist.setAdapter(adapter);} }

他的参数自己定,方法随之改变

他实际上有一个线程池,支持多线程的,原理一样


为了让加载时间缩短一点,不要一上来就显示所有数据,而是最好一批一批的显示,拖动到一定时候再加载新的数据

此即listview的分批加载,看新闻的时候拖动一下显示个圈圈正在加载中


需要在dao里来个部分查找

public List<BlackNumber> queryPartBlackNum(){   List<BlackNumber> list = new ArrayList<BlackNumber>();           //Cursor cursor=db.query("blacknuminfo", null, null,  null, null, null, " _id desc"); Cursor cursor =db.rawQuery("select * from blacknuminfo  order by _id desc  limit  20 ;", null);         while (cursor.moveToNext()) {         int id = cursor.getInt(0);         String number = cursor.getString(1);         int mode = cursor.getInt(2);         BlackNumber blackNumber = new BlackNumber(id, number, mode);                   list.add(blackNumber);          }    return list;  }

@Overridepublic void intask() {// TODO Auto-generated method stublist = dao.queryPartBlackNum();}

需要监听listview,拖动完后能够重新加载 这里只用得上第一个方法,有3种状态

lv_callsmsguard_numlist.setOnScrollListener(new OnScrollListener(){@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {// TODO Auto-generated method stubswitch (scrollState) {case SCROLL_STATE_IDLE:break;case SCROLL_STATE_TOUCH_SCROLL:break;case SCROLL_STATE_FLING:break;default:break;}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {// TODO Auto-generated method stub}    });


SCROLL_STATE_IDLE:没有滑动了,拖到最后了;正是我们需要的

touch即是在滚动的状态,当放开后还在惯性滑动的即为fling

case SCROLL_STATE_IDLE://当前view的最后一个可见item的位置int lastp =view.getLastVisiblePosition();//最后一个位置是我们分批加载到的最后一条,才再加载新的if(lastp==list.size()-1){startindex=startindex+20;Toast.makeText(getApplicationContext(), "此处开始加载数据", 0).show();new AsyncTask().execute();//需在判断里,在外面则不到底就自动加载}break;

每次查询的位置要注意,在查询里要带上位置参数,dao里也应该有相应变化


@Overridepublic void intask() {// TODO Auto-generated method stublist = dao.queryPartBlackNum(startindex);}

 public List<BlackNumber> queryPartBlackNum(int offset){   List<BlackNumber> list = new ArrayList<BlackNumber>();           //Cursor cursor=db.query("blacknuminfo", null, null,  null, null, null, " _id desc"); Cursor cursor =db.rawQuery("select * from blacknuminfo  order by _id desc  limit  20  offset ? ;", new String[]{offset+""});

这时候往后拖,前面的就没有了,因为在async里的list之保存当前20个item,每次都重新加载了

应该能显示前面的

@Overridepublic void intask() {// TODO Auto-generated method stubif(list == null){list = dao.queryPartBlackNum(startindex);}else{//在原有的基础上加上重新加载的itemlist.addAll(dao.queryPartBlackNum(startindex));}}

另一个问题,每次加载后都显示到最顶上

post中的adapter应该只在第一次new出来,而不是每一次加载都要new一个,之后只需要通知一下,直接调用就好

@Overridepublic void posttast() {// TODO Auto-generated method stubpb.setVisibility(View.INVISIBLE);if(adapter == null){adapter = new MyListViewAdapter();}else{    adapter.notifyDataSetChanged();}    lv_callsmsguard_numlist.setAdapter(new MyListViewAdapter());}

OK,这才是listview最重要的部分,设计到listview都要考虑到大数据量的问题


拦截功能的实现:需要service,manifest里要注册

@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();smsReceiver = new MySmsAbortReceiver();IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");registerReceiver(smsReceiver, intentFilter);}class MySmsAbortReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// 收到短信后,将短信拿出来找到发信人,看发信人是否在黑名单数据库里,并且模式是否是短信拦截}}


另外这个service是否启动,应该给用户一个选择,在setting里设置,settingItem1的风格

<com.rjl.mobilephonemanager.ui.SettingItem          android:id="@+id/settingitem_blacknum"          android:layout_width="fill_parent"          android:layout_height="wrap_content"          rjl:itemtitle="黑名单拦截"          rjl:desc_checkbox_on="开启拦截"          rjl:desc_checkbox_off="关闭拦截"/>


声明

private SettingItem settingItem_blacknum;
找到

settingItem_blacknum =(SettingItem) findViewById(R.id.settingitem_blacknum);

初始化

intiSetBlackNumItem();
方法实现,不通过sp回显,和之前的显示号码归属地一模一样,抽成组合控件多方便,加几行代码就好

private void intiSetBlackNumItem() {// TODO Auto-generated method stubsettingItem_blacknum.setCheck(ServiceUtils.isRunning(this, "com.rjl.mobilephonemanager.service.BlackNumberService"));settingItem_blacknum.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stub//Editor editor = sp.edit();if (settingItem_blacknum.getChecked()) {settingItem_blacknum.setCheck(false);settingItem_blacknum.setdescriptionoff();  Intent intent = new Intent (SettingActivity.this, BlackNumberService.class);         stopService(intent);}else {settingItem_blacknum.setCheck(true);settingItem_blacknum.setdescriptionon(); Intent intent = new Intent (SettingActivity.this,BlackNumberService.class);         startService(intent); }//editor.commit();}});}

destroy时要取消注册

@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();if (smsReceiver!=null) {unregisterReceiver(smsReceiver);}System.out.println("BlackNumberService.onDestroy()");}

实现短信拦截

之前有一个smsReceiver注册了一个静态的service

现在发短信的时候,两个service都启动了,一个动态,一个静态,当优先级相同时,动态注册先收到

如果想让静态注册先收到,把静态注册的优先级设成更高,1000并不是上限,而是int的最大值, 0x7FFFFFFF =21 4748 3647=Integer.MAX_VALUE;


拆分短信

@Overridepublic void onReceive(Context context, Intent intent) {// 收到短信后,将短信拿出来找到发信人,看发信人是否在黑名单数据库里,并且模式是否是短信拦截Bundle bundle =  intent.getExtras();                  Object[] obj =   (Object[]) bundle.get("pdus");         for (Object object : obj) { SmsMessage  sms = SmsMessage.createFromPdu((byte[])object); String addr = sms.getOriginatingAddress(); String body = sms.getMessageBody();         System.out.println("MySmsReceiver. onReceive()短信的具体内容是" +addr+":"+body);          }}

需要通过dao对比,把dao声明、获取

public class BlackNumberService extends Service {private MySmsAbortReceiver smsReceiver;<span style="color:#ff6600;">private BlackNumberDao blacknumdao;</span>@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();<span style="color:#ff0000;">blacknumdao = new BlackNumberDao(getApplicationContext());</span>

for (Object object : obj) { SmsMessage  sms = SmsMessage.createFromPdu((byte[])object); String addr = sms.getOriginatingAddress(); String body = sms.getMessageBody();         System.out.println("MySmsReceiver. onReceive()短信的具体内容是" +addr+":"+body);         //if里比较了两次,应该抽出来,否则要查询2次,耗时         int mode = blacknumdao.querymode(addr);         if (mode==0|| mode==2) { abortBroadcast();    //此处可以进一步继续开发 ,将拦截到的短信保存到手机管家自己的数据库中//然后再写一个类似短信的一个list页面,去显示拦截到的短信 // 还可以添加一个按钮让user 去恢复某条短信}          }


再来完善拦截电话,需要监听电话

TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);MyPhoneCallListener listener = new MyPhoneCallListener();    tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

这里用到了反射和远程服务stub,需要包com.android.internal.telephony及ITelephony.aidl,他又需要一个包android.telephony下的NeighboringCellInfo文件


class MyPhoneCallListener extends PhoneStateListener{@Overridepublic void onCallStateChanged(int state, String incomingNumber) {// TODO Auto-generated method stubswitch (state) {case TelephonyManager.CALL_STATE_IDLE:break;case TelephonyManager.CALL_STATE_RINGING:int mode = blacknumdao.querymode(incomingNumber);if (mode==1||mode==2) {endcall();} break;case TelephonyManager.CALL_STATE_OFFHOOK:break;default:break;}super.onCallStateChanged(state, incomingNumber);}private void endcall() {//此处应该实现一个挂电话的功能!Toast.makeText(getApplicationContext(), "需要拦截", 0).show();Class<?> loadClass;try {loadClass = getClassLoader().loadClass("android.os.ServiceManager");Method method = loadClass.getMethod("getService", String.class);IBinder invoke = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);// 返回了 真正的电话管理者     ITelephony asInterface = ITelephony.Stub.asInterface(invoke);  // 挂断电话asInterface.endCall();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

endcall是一个被隐藏起来的方法,不能随便就挂电话,所以需要远程调用,ibinder

在ServiceManager找到一个getService方法,他传的是string类的字节码

找到后用invoke调用,两个参数,第一个是object receiver,getclassloader是serviceManager里的静态方法,不需要对象,所以这里是null,

前面传的是TELEPHONY_SERVICE==phone这个参数,有了这个参数,就可以调用到ibinder,就可以转成一个本地类,这个本地类就可以用来挂断电话了,否则无法随便调用挂断电话的方法

权限

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


0 0
原创粉丝点击