声明:在保证本文完整的情况下可自由转载!
前言
国士工作室是一支专注于Android平台企业级应用开发的技术团队,致力于做中国最棒的Android应用程序开发机构,提供最棒的Android企业级应用开发培训服务。
企业培训和开发合作官方联系方式:
电话:18610086859
Email:hiheartfirst@gmail.com
QQ:1740415547
QQ群:175189001
国士工作室 有你更美好!
若立志成为Android高手,如有耐心,“一瓶一钵足矣”。
“天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。人之为学有难易乎?学之,则难者亦易矣;不学,则易者亦难矣。”想成为Android高手?这可不是想象中写几行代码那么容易的事情,但也不是不可实现。
如何做?
1,学会懒惰!奇怪吧?但是,你一定也听说过和感受过这个世界某种程度上是由懒人推动的,生命在于懒惰,懒人创造世界。当然,懒惰也是真的傻傻的呆在那里什么都不做,而是说要善于想出做事情的更好的方式,这样就可以节约大量的时间,也就有更多的机会懒惰了,同事也懒出了境界。在Android中如何懒惰?《如何成为Android高手》一文就如何在Android中学会懒惰和朋友们进行了分享。
2,精通Android体系架构、MVC、常见的设计模式、控制反转(IoC):这一点难吗?“学之,则难者亦易矣;不学,则易者亦难矣。”
3,编写可重用、可扩展、可维护、灵活性高的代码:Android应用程序开发的使用纯粹面向对象的Java作为开发语言,自然也就继承了关于Java关于面向对象的优秀想思想,如何做?《如何成为Android高手》一文就如何在Android中编写可重用、可扩展、可维护、灵活性高的代码和朋友们进行了分享。
4,高效的编写高效的代码:高效的编写代码和编写高效的代码好像天生就是死敌。似乎开发速度上去了,程序的执行效率就下去了;程序的执行效率上去,开发速度就下去了。如何解决二者的忙着,请听《如何成为Android高手》一文想大家娓娓道来。
5,学会至少一门服务器端开发技术:没搞错吧,成为Android高手还需要学习服务端开发技术?对,需要!《如何成为Android高手》一文就该问题和大家进行了分享。
“蜀之鄙,有二僧:其一贫,其一富。贫者语于富者曰:"吾欲之南海,何如?"富者曰:"子何恃而往?"曰:"吾一瓶一钵足矣。"富者曰:"吾数年来欲买舟而下,犹未能也。子何恃而往!"越明年,贫者自南海还,以告富者,富者有惭色。西蜀之去南海,不知几千里也,僧富者不能至,而贫者至之,人之立志,顾不如蜀鄙之僧哉 ”
若立志成为Android高手,如有耐心,“一瓶一钵足矣”。
正文
关于国士工作室
我们(国士工作室)是一支专注于Android平台企业级应用开发的技术团队,对娱乐多媒体应用有着深刻的理解及研发能力,致力服务于企业用户。为音视频等娱乐多媒体网站、门户网站、SNS、论坛、电子商务等传统网络应用向移动互联网发展提供解决方案和技术支持,为企业提供Android培训服务等多种业务。
我们尤其擅长于提供从Android客户端到服务端的一站式解决方案和技术支持,服务端可以采用Java EE,也可以采用轻量级流行的LAMP技术体系。目前,研发出了比KU6、优酷更加强大和完善的Android视频网站娱乐多媒体客户端软件,并在持续升级中。
目前,我们正在务实而卓有成效的与音视频等娱乐多媒体网站、门户网站、SNS、论坛、电子商务等传统网络服务商合作,发展迅速,渴望有志之士的加入,和我们一起为成为世界最好的Android软件开发和咨询、培训公司而奋斗,为移动互联网和智能手机时代贡献力量!
联系我们
电话:18610086859
Email:hiheartfirst@gmail.com
QQ:1740415547
博客:
注意:该文档参考和使用了网络上的很多免费开放的内容,并以免费开放的方式发布,希望为移动互联网和智能手机时代贡献绵薄之力!可以随意转载,但不得使用该文档谋利。
另外:国士工作室已免费发布原创教程《大话企业级Android开发》,请访问国士工作室博客获取教程。
·2007年底Google宣布举办总奖金高达1000万美元的开发者大奖赛,鼓励程序开发者在Android上写出实用而又具有创意的应用程序;
·2009年5月27日,在Google的I/O开发者聚会上,Google发布了总奖金接近2000万美元的第二次大奖赛的消息,开发者们开始了新一轮的较量;
Android是Google于2007年11月5日宣布的基于Linux平台的开源手机操作系统的名称,该平台由操作系统、中间件、用户界面和应用软件组成,号称是首个为移动终端打造的真正开放和完整的移动软件。
Android一出生就被打上了富二代的胎记,不仅仅是因为诞生于当今的网络霸主Google,更主要还有一个空前强大和壮观的开放手机联盟OHA(Open Handset Alliance)提供全力的支持。OHA是什么?OHA涵盖了中国移动、T-Mobile、Sprint等移动运营商,包括HTC、Motolora、三星等手机制造商,有Google为代表的手机软件商,还有Inter、Nvidia为标志的底层硬件厂商和等商业运作公司,该组织声称组织的所有成员都会基于Android来开发新的手机业务。
但是,要成为Android高手并不是一件容易的事情。并不是很多人想象的能够飞快的写出几行漂亮的代码去解决一些困难的问题就是Android高手了。真正的Android高手需要考虑的问题远远不是写些漂亮的代码就足够的。下面是成为一名真正的Android高手必须掌握和遵循的一些准则:
1,学会懒惰
2,精通Android体系架构、MVC、常见的设计模式、控制反转(IoC)
3,编写可重用、可扩展、可维护、灵活性高的代码
4,高效的编写高效的代码
5,学会至少一门服务器端开发技术
一:学会懒惰
没搞错吧?竟然让程序开发人员学会懒惰?程序开发人员可能是世界上最为忙碌的一类人啦!对,没错,学会懒惰!正因为程序开发人员忙碌,正因为程序开发人员可能会在客户无限变化的需求之下没日没夜的加班,所以要学会懒惰,这样,你就可以把更多的时间浪费在美好的事物身上!
如何懒惰:
1,Don't Reinvent the Wheel(不要重复发明轮子)。
2,Inventing the Wheel(发明轮子)。
1,Don't Reinvent the Wheel(不要重复发明轮子)。
“轮子理论”,也即“不要重复发明轮子”,这是西方国家的一句谚语,原话是:Don't Reinvent the Wheel。“不要重复发明轮子 ”意思是企业中任何一项工作实际上都有人做过,我们所需要做的就是找到做过这件事情的人。拿到软件领域中就是指有的项目或功能,别人已经做过,我们需要用的时候,直接拿来用即可,而不要重新制造。
Android号称是首个为移动终端打造的真正开放和完整的移动软件。Android发布后不久Google公司就发布了操作系统核心(Kernel)与部分驱动程序的源代码,到目前位置除了Google Map等Google公司的核心组件没有开放源代码外,Android基本完成了完全的开源,这就极大的促进了Android的普及和移植。受到Android开放行为和开源精神的影响,在世界各地,有成千上万的程序员喜欢和别人分享自己的聪明才智和自己编写的代码。你可以在Google的Android讨论组或者Google搜索引擎上搜索到很多优秀的程序代码。这样做并不是鼓励大家整天等着让别人为你编写代码,而是你可以“站在伟人的肩膀上”,充分发扬“拿来主义”,聪明地应用别人的程序代码可以节省你大量的时间。
下面笔者为大家介绍几个通用的类,这些类来自笔者平日的收集,如果你能把它们加入到你自己的类库中,迟早你会发现自己在进行Android开发的时候受益无穷:
1) 从输入流中获取数据并以字节数组返回,这种输入流可以来自Android本地也可以来自网络。
代码 1 import java.io.ByteArrayOutputStream; 2 3 import java.io.InputStream; 4 5 6 7 public class StreamTool { 8 9 /** 10 11 * 从输入流获取数据 12 13 * @param inputStream 14 15 * @return 16 17 * @throws Exception 18 19 */ 20 21 public static byte [] readInputStream(InputStream inputStream) throws Exception { 22 23 byte [] buffer = new byte [ 1024 ]; // 你可以根据实际需要调整缓存大小 24 25 int len = - 1 ; 26 27 ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); 28 29 while ( (len = inputStream.read(buffer)) != - 1 ){ 30 31 outSteam.write(buffer, 0 , len); 32 33 } 34 35 outSteam.close(); 36 37 inputStream.close(); 38 39 return outSteam.toByteArray(); 40 41 } 42 43 } 44 45
2) 通过Android客户端上传数据到服务器:可以上传简单的表单,也可以方便的上传带有附件的文件,此类远远比Android自身的HttpClient更高效、更易于使用:
代码 import java.io.DataOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; public class HttpRequester { /** * 直接通过HTTP协议提交数据到服务器,实现如下面表单提交功能:* <FORM METHOD=POST ACTION=" http://192.168.0.200 :8080/ssi/fileload/test.do" enctype="multipart/form-data"><INPUT TYPE="text" NAME="name"><INPUT TYPE="text" NAME="id"><input type="file" name="imagefile"/><input type="file" name="zip"/></FORM>* @param actionUrl 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用或http://192.168.1.10:8080这样的路径测试)* @param params 请求参数 key为参数名,value为参数值* @param file 上传文件 */ public static String post(String actionUrl, Map < String, String > params, FormFile[] files) { try { String BOUNDARY = " ---------7d4a6d158c9 " ; // 数据分隔线 String MULTIPART_FORM_DATA = " multipart/form-data " ;URL url = new URL(actionUrl);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout( 5 * 1000 );conn.setDoInput( true ); // 允许输入 conn.setDoOutput( true ); // 允许输出 conn.setUseCaches( false ); // 不使用Cache conn.setRequestMethod( " POST " ); conn.setRequestProperty( " Connection " , " Keep-Alive " );conn.setRequestProperty( " Charset " , " UTF-8 " );conn.setRequestProperty( " Content-Type " , MULTIPART_FORM_DATA + " ; boundary= " + BOUNDARY);StringBuilder sb = new StringBuilder(); for (Map.Entry < String, String > entry : params.entrySet()) { // 构建表单字段内容 sb.append( " -- " );sb.append(BOUNDARY);sb.append( " \r\n " );sb.append( " Content-Disposition: form-data; name=\ "" + entry.getKey() + " \ " \r\n\r\n " );sb.append(entry.getValue());sb.append( " \r\n " );}DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());outStream.write(sb.toString().getBytes()); // 发送表单字段数据 for (FormFile file : files){ // 发送文件数据 StringBuilder split = new StringBuilder();split.append( " -- " );split.append(BOUNDARY);split.append( " \r\n " );split.append( " Content-Disposition: form-data;name=\ "" + file.getFormname()+ " \ " ;filename=\ "" + file.getFilname() + " \ " \r\n " );split.append( " Content-Type: " + file.getContentType() + " \r\n\r\n " );outStream.write(split.toString().getBytes()); if (file.getInStream() != null ){ byte [] buffer = new byte [ 1024 ]; int len = 0 ; while ((len = file.getInStream().read(buffer)) !=- 1 ){ outStream.write(buffer, 0 , len);}file.getInStream().close();} else { outStream.write(file.getData(), 0 , file.getData().length);}outStream.write( " \r\n " .getBytes());} byte [] end_data = ( " -- " + BOUNDARY + " --\r\n " ).getBytes(); // 数据结束标志 outStream.write(end_data);outStream.flush(); int cah = conn.getResponseCode(); if (cah != 200 ) throw new RuntimeException( " 请求url失败 " );InputStream is = conn.getInputStream(); int ch;StringBuilder b = new StringBuilder(); while ( (ch = is.read()) != - 1 ){ b.append(( char )ch);} outStream.close();conn.disconnect(); return b.toString();} catch (Exception e) { throw new RuntimeException(e);}} /** * 提交数据到服务器* @param actionUrl 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用或http://192.168.1.10:8080这样的路径测试)* @param params 请求参数 key为参数名,value为参数值* @param file 上传文件 */ public static String post(String actionUrl, Map < String, String > params, FormFile file) { return post(actionUrl, params, new FormFile[]{file});} public static byte [] postFromHttpClient(String path, Map < String, String > params, String encode) throws Exception{ List < NameValuePair > formparams = new ArrayList < NameValuePair > (); // 用于存放请求参数 for (Map.Entry < String, String > entry : params.entrySet()){ formparams.add( new BasicNameValuePair(entry.getKey(), entry.getValue()));} UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, " UTF-8 " );HttpPost httppost = new HttpPost(path);httppost.setEntity(entity);HttpClient httpclient = new DefaultHttpClient(); // 看作是浏览器 HttpResponse response = httpclient.execute(httppost); // 发送post请求 return StreamTool.readInputStream(response.getEntity().getContent());} /** * 发送请求* @param path 请求路径* @param params 请求参数 key为参数名称 value为参数值* @param encode 请求参数的编码 */ public static byte [] post(String path, Map < String, String > params, String encode) throws Exception{ // String params = "method=save&name="+ URLEncoder.encode("国士工作室", "UTF-8")+ "&age=28&"; // 需要发送的参数 StringBuilder parambuilder = new StringBuilder( "" ); if (params != null && ! params.isEmpty()){ for (Map.Entry < String, String > entry : params.entrySet()){ parambuilder.append(entry.getKey()).append( " = " ).append(URLEncoder.encode(entry.getValue(), encode)).append( " & " );}parambuilder.deleteCharAt(parambuilder.length() - 1 );} byte [] data = parambuilder.toString().getBytes();URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection)url.openConnection();conn.setDoOutput( true ); // 允许对外发送请求参数 conn.setUseCaches( false ); // 不进行缓存 conn.setConnectTimeout( 5 * 1000 );conn.setRequestMethod( " POST " ); // 下面设置http请求头 conn.setRequestProperty( " Accept " , " image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* " );conn.setRequestProperty( " Accept-Language " , " zh-CN " );conn.setRequestProperty( " User-Agent " , " Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) " );conn.setRequestProperty( " Content-Type " , " application/x-www-form-urlencoded " );conn.setRequestProperty( " Content-Length " , String.valueOf(data.length));conn.setRequestProperty( " Connection " , " Keep-Alive " ); // 发送参数 DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());outStream.write(data); // 把参数发送出去 outStream.flush();outStream.close(); if (conn.getResponseCode() == 200 ){ return StreamTool.readInputStream(conn.getInputStream());} return null ;}}
2,Inventing the Wheel(发明轮子)。
发明轮子?不错,发明轮子!我们不仅要发明轮子,更要成为努力成为世界上发明轮子的主导力量,唯有这样,才能谈的上中华名族软件大业的真正强大。在Android,要发明轮子,就是我们要主动的是解决一些世界上他人未解决的难题或者创造新的编程框架或者对Android进行深度的改造以适合自己的业务发展需要。Google发布了Android后不久,中国移动便投入了大量的人力和物力,在Android的基础上创建融入自己业务并开发、封装了新的功能的和框架的OMS,这是Android中发明轮子的一个非常重要的例子。可能你会说,这发明轮子也太难了吧,别急,我们慢慢来,开发一个框架特定领域的框架吧!你可能会一脸无辜的说,开发一个框架是说的那么容易吗?当然不是啦。但是也并非不可能,首先,我们分析一下框架的魅力的源泉,看看Spring、Struts等Java EE框架,在看看.NET框架,当然也可以看看发展的如火如荼、层出不穷的PHP框架,她们的强大和魅力的源泉都在于:IoC(Inversion of Control)。
Don't call us, we'll call you(别找我,我会来找你的)。我们下面就自己发明一个轮子的模型,实际展示一个框架最初核心的类,让你一饱眼福:
1) 下面的类是文件下载类,支持文件的多线程断点续传,使用该类的即可安全、高效的下载任何类型的二进制文件:
代码 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import cn.itcast.service.FileService; import android.content.Context; import android.util.Log; /** * 文件下载器 */ public class FileDownloader { private Context context; private FileService fileService; private static final String TAG = " FileDownloader " ; /* 已下载文件大小 */ private int downloadSize = 0 ; /* 原始文件大小 */ private int fileSize = 0 ; /* 线程数 */ private DownloadThread[] threads; /* 下载路径 */ private URL url; /* 本地保存文件 */ private File saveFile; /* 下载记录文件 */ private File logFile; /* 缓存各线程最后下载的位置 */ private Map < Integer, Integer > data = new ConcurrentHashMap < Integer, Integer > (); /* 每条线程下载的大小 */ private int block; private String downloadUrl; // 下载路径 /** * 获取线程数 */ public int getThreadSize() { return threads.length;} /** * 获取文件大小* @return */ public int getFileSize() { return fileSize;} /** * 累计已下载大小* @param size */ protected synchronized void append( int size) { downloadSize += size;} /** * 更新指定线程最后下载的位置* @param threadId 线程id* @param pos 最后下载的位置 */ protected void update( int threadId, int pos) { this .data.put(threadId, pos);} /** * 保存记录文件 */ protected synchronized void saveLogFile() { this .fileService.update( this .downloadUrl, this .data);} /** * 构建文件下载器* @param downloadUrl 下载路径* @param fileSaveDir 文件保存目录* @param threadNum 下载线程数 */ public FileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) { try { this .context = context; this .downloadUrl = downloadUrl;fileService = new FileService(context); this .url = new URL(downloadUrl); if ( ! fileSaveDir.exists()) fileSaveDir.mkdirs(); this .threads = new DownloadThread[threadNum]; HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout( 6 * 1000 );conn.setRequestMethod( " GET " );conn.setRequestProperty( " Accept " , " image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* " );conn.setRequestProperty( " Accept-Language " , " zh-CN " );conn.setRequestProperty( " Referer " , downloadUrl); conn.setRequestProperty( " Charset " , " UTF-8 " );conn.setRequestProperty( " User-Agent " , " Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729) " );conn.setRequestProperty( " Connection " , " Keep-Alive " );conn.connect();printResponseHeader(conn); if (conn.getResponseCode() == 200 ) { this .fileSize = conn.getContentLength(); // 根据响应获取文件大小 if ( this .fileSize <= 0 ) throw new RuntimeException( " 1无法获知文件大小 " );String filename = getFileName(conn); this .saveFile = new File(fileSaveDir, filename); /* 保存文件 */ Map < Integer, Integer > logdata = fileService.getData(downloadUrl); if (logdata.size() > 0 ){ for (Map.Entry < Integer, Integer > entry : logdata.entrySet())data.put(entry.getKey(), entry.getValue() + 1 );} this .block = this .fileSize / this .threads.length + 1 ; if ( this .data.size() == this .threads.length){ for ( int i = 0 ; i < this .threads.length; i ++ ) { this .downloadSize += this .data.get(i + 1 ) - ( this .block * i);}print( " 已经下载的长度 " + this .downloadSize);} } else { throw new RuntimeException( " 2服务器响应错误 " );}} catch (Exception e) { print(e.toString()); throw new RuntimeException( " 3连接不到下载路径 " );}} /** * 获取文件名 */ private String getFileName(HttpURLConnection conn) { String filename = this .url.toString().substring( this .url.toString().lastIndexOf( ' / ' ) + 1 ); if (filename == null || "" .equals(filename.trim())){ // 如果获取不到文件名称 for ( int i = 0 ;; i ++ ) { String mine = conn.getHeaderField(i); if (mine == null ) break ; if ( " content-disposition " .equals(conn.getHeaderFieldKey(i).toLowerCase())){ Matcher m = Pattern.compile( " .*filename=(.*) " ).matcher(mine.toLowerCase()); if (m.find()) return m.group( 1 );}}filename = UUID.randomUUID() + " .tmp " ; // 默认取一个文件名 } return filename;} /** * 开始下载文件* @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null* @return 已下载文件大小* @throws Exception */ public int download(DownloadProgressListener listener) throws Exception{ try { if ( this .data.size() != this .threads.length){ this .data.clear(); for ( int i = 0 ; i < this .threads.length; i ++ ) { this .data.put(i + 1 , this .block * i);}} for ( int i = 0 ; i < this .threads.length; i ++ ) { int downLength = this .data.get(i + 1 ) - ( this .block * i); if (downLength < this .block && this .data.get(i + 1 ) < this .fileSize){ // 该线程未完成下载时,继续下载 RandomAccessFile randOut = new RandomAccessFile( this .saveFile, " rw " ); if ( this .fileSize > 0 ) randOut.setLength( this .fileSize); randOut.seek( this .data.get(i + 1 )); this .threads[i] = new DownloadThread( this , this .url, randOut, this .block, this .data.get(i + 1 ), i + 1 ); this .threads[i].setPriority( 7 ); this .threads[i].start();} else { this .threads[i] = null ;}} this .fileService.save( this .downloadUrl, this .data); boolean notFinish = true ; // 下载未完成 while (notFinish) { // 循环判断是否下载完毕 Thread.sleep( 900 );notFinish = false ; // 假定下载完成 for ( int i = 0 ; i < this .threads.length; i ++ ){ if ( this .threads[i] != null && ! this .threads[i].isFinish()) { notFinish = true ; // 下载没有完成 if ( this .threads[i].getDownLength() == - 1 ){ // 如果下载失败,再重新下载 RandomAccessFile randOut = new RandomAccessFile( this .saveFile, " rw " );randOut.seek( this .data.get(i + 1 )); this .threads[i] = new DownloadThread( this , this .url, randOut, this .block, this .data.get(i + 1 ), i + 1 ); this .threads[i].setPriority( 7 ); this .threads[i].start();}}} if (listener != null ) listener.onDownloadSize( this .downloadSize);}fileService.delete( this .downloadUrl);} catch (Exception e) { print(e.toString()); throw new Exception( " 下载失败 " );} return this .downloadSize;} /** * 获取Http响应头字段* @param http* @return *