[安卓]手机管家(十四)通讯卫士之工具类及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"/>
- [安卓]手机管家(十四)通讯卫士之工具类及listView的分批加载
- [安卓]手机管家(十三)通讯卫士之listview优化
- [安卓]手机管家(十二)通讯卫士
- 安卓ListView 数据分批加载
- 安卓ListView 数据分批加载
- ListView的分批加载
- 安卓手机卫士
- ListView的分批加载数据
- ListView中数据的分批及分页加载
- [安卓]手机管家(九)高级工具之号码归属地查询
- [安卓]手机管家(六)防盗之UI及自定义样式
- 立波设置管家:安卓手机快速设置的优秀工具
- ListView的分批加载和分页加载
- Android初学之listView分批加载数据
- [安卓]手机管家(五)防盗之加密
- [安卓]手机管家(七)防盗之左右划屏
- [安卓]手机管家(八)防盗之业务逻辑
- [安卓]手机管家(十九)软件管理之软件锁
- 第10题
- Android Studio分模块自动化构建实战
- How to improve quality and syntax of your Android code
- 最长公共子串问题
- 文件不关闭防断电追加写入 写读注册表
- [安卓]手机管家(十四)通讯卫士之工具类及listView的分批加载
- CSDN-Markdown基础用法
- 【最大点权独立集】【HDU1565】【方格取数】
- 嘻唰唰第四批之构造函数
- android PopupWindow消失后,将EditText的软键盘隐藏
- 数据库优化-基准测试(一)
- 第三题
- mysql的partition操作
- 4