网络相关的客户端程序:(这其实是最多的一种) 1.新浪微博 2.人人网客户端 3.网易新闻 4.故事会 流程: 1.我们需要了解服务器返回数据的接口(一个非常宽泛的概念)信息. 例如:一个网络地址 ,<xml> 定义了版本号 定义新版本的地址 2.解析接口返回的数据. 3.把数据显示到界面. 用户登陆的流程. 1.提供界面让用户可以登陆到服务器上,完成用户身份的认证 2.提供一种身份,会话维持的技术, 识别当前登陆用户的身份状态. WEB中的。 session 会话的id来识别用户. cookie 本地硬盘上文件 android oauth认证技术.识别当前的请求来自于哪一个用户。 优米客户端的需求文档 iphone下的文档. 如果我一个iphone应用 移植到android. 1.查看iphone是怎么获取数据的 http://xxx.xxx.xxx.xx/pingdaoxinxi.xml 2.解析xml文件 3.显示到界面上. 关于从服务器上下载数据的操作有很多种: 1.解析xml 2.json 3.html 解析html的标签 02-29 01:39:42.147: INFO/ActivityManager(59): Starting activity: Intent { act=android.intent.action.MAIN cmp=com.android.settings/.WirelessSettings } selector选择器 <merge xmlns:android="http://schemas.android.com/apk/res/android"> merge 合并 合体操作 <include layout="@layout/titlebar"/> 通过include的标签把定义的merge标签的内容引入到layout界面 开放平台: 网站 暴露出来一个webservice. 游戏大厅, 提供了一些接口. web游戏 qq偷菜 广义讲 windows操作系统 也是一个开放平台. 核心的api暴露 windows api android os 豆瓣网站 社交开放式平台 新浪微博 人人网 豆瓣API是豆瓣为第三方开发人员提供的编程接口。 利用豆瓣API,你可以在你的网站或程序中使用豆瓣的数据和功能 什么是服务器的接口 什么是服务器提供的api? http://api.douban.com/book/subject/1220562 webservice 客户端通过接口访问服务器的数据 json xml html 实际上的操作 1.url 2.url 获取到一个urlconntection; 3.获取到服务器返回回来的具体数据信息 4.解析我们关心的内容 5.显示界面 使用开放平台的api 第一步 需要申请一个api使用key文件 为了保护豆瓣用户的数据;防止API被滥用或恶意使用,豆瓣要求每个API的使用者申请一个API Key, 而每个API Key唯一标识一个API使用者. 黑客, 抓取人人网数据的操作. 服务器根据api 的key 可以把你的客户端的请求给禁用. 你的豆瓣 API key: 0c51c1ba21ad8cfd24f5452e6508a6f7 API key 的详细信息 · · · · · · 私钥:359e16e5e5c62b6e -请保护好你的私钥 应用名称:黑马小瓣瓣 创建时间: 2012-02-29 应用说明: 这个应用帮助用户快速访问豆瓣的数据,发豆瓣消息,写日记. commons-codec-1.3.jar 公用的工具类 commons-httpclient-3.1.jar httpclient http请求 commons-logging-1.1.jar 日志的输出 c3p0 jdbc douban-java-0.2.2-r34.jar 获取服务器数据 xml 格式 解析xml格式数据 gdata-client-1.0.jar gdata-core-1.0.jar google提供的解析xml的工具 oauth-1.3.jar 用户身份认证的jar包 oauth认证的操作 通过qq号登陆京东网 http://fanli.qq.com/fanli_connect/qq_login.php?mall_id=360buy&mall_cb=http%3A%2F%2Fwww.360buy.com&url=http%3A%2F%2Fqq.360buy.com%2Fnew%2Fqq%2Fcallback.aspx&ts=20120229115122&flag=&forcelogin=1&vkey=87db3016f3f98dc6ac5b4168dfe17148 这个页面的服务器是在那一个公司? 顶级域名 qq.com 二级域名 fanli.qq.com http://www.ccb.yinhang.com .org .com.cn .us http://www.ccb.yinhang.com www.ccb.hah6.com xxc.ccb.com icbc.com //得到了豆瓣后门的钥匙 83e32a78f6148aa28ba97f6ae23d9fe6 ab31cb1271593021 83e32a78f6148aa28ba97f6ae23d9fe6 ab31cb1271593021 特点:一旦我们用户第一次授权完毕后. 如果用户不取消授权 ,accesstoken 和 tokensecret不会发生改变 9644556d2ba3cabdc2cd004782eb6037 c78f514836b98fed 302 重定向. jericho-html-3.1.jar html的解析包 对html进行面向对象的封装. 剖析ResourceType下的所有东西。 layer-list显然是一个图层的列表,作用就是一层一层的按照顺序叠加图片。 参看:http://blog.csdn.net/chenlaic/article/details/6089989 1.可以将多个图片按照顺序层叠起来 2.在drawable下建立一个xml文件 <?xml version="1.0" encoding="UTF-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!--图片1--> <item android:id="@+id/user_faceback_drawable" android:drawable="@drawable/faceback" /> <!--图片2--> <item android:id="@+id/user_face_drawable" android:drawable="@drawable/h001" android:left="10.0dip" android:top="18.0dip" android:right="25.0dip" android:bottom="35.0dip" /> </layer-list> <!--2个图片的叠加--> 每个item里面是一个图片,不过这个图片可以是png,jpg,gif也可以是我们用XML定义的图片。 <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 是否支持渐变色 --> <item> <shape> <gradient android:startColor="#ffa6a6a6" android:centerColor="#ffdbdbdb" android:endColor="#ffe7e7e7" android:height="1px" android:angle="90" android:dither="true" /> </shape> </item> </layer-list> 这个图层里面只有一张图片,其实也可以不这样做,可以直接用一个shape. 我们看到这张图片是一个shape,图片里面表示渐变色,它就是一个长高均为一的正方形, 这个正方形有渐变色,然后作为了一个背景图片,不过在作为背景的时间,引用它的 那个控件是填充父窗体的的,所以它会变成一个充满屏幕的矩形,那么渐变的色彩也会明显很多 。 调用: android:background="@drawable/home_gradient" <!-- 因为它是src,所以它只是ImageView内的一部分,所以也会有android:paddingLeft这样的padding属性,如果是android:background它直接就填充整个 ImageView 根本没有padding这样的属性而言。--> <ImageView android:id="@+id/logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="60dip" android:paddingLeft="20dip" android:paddingRight="20dip" android:scaleType="centerInside" android:src="@drawable/logo" > </ImageView> 有一个绝招,当你的系统想要进入系统的某个设置界面,但是又不知道 相应的activity,那么你可这样做,点击进入它,然后看打印出来的信息。 自定义对话框:(所有的布局呢都是自己写的,直接用的是Dialog) <!-- 这个东西是从源码中获得的 ,我们主动去覆盖了默认的背景,并让其无标题--> <style name="MyDialog" parent="@android:style/Theme.Dialog"> <item name="android:windowBackground">@drawable/title_background</item> <item name="android:windowNoTitle">true</item> </style> dialog = new Dialog(this,R.style.MyDialog); //使用这种方式,不可以得到这个View对象 ,不能获得里面的值,所以还是要吹起来。 //dialog.setContentView(R.layout.first_entry_dialog); //这将是最简单的一种方法,至此把XML转换成View对象已有三种方法 View view = View.inflate(this, R.layout.first_entry_dialog, null); edp1 = (EditText) view.findViewById(R.id.et_first_entry_pwd); edp2 = (EditText) view.findViewById(R.id.et_first_entry_pwd_confirm); Button ok = (Button) view.findViewById(R.id.bt_first_dialog_ok); Button cancel = (Button) view.findViewById(R.id.bt_first_dialog_cancle); ok.setOnClickListener(this); cancel.setOnClickListener(this); dialog.setContentView(view); dialog.show(); //延时两秒进入new Handler().postDelayed(r, delayMillis) //通过handler 延时2秒 执行r任务 new Handler().postDelayed(new LoadMainTabTask(), 2000); /** * 得到网络状态 */ public boolean isNetWorkConnected(){ ConnectivityManager manager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo info = manager.getActiveNetworkInfo(); //判断是wifi连接 还是 无线连接 // WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); // wifiManager.isWifiEnabled(); // wifiManager.getWifiState(); return (info!=null&&info.isConnected());//操,这个比三元表达式更牛逼。 // return (info!=null&&info.isConnected())?true:false; } 最常用的tabhost. 其实它默认是在上面的。你想要让它在哪,要自己再定位。 tabhost这种东西默认的很难看,一般都自定义的。 selector是定义的一上东西在不同状态下的背景。 FitXY. include merg两个标签 。 如果listview你不指定cacheColorHint="#0000000" 你点击的时间会显示出默认的颜色,所以把它改成透明的。 还有一个listSelector,当你选中的某个条目的时间要显示的颜色, 这次终于明白了。这两个listview下参数的含义。 还有一个divider这个表示每个条目之间的填充物。 tabhost:明白哪个是TabWidget哪个是tabcontent 一个重要的问题,有多个页面用到了同一个东西,跟JSP中一样,可以include过来。 让它作为一个单独的东西。 <merge xmlns:android="http://schemas.android.com/apk/res/android" >它表示 它是要被合并的。 在用的地方: <include layout="@layout/titlebar" /> <include layout="@layout/titlebar" /> <!-- include 包含某个布局 android:cacheColorHint 取消listview的背景 android:divider listview 里面两个item之间的背景 android:listSelector="@drawable/category_selector":点击 或者不点击这个条目(即这个条目在不同状态下的背景色) --> author:就是因为不信任才产生的,你不信任360, 你可以用QQ登陆,然后腾讯的服务器返回一个结果, 然后360用这个作为你登陆的凭证。 它每次请求都要 4a73c256be235a29df5b47043a6a0e7b 9dcf2bfa571799e2 登陆界面在最外层做一个ScrollView,这样做屏幕适配就非常的好。 找个那个解析HMTLjar包的API。 progressbar通常是在线程里面start,在handler里面dismiss;. 此属性意思是此视图是否显示,例如RelativeLayout中android:visibility="gone" 其有三个属性:visible显示;invisible显示黑背景条;gone不显示(不占空间) 在类中,可以设置其显示与否,setVisibility(View.GONE);不显示 setVisibility(View.VISIBLE);显示 xml中是visible 代码中是visibility 我发现了做WEB就是看HTTP协议和服务器返回的源码,然后我们对源码进行解析和封装。 既然你发现很多是一样的,所以要抽取出来。 之前我们在线程启动之前开个progressbar,子线程结束后, 发个消息告诉主线程,把那个progressbar关了,然后结束。 异步从网络下载操作,下载完毕自动放到页面中。太厉害了。非常常见的操作。 要做初始化和扫尾工作,但是它并不知道要怎么做,让调用者来做。 所以弄一个实现了该接口的对象给传进来。 其实就是调用者为了用这两个方法,我们必须把它给抽象出来,然后 通过构造函数把这个接口的实现给传递过来,那么调用 者就可以调用 接口里面的方法了。 异步加载过来,其实就是先把整个页面给展现出来,然后图片在自己在 后台下载,后台下载完毕后自动显示出来,绝大部分都是这样做的。 这是一个可以共用的图片异步下载方法。 import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; /** * * 第一个参数 就是图片下载路径的url * 第二个参数是 下载的进度 * 第三个参数就是异步任务执行完毕后的返回值 * @author Administrator * */ public class LoadImageAsynTask extends AsyncTask<String, Void, Bitmap> { LoadImageAsynTaskCallback loadImageAsynTaskCallback; public LoadImageAsynTask(LoadImageAsynTaskCallback loadImageAsynTaskCallback) { this.loadImageAsynTaskCallback = loadImageAsynTaskCallback; } public interface LoadImageAsynTaskCallback{ public void beforeLoadImage(); public void afterLoadImage(Bitmap bitmap); } /** * 当异步任务执行之前调用 */ @Override protected void onPreExecute() { //初始化的操作具体怎么去实现, LoadImageAsynTask 不知道 // 需要让调用这个 LoadImageAsynTask 的人 去实现 loadImageAsynTaskCallback.beforeLoadImage(); super.onPreExecute(); } /** * 异步任务执行之后调用 */ @Override protected void onPostExecute(Bitmap result) { loadImageAsynTaskCallback.afterLoadImage(result); super.onPostExecute(result); } /** * 后台子线程运行的异步任务 * String... params 可变长度的参数 */ @Override protected Bitmap doInBackground(String... params) { try { String path = params[0]; URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); InputStream is = conn.getInputStream(); return BitmapFactory.decodeStream(is); } catch (Exception e) { e.printStackTrace(); return null; } } } 这也是一个经典的异步,第一个先把页面加载过来,加载完成之后,再去加载图片。是不是很牛逼。 @Override public void fillData() { new AsyncTask<Void, Void, Void>(){ //onPreExecute 在异步任务执行之前调用的方法 // 运行在主线程里面的 // 初始化ui的操作 @Override protected void onPreExecute() { showLoading(); super.onPreExecute(); }; // onPostExecute 在异步任务(后台任务)执行之后调用的方法 // 运行在ui线程中 , @Override protected void onPostExecute(Void result) { hideLoading(); super.onPostExecute(result); tv_info.setText(content); tv_location.setText(location); tv_name.setText(name); //设置用户的头像 ,这个是自定义实现的,其实那些系统的对话框这些东西也是这样实现的。 LoadImageAsynTask loadImageAsynTask = new LoadImageAsynTask(new LoadImageAsynTaskCallback() { @Override public void beforeLoadImage() { iv_icon.setImageResource(R.drawable.ic_launcher); } @Override public void afterLoadImage(Bitmap bitmap) { if (bitmap!=null) { iv_icon.setImageBitmap(bitmap); }else{ iv_icon.setImageResource(R.drawable.ic_launcher); } } }); loadImageAsynTask.execute(iconurl); }; // doInBackground 后台执行的任务 // 方法运行在一个子线程当中 @Override protected Void doInBackground(Void... params) { // 执行耗时的操作 try { UserEntry ue = doubanService.getAuthorizedUser(); name = ue.getTitle().getPlainText(); location = ue.getLocation(); content = ((TextContent) ue.getContent()).getContent().getPlainText(); for (Link link : ue.getLinks()) { if ("icon".equals(link.getRel())) { System.out.println("图片 。"+link.getHref()); iconurl = link.getHref(); } } } catch (Exception e) { e.printStackTrace(); } return null; } }.execute(); } 那些 listview中,不可能每次读取一次就从网络下载一次 ,肯定是要缓存起来。如果在缓存中就用缓存中的, 如果不在,就重新下载。 通过构造方法和成员变量配合的传值是一种传值的非常重要的手段。 1、异步加载图片,非常重要。 2、加载过的图片存在SD卡上,直接用缓存,十分重要。 3、可以不用2,用更好的方法,软引用,不会增加用户的SD卡负担。 为了不每次从互联网上下载图片,而是从缓存中读取,有几种方法。 1、存到SD卡上。 2、在java里面还有一种引用类型,叫做软引用,它是在java虚拟机的层面保证 java虚拟机会尽量长时间的保留引用的对象。 当java虚拟机发现内存不足的时侯,才去回收软件软引用的对象。 我们可以使用软件引用的对象做内存缓存。 软件引用对象其实是一个容器,包装类。 key:是图片的URL value:是一个软引用类型的bitmap //软引用 Map<String, SoftReference<Bitmap>> iconCache; //初始化内存缓存 iconCache = new HashMap<String, SoftReference<Bitmap>>(); //第二种方法:把图片存储到内存缓存里面,把bitmap的软件引用存储到map集合里面。 iconCache.put(iconname, new SoftReference<Bitmap>(bitmap)); 如果listview数据量很大10万条。 1、分页加载,每个页面显示的条目都一样多。 2、分批加载。 当滚动到最下方,并且静止的时间再加载五条,所以 就是加一个滚动的监听事件。 如果有人问,10000条数据怎么办? 1、分批,分页加载。 2、如果每个条目中有大对象,异步加载,然后把大对象存储到软件引用中。 3、如果引用的内存溢出,写一个类继承application. 软引用当内存不足的时间先给清空,如果把所有的软引用清空后, 还显示内存不足的时间,才会爆出内存不足的错误。 待做? public class MyApp extends Application{ /** * 当内存不足的时间调用这个方法。 */ @Override public void onLowMemory() { super.onLowMemory(); // 发送一些广播 关闭掉一些activity service Intent intent = new Intent(); intent.setAction("kill_activity_action"); sendBroadcast(intent); } } 然后在相应的activity里面写广播接收者的类,并为其注册,在相应的方法里面处理。 这个方法是onScrollStateChanged拖住滑动。 /** * 在代码中注册的广播接收者话,你要在destory方法里面把广播接收者 * 反注册掉。否则会报错。 */ @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver); } 虽然分批加载可以在一定程序上解决大数据量问题,不过 加载过多还是会产生内存溢出问题。 分页获取数据? 分页原理:指定每一个页面显示的最大数目,每个页面最多 显示内容为100条。 book movie music等。 为什么这种分页不是很好呢?因为我们API没有提供这种方法 第二,当这个listview中某个条目为空的时间可以用 setEmptyView()去填充,例如用某个图片等。 上下文菜单: 先在主UI里面注册 registerForContextMenu(subjectlist); /** * 创建菜单 */ @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.context_menu, menu); } /** * 监听菜单 */ @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); int position = (int) info.id; Note note = (Note) subjectlist.getItemAtPosition(position); NoteEntry entry = note.getNoteEntry(); switch (item.getItemId()) { case R.id.menu_add_note: return true; case R.id.menu_delete_note://删除也是一个耗时的操作。 deleteNote(entry); return true; case R.id.menu_edit_note: return true; } return super.onContextItemSelected(item); } <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/menu_add_note" android:title="添加日记"> </item> <item android:id="@+id/menu_edit_note" android:title="修改日记"> </item> <item android:id="@+id/menu_delete_note" android:title="删除日记"> </item> </menu> 1. AsyncTask的三个泛型参数说明(三个参数可以是任何类型) 2. 第一个参数:传入doInBackground()方法的参数类型 3. 第二个参数:传入onProgressUpdate()方法的参数类型 4. 第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型。 execute的参数类型是这个任务要接受的参数,也就是doInBackground执行的参数,这个类型与第一个类型 是一致的,如果这个任务的执行不需要参数,可以为空。 private void deleteNote(NoteEntry entry) { new AsyncTask<NoteEntry, Void, Boolean>() { @Override protected void onPostExecute(Boolean result) { pd.dismiss(); if (result) { fillData(); }else { showToast("删除日记失败"); } super.onPostExecute(result); } @Override protected void onPreExecute() { pd = new ProgressDialog(MyNoteActivity.this); pd.setTitle("正在删除日记"); pd.show(); super.onPreExecute(); } @Override protected Boolean doInBackground(NoteEntry... params) { try { doubanService.deleteNote(params[0]); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }.execute(entry); } 新增成功之后 ,finish当前的activity,返回到列表页面的时间,自动让这条日记新增出来 ,很明显,在那个页面要setResult(200);然后在列表页面重写onActivityResult方法,让它重新 获取数据即可。 编辑日记是一个非常好的操作。 第一,它用到了不同的请求码去对应同一个页面。 第二,更新时间如何把日记给传过去 。有两种方法,一种是 放在Bundle中,另外一种是放在全局的app中。 case R.id.menu_edit_note: Intent editIntent = new Intent(MyNoteActivity.this,NewNoteActivity.class); editIntent.putExtra("iseditnote", true);//用来区别是更新还是新增。 //把数据放到全局里面,那边好取。 MyApp myApp = (MyApp) getApplication(); myApp.note = note; //需要告诉NewNoteActivity操作这是一个编辑日记的操作。 startActivityForResult(editIntent, EDIT_NOTE); return true; 取: boolean flag = getIntent().getBooleanExtra("iseditnote", false); if (false) { // 编辑日记 从全局中取数据 MyApp myApp = (MyApp) getApplication(); Note note = myApp.note; EditTextTitle.setText(note.getTitle()); EditTextContent.setText(note.getContent()); ......... }else { // 新日记 } 我的错误,写完Application后,一定要在配置文件里面注册,否则会爆出类型转换异常。 程序中的异常处理?十分重要,在面试中可以大说,特说的东西。 打开之后突然不见了,就是这些捕获的异常。UncaughtExceptionHandler 这个是十分重要的,如果出现异常,程序会自动退出,并且把错误的所有信息 发送到服务器上,让工程师去解决。 Build这个类里面有你手机的固件信息。 通过解析HTML数据获取我们需要的内容。 一淘网,抓取各种购物网站的同种商品。这是在别人没有开放网络接口的时间 获取别人的数据,它是从HTML里面直接获取的。 用jericho-html-3.1jar包。 这个东西是自己先分析别人的HTML,然后用J2SE项目调试。 看一它相关的资料和文档。 http://jericho.htmlparser.net/docs/javadoc/index.html /** * Html解析 获取新书数据 * @param context * @return * @throws Exception */ public static List<NewBook> getNewBooks(Context context ) throws Exception{ String path =context.getResources().getString(R.string.newbookpath); URL url = new URL(path); URLConnection conn = url.openConnection(); Source source = new Source(conn); List<NewBook> newbooks = new ArrayList<NewBook>(); List<Element> lielements = source.getAllElements("li"); System.out.println(lielements.size()); for(Element lielement : lielements){ List<Element> childrenlists = lielement.getChildElements(); if(childrenlists.size()==2){ if("detail-frame".equals(childrenlists.get(0).getAttributeValue("class"))){ NewBook newbook = new NewBook(); //数目对应的div信息 Element div = childrenlists.get(0); List<Element> divChildren = div.getChildElements(); String name = divChildren.get(0).getTextExtractor().toString(); newbook.setName(name); String description = divChildren.get(1).getTextExtractor().toString(); newbook.setDescription(description); String summary = divChildren.get(2).getTextExtractor().toString(); newbook.setSummary(summary); Element achild = childrenlists.get(1); String iconpath = achild.getChildElements().get(0).getAttributeValue("src"); newbook.setIconpath(iconpath); newbooks.add(newbook); } } } return newbooks; } 十分重要的问题,出现图片乱跳的现象? 虽然使用listview的缓存,很简单,效率高,不过 由于它缓存了历史的view对象,不过,当你的图片 是异步下载的时间会出现跳图片的现象,因为缓存了 图片,会出现新的条目一直显示老的图片,并且一直在 与新的图片切换。 其实浪费时间的操作都在View.inflate这个方法的操作上。 我们在布局里面把imageview干掉,在代码里面生成 我们自己定义的imageview就不会再被listview缓存。 PreferenceActivity可以直接用它。 配置信息的页面这样用。其实在系统的设置里面其实都是用的这个activity. 参看项目中的代码 。只要记住Setting里面基本用的都是PreferenceActivity。 在线格式化JSON格式 。 每一个大括号是一个JSON对象----JSONObject 每一个中括号叫一个JSON数组----JSONArray. {}JSON对象里面的内容,必须是键值对的形式,如果不是就是产生JSONException. <!-- 自定义activity为弹出窗float window --> <style name="floatwindow" parent="@android:style/Theme.Dialog"> <item name="android:windowBackground">@drawable/background</item> </style> 导入代码问题: