android——抓取网页的android课表
我要做的东西是把学校服务器的课程表抓取下来,然后再在本机显示。这个课表程序可以对某节课进行编辑删除,可以设置闹钟。
1.读取学校教务处网页:
用android自带的httpclient来获取网页。httpclient非常强大,它可以模拟一个浏览器来对服务器进行访问。对于不需要进行验证的网页,只要用get方法就可以取得网页,而对于需要验证的网页就得用post方法。(看到一篇关于http协议的文章,很好:http://blog.csdn.net/gueter/article/details/1524447)
但是有时候你也许服务器也不会理你,因为你的http报头并没有满足它的要求。这时候怎么办呢?给个提示:去研究一下http协议。
还有,把网页抓取下来后的html还需要进行解析才行。由于我的目标网页结构简单,我就自己写了一个简单的解析操作。
有人问我如果目标网页是有框架的该怎么抓取呢?我发一张图你就明白了
这是CSDN的论坛页面,它是有框架的,左边页面和右边页面的网址不一样。所以你只需要填入你想要的页面的网址就可以了。
2.接下来就是表格显示
由于刚开始学,很笨,就用一个个的textview来拼成了课表,不过这样挺好的,可以让我后期做的时候方便一些。效果是这样的:
所有的东西都是textview,最左边的一列(第几节那列)是不随着右边的滑动而滑动的,这样浏览的时候更方便。下面是布局的嵌套关系
3.进度条对话框和线程
当响应操作时间比较长的时候就需要进度条对话框了。
其实说到它我最想记录的还是线程。开启了进度条对话框后常常与之对应的就是线程了。用以下的方法就可以开启一个线程
[java] view plain copy
- //开启一个线程用于获取课表
- newThread(){
- publicvoidrun(){
- try{
- //想要在线程里做的
- }catch(InterruptedExceptione){
- handler.sendEmptyMessage(0x222);
- }catch(Exceptione){
- //TODOAuto-generatedcatchblock
- handler.sendEmptyMessage(0x333);
- }finally{
- //在后台程序完成后,必须关闭进度对话框,否则进度对话框永远无法关闭
- pdialog.dismiss();
- }
- }
- }.start();
4.handler
上面代码里的handler.sendEmptyMessage给已经定义好的handler发消息,这样能够在线程执行过程中通知线程的执行状态,这样可根据发回来的消息来进行下一步操作。很有用!
[java] view plain copy
- Handlerhandler=newHandler()
- {
- @Override
- publicvoidhandleMessage(Messagemsg)
- {
- if(msg.what==0x111)
- {
- }
- if(msg.what==0x333)
- {
- }
- }
- };
用msg.what取得线程发回来的消息。
5.DES加密
有时候要加密用户的信息来存储。可以参考下面的代码。(这是从别人那儿看来的,出处找不到了,真不好意思!)
[java] view plain copy
- publicclassEncrypt{
- publicstaticStringencrypt(Stringtxt,Stringkey)
- throwsInvalidKeySpecException,InvalidKeyException,
- NoSuchPaddingException,IllegalBlockSizeException,
- BadPaddingException{
- StringBuffersb=newStringBuffer();
- DESKeySpecdesKeySpec=newDESKeySpec(key.getBytes());
- SecretKeyFactoryskeyFactory=null;
- Ciphercipher=null;
- try{
- skeyFactory=SecretKeyFactory.getInstance("DES");
- cipher=Cipher.getInstance("DES");
- }catch(NoSuchAlgorithmExceptione){
- e.printStackTrace();
- }
- SecretKeydeskey=skeyFactory.generateSecret(desKeySpec);
- cipher.init(Cipher.ENCRYPT_MODE,deskey);
- byte[]cipherText=cipher.doFinal(txt.getBytes());
- for(intn=0;n<cipherText.length;n++){
- Stringstmp=(java.lang.Integer.toHexString(cipherText[n]&0XFF));
- if(stmp.length()==1){
- sb.append("0"+stmp);
- }else{
- sb.append(stmp);
- }
- }
- returnsb.toString();
- }
- publicstaticStringdecrypt(Stringtxt,Stringkey)
- throwsInvalidKeyException,InvalidKeySpecException,
- NoSuchPaddingException,IllegalBlockSizeException,
- BadPaddingException{
- DESKeySpecdesKeySpec=newDESKeySpec(key.getBytes());
- SecretKeyFactoryskeyFactory=null;
- Ciphercipher=null;
- try{
- skeyFactory=SecretKeyFactory.getInstance("DES");
- cipher=Cipher.getInstance("DES");
- }catch(NoSuchAlgorithmExceptione){
- e.printStackTrace();
- }
- SecretKeydeskey=skeyFactory.generateSecret(desKeySpec);
- cipher.init(Cipher.DECRYPT_MODE,deskey);
- byte[]btxts=newbyte[txt.length()/2];
- for(inti=0,count=txt.length();i<count;i+=2){
- btxts[i/2]=(byte)Integer.parseInt(txt.substring(i,i+2),16);
- }
- return(newString(cipher.doFinal(btxts)));
- }
- }
包括了加密和解密。但要注意使用相同的Key
6.文件操作
文件操作一google一大堆,但有的用不了,中文会出现乱码,我是用InputStreamReader和BufferedReader来读取才能读出中文的
[java] view plain copy
- InputStreamReaderisr=newInputStreamReader(newFileInputStream(file),"utf-8");
- BufferedReaderbr=newBufferedReader(isr);
接下来只要用br.readLine()就可以按行读取了。
7.Activity界面通信
刚开始还没学会activity,显示与切换界面全用的是setContentView()。后来果然有很多的不方便啊,比如界面要经常切换而且它们之间要传递信息的时候写的代码就很乱。和我一样懒的还是好好学activity吧。来记录一个很有用的:我的MainActivity需要传递信息给UpdateActivity,后者也要返回信息。
在MainActivity里这么写:
[java] view plain copy
- Bundledata=newBundle();
- data.putString("info",要传的信息);
- Intentintent=newIntent();
- intent.setClass(MainActivity.this,UpdateActivity.class)
- intent.putExtras(data);
- startActivityForResult(intent,0);//这样就可以等待另一个activity返回信息
在UpdateActivity里这么写:
[java] view plain copy
- Stringstr=this.getIntent().getExtras().getString("info");//获得MainActivity传来的名为info的消息
- //返回给mainactivity信息
- Intentintent=getIntent();
- Bundlebundle=newBundle();
- bundle.putString("backinfo",回传的消息);
- intent.putExtras(bundle);
- setResult(RESULT_OK,intent);
- finish();
在MainActivity里重写接收回传信息的方法
[java] view plain copy
- //updateactivity返回信息后会执行这个方法
- @Override
- protectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata)
- {
- switch(resultCode)
- {
- //收到updateactivity发回来的消息要对表格和文件进行更新
- caseRESULT_OK:
- Bundlebunde=data.getExtras();
- Stringback=bunde.getString("lessoninfo");//获得名为lessoninfo的返回信息接下来就对返回信息进行操作
- }
- }
8.由资源id对应的名称获得资源id
我之间也说过我的表格全是用的textview来做的,像这样android:id="@+id/tv1"是我第一个textview。那么当我要操作那么多个的textview时不可能一个个地写它们对应的事件吧。所以用到了下面的方法。//这个方法可以根据id对应的string返回在R.java里记录资源的id
[java] view plain copy
- Resourcesres=getResources();
- intj=res.getIdentifier("tv"+String.valueOf(i),"id",getPackageName());
再啰嗦一下,就是有tv1,tv2,tv3,……上面的i就可以是1,2,3
9.设置闹钟
可参考:http://blog.csdn.net/jeethongfei/article/details/6767826我要说的是怎么设置多个闹钟。关键是PendingIntent pi=PendingIntent.getActivity(MainActivity.this, i, intent, 0);里的i。把i设置为不同的值就可以设置不同的闹钟了。只需设置这个地方而已!不需其它!
[java] view plain copy
- Intentintent=newIntent(MainActivity.this,AlarmActivity.class);
- Bundledata=newBundle();
- PendingIntentpi=PendingIntent.getActivity(MainActivity.this,i,intent,0);
- ring=(AlarmManager)getSystemService(Service.ALARM_SERVICE);//我是用另一个activity来做闹铃的
- ring.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),600*100*60*24*7,pi);
10.一设置闹钟就会响的问题
如果你设置响铃的时间是在当前时间之前,那你一设置好闹钟就会响。可以按照下面的设置[java] view plain copy
- Calendarc=Calendar.getInstance();
- intcweek=c.get(Calendar.DAY_OF_WEEK);
- intchour=c.get(Calendar.HOUR_OF_DAY);
- intcminute=c.get(Calendar.MINUTE);
- if(week<cweek||(week==cweek&&(hour<chour||minute<cminute)))
- {
- ring.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi);
- }
- else
- {
- ring.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),600*100*60*24*7,pi);
- }
ring是AlarmManager的实例,calendar是Calendar实例。week是想要设置的是星期几,hour是想要设置的小时,minute是分钟。要注意用get(Calendar.DAY_OF_WEEK)返回的星期天是1,星期一是2,依此类推。600*100*60*24*7,是一个星期的时间。
那为什么要像上面那么设置呢?如果今天是星期六(cweek就是7),想要设置的是星期1响(week就是2),那么如果用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);这样能够让下个星期一响。如果用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi); 这样会让下下个星期一响,不符合设想。如果想要让下个星期五(week就是6)响,用用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);闹钟会立即响,而用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi);就能让下个星期五响。有点明白了吧,得好好想想。
11.一定要注意!
比较两个字符串一定要用equals方法。String的split方法不会修改它本身,而是产生一个新的修改过的字符串!当要用系统资源时要加上权限!记得在AndroidManifest.xml中注册Activity!
由于涉及一些信息,我就不 上传源码了。
[java] view plain copy- <pre></pre>
- <pre></pre>
- <pre></pre>
更多相关文章
- Android 性能典范-线程
- android 线程池
- Android 关于异常与线程
- Android WebView 获取网页的标题
- android 用流打开网页
- Android实现计时与倒计时(限时抢购)的几种方法(线程)
- 【Android】从主线程向子线程发消息