posts - 33, comments - 0, trackbacks - 0, articles - 0
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

                在第一篇時(shí)就說過框架要在URL上作文章,是的,本文就框架怎樣充分利用url上作盡可能詳細(xì)的說明。

                做web開發(fā)的不可能對(duì)url陌生,早在web1.0時(shí)代,url作為統(tǒng)一資源定位符,在對(duì)web中資源的如何獲得上起到巨大作用。不論用戶請(qǐng)求的時(shí)靜態(tài)頁(yè)面或者是各種圖片、腳本文件,通過url總能從web網(wǎng)站獲取要訪問的資源。Web2.0更是常常使用url作為get請(qǐng)求時(shí)參數(shù)的傳遞,如http://xxx.xxx.xxx/xxx.jsp?user=admin。以及近幾年很火restful web service 摒棄soap而使用url傳遞請(qǐng)求參數(shù) 都說明合理利用url的可行和流行。

                當(dāng)然不止是使用了url就算好的實(shí)踐,而是能夠做到優(yōu)雅的使用,保證層次分明和整體的簡(jiǎn)潔,這才算是好的方式,這也正是本框架對(duì)使用url 所追求的目標(biāo)。

                首先來看幾個(gè)例子:

          http://www.cnblogs.com/p2
          http://www.xxx.com/index.do?page=2

          http://www.xxx.com/product/
          http://www.xxx.com/channel.do?channel=product

          http://www.xxx.com/product/mobile
          http://www.xxx.com/channel.do?channel=product&&subChannel=mobile

                相信各位看官不用我說也能明白,這幾組的實(shí)現(xiàn)肯定第一種實(shí)現(xiàn)的方式更佳。拋開它能屏蔽服務(wù)器端使用的技術(shù)這一特性不說,它還能夠更好地說明動(dòng)態(tài)網(wǎng)站的層次結(jié)構(gòu),讓用戶在訪問時(shí)能明確知道在網(wǎng)站的什么位置,而不會(huì)覺得是陷入了一個(gè)迷宮。

                當(dāng)然上面列舉的例子是網(wǎng)站前端所使用的url表現(xiàn)方式,因?yàn)楸憩F(xiàn)方式可以多種多樣,個(gè)人喜好不同,本框架在設(shè)計(jì)時(shí)沒有給指定前端url的表現(xiàn)方式,而是定義接口,把這個(gè)權(quán)利留給使用的用戶??蚣軐⒖紤]更多 通用性的東西而不是 個(gè)性 自由的東西。

           

                下面對(duì)框架里默認(rèn)使用的url Router AMPPathRouter做詳細(xì)的介紹,包括設(shè)計(jì)的思想和實(shí)現(xiàn)的方式。首先AMPPathRouter的用途定位為后臺(tái)使用。為了理解快速的理解它的工作原理,先來和struts做一下對(duì)比。

                Struts關(guān)于請(qǐng)求的配置:

           

          <action name="login" class="com.lscmjx.action.LoginAction" method="login">
          <result name="success">
          main.jsp
          </result>
          <result name="failure">
          login.jsp
          </result>
          </action>

              它提交的url會(huì)是http://xxx.xxx.xxx/login,訪問web服務(wù)器時(shí)會(huì)把此url傳遞到struts框架交給它處理,之后struts會(huì)在struts.xml中尋找login的相關(guān)的配置,像上面例子,struts會(huì)找到LoginAction的類,并且調(diào)用其login的方法。

                寫到這里,我請(qǐng)問這是最好的方式嗎?當(dāng)然不是,至少我在使用struts時(shí)就認(rèn)為這是相當(dāng)撇腳的設(shè)計(jì)。上面例子只是列舉一個(gè)login方法,假如一個(gè)系統(tǒng)中要對(duì)后臺(tái)調(diào)用的方法是100個(gè),那豈不是就需要在struts.xml中寫100個(gè)與之類似的配置。想想都頭大,這樣繁瑣的工作,應(yīng)該是由框架自己去處理,而不是人工給配置。

           

                再來看實(shí)現(xiàn)相同功能的Unicorn web框架的配置。

           

          <action class="com.mh.action.UserAction"></action>

                當(dāng)然提交的url肯定需要包含多一些的信息,來保證能通過url正確調(diào)用框架Action里的方法。這里提交的url方式:http://xxx.xxx.xxx/UserAction/login/

                通過在url里附加調(diào)用的Action類的信息,可以省略為不同的方法都在xml里配置的麻煩。假如UserAction里有100個(gè)方法,框架也只需這一行的配置。

                有了大體的認(rèn)識(shí)之后,來看框架的核心部分AMPPathRouter的具體實(shí)現(xiàn)。

           

            /**
          * 檢查url是否是此Router類要處理的,/Action/Method/Param 格式的將會(huì)被檢查合格,返回true
          *
          @param relativeUri
          *
          @param actionMap
          *
          @return
          */
          public boolean checkUrl(String relativeUri, Map<String, ActionSupport> actionMap) {
          Pattern pattern = Pattern.compile("^/\\w+/\\w+/\\S*");
          Matcher matcher = pattern.matcher(relativeUri);
          if(matcher.matches()) {
          String actionName = relativeUri.split("/")[1];
          ActionSupport actionSupport = actionMap.get(actionName);
          if(null != actionSupport) {
          String actionMethodName = relativeUri.split("/")[2];
          Class<?> actionClass = actionSupport.getClass();
          Method[] methods = actionClass.getMethods();
          for(int i = 0; i < methods.length; i++) {
          Method method = methods[i];
          String methodName = method.getName();
          if(methodName.equals(actionMethodName)) {
          return true;
          }
          }
          } else {
          return false;
          }
          }
          return false;
          }
          /**
          * 匹配規(guī)則為:
          * 1、符合/Action/method/param格式,
          * 2、并且Action在actionMap中的確存在
          * 3、method在此Action中存在
          */
          @Override
          public boolean route(String relativeUri, UrlFilter urlFilter) {
          Map<String, ActionSupport> actionMap = urlFilter.getActionMap();
          if(!this.checkUrl(relativeUri, actionMap)) {
          return false;
          }
          // 攔截Action/Method/Param方式的請(qǐng)求,并構(gòu)建ActionSupport類的屬性
          String[] params = relativeUri.split("/");
          try {
          ActionSupport actionSupport = actionMap.get(params[1]);
          Class<?> action = actionSupport.getClass();
          Method method = action.getMethod(params[2], new Class[] {});
          if(params.length > 3) {
          this.boxingRequest(urlFilter.getRequest(), params[3]);
          }
          // 只要找到ActionSupport的子類,則初始化其所具有的屬性
          Object newInstance = action.newInstance();
          this.initActionSupport(newInstance, urlFilter);
          String result = (String) method.invoke(newInstance, new Object[] {});
          if (null == result || ActionSupport.AJAX.equals(result) || ActionSupport.FORWARD.equals(result) || ActionSupport.WEB_SERVICE.equals(result)) {
          return true;
          }
          if(ActionSupport.REDIRECT.equals(result)) {
          urlFilter.getResponse().sendRedirect(result);
          return true;
          }
          } catch (NoSuchMethodException e) {
          e.printStackTrace();
          } catch (SecurityException e) {
          e.printStackTrace();
          } catch (IllegalAccessException e) {
          e.printStackTrace();
          } catch (IllegalArgumentException e) {
          e.printStackTrace();
          } catch (InvocationTargetException e) {
          e.printStackTrace();
          } catch (InstantiationException e) {
          e.printStackTrace();
          } catch (Exception e) {
          e.printStackTrace();
          }
          return false;
          }
          /**
          * 把url中得param加入到request的attribute里
          *
          @param request
          *
          @param parameter
          */
          private void boxingRequest(HttpServletRequest request, String parameter) {
          String[] parameters = parameter.split("&");
          for (int i = 0; i < parameters.length; i++) {
          String param = parameters[i];
          String[] key_value = param.split("=");
          if(key_value.length == 2) {
          request.setAttribute(key_value[0], key_value[1]);
          }
          }
          }
          /**
          * 初始化ActionSupport類中所需的request、response、session、application等對(duì)象
          *
          @param obj
          *
          @param urlFilter
          */
          private void initActionSupport(Object obj, UrlFilter urlFilter) {
          ActionSupport action = (ActionSupport) obj;
          action.setRequest(urlFilter.getRequest());
          action.setResponse(urlFilter.getResponse());
          action.setSession(urlFilter.getSession());
          action.setApplication(urlFilter.getApplication());
          }

           

                這便是AMPPathRouter的全部?jī)?nèi)容,其中在把請(qǐng)求分發(fā)到ActionSupport的子類 并調(diào)用相關(guān)方法時(shí) 是通過反射實(shí)現(xiàn),其他地方地方都是相當(dāng)容易理解的。

                空說無憑,把框架應(yīng)用到實(shí)戰(zhàn)中才是硬道理:

           

                好了,下一篇介紹Action 和 json。

          posted @ 2011-11-25 18:18 馬航 閱讀(271) | 評(píng)論 (0)編輯 收藏

                上篇說過,所有提交到web程序的url都被此UrlFilter攔截。攔截到請(qǐng)求后,UrlFilter則召集它的好多個(gè)得力干將Router 們, 詢問他們:“誰(shuí)能處理此URL啊 ?”

          這時(shí)一位叫做AMPRouter 首當(dāng)其沖 說:“這個(gè)url交給我了”。這時(shí)filter就會(huì)把此url全權(quán)交給AMPRouter來辦,至于如何去處理,filter也不再過問,它覺得:“我把任務(wù)都交給你了,怎么解決是你的事”。

                根據(jù)單一職責(zé)的原則,UrlFilter就負(fù)責(zé)上面情景中的分發(fā)urlRouter中的差事,url如何分發(fā)交給Router處理。并且Router實(shí)際是一個(gè)接口,使用框架的用戶完全可以自己實(shí)現(xiàn)Router,這樣用戶可以自主定義的url分發(fā)的策略。另外呢,框架初始化的一些操作它也是 推脫不掉的,像根據(jù)unicorn-config.xml初始化系統(tǒng)中的RouterAction'。下面是具體的代碼:

           

          @Override
          public void init(FilterConfig config) throws ServletException {
          	application = config.getServletContext();
          	String loadPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
          	String classPath = loadPath.substring(1, loadPath.length());
          	ArrayList<String> actions = XMLReader.getNodeValues(classPath + "unicorn-config.xml", "actions");
          	this.initActions(actions);
          	ArrayList<String> routers = XMLReader.getNodeValues(classPath + "unicorn-config.xml", "routers");
          	this.initRouters(routers);
          }

           

          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
          			throws IOException, ServletException {
          	HttpServletRequest request = (HttpServletRequest) servletRequest;
          	String path = request.getContextPath();
          	String uri = request.getRequestURI();
          	String relativeUri = uri.substring(path.length(), uri.length());
          	this.request = request;
          	this.session = request.getSession();
          	this.response = (HttpServletResponse) servletResponse;
          	// 用戶自定義的Router優(yōu)先級(jí)最高,url先通過用戶定義的
          	Iterator<IPathRouter> iterator = routerList.iterator();
          	while(iterator.hasNext()) {
          		IPathRouter router = iterator.next();
          		if(router.route(relativeUri, this)) {
          			return ;
          		}
          	}
          	// 攔截不到的繼續(xù)訪問
          	filterChain.doFilter(servletRequest, servletResponse);
          }
          

                  其中Router類的初始化,Action類的初始化于這個(gè)類似:

          private void initRouters(ArrayList<String> routers) {
          	routerList = new ArrayList<IPathRouter>();
          	for (int i = 0; i < routers.size(); i++) {
          		String routerName = routers.get(i);
          		try {
          			Class<?> clz = Class.forName(routers.get(i));
          			// 單例模式通過方法獲取對(duì)象實(shí)例
          			IPathRouter router = (IPathRouter) clz.newInstance();
          			routerList.add(router);
          		} catch (ClassNotFoundException e) {
          			e.printStackTrace();
          		} catch (IllegalAccessException e) {
          			e.printStackTrace();
          		} catch (SecurityException e) {
          			e.printStackTrace();
          		} catch (IllegalArgumentException e) {
          			e.printStackTrace();
          		} catch (InstantiationException e) {
          			e.printStackTrace();
          		}
          	}
          	// 最后把框架默認(rèn)的Router加入進(jìn)來
          	routerList.add(new AMPPathRouter());
          }
          

                  其中unicorn-config.xml文件的編寫,拿其中我一個(gè)項(xiàng)目里的這個(gè)文件來舉例:

          <?xml version="1.0" encoding="UTF-8" ?>
          <config>
          <routers>
          <router class="com.mh.router.MySessionCheckRouter"></router>
          </routers>
          <actions>
          <action class="com.mh.action.UserAction"></action>
          <action class="com.mh.action.InformationAction"></action>
          <action class="com.mh.action.UploadInformationIconAction"></action>
          <action class="com.mh.action.TempPicAction"></action>
          <action class="com.mh.action.MobileAction"></action>
          </actions>
          </config>

                這里即定義了Action,也定義了自己的Router,并且從名稱上可以看出,這個(gè)SessionCheckRouter是要判斷所有提交到服務(wù)器的指定url的請(qǐng)求 是否已經(jīng)登錄過,沒有登錄,可能會(huì)把此請(qǐng)求遣送會(huì)登錄頁(yè)。以及初始化所有的Action,在Router處理完請(qǐng)求,分發(fā)給action時(shí),可以從filter里面去取。

           


          posted @ 2011-11-25 12:35 馬航 閱讀(274) | 評(píng)論 (0)編輯 收藏

                承接上篇的簡(jiǎn)單介紹,下面詳細(xì)介紹整個(gè)框架的大致結(jié)構(gòu)。

                先來看一下整個(gè)框架包的結(jié)構(gòu):

           

                可以看出框架包含的包很少,包的結(jié)構(gòu)也超簡(jiǎn)單。這里 涉及Filter、ActionSupport、Router等三個(gè)概念,他們之間的關(guān)系,通過下圖來表示:

           

                圖也不規(guī)范,說不上來是哪個(gè)UML圖,不過通過它也能看出一個(gè)請(qǐng)求到達(dá)時(shí),框架基本的處理流程。首先由Filter攔截到所有請(qǐng)求,然后把請(qǐng)求交給所有注冊(cè)的Router類,如果請(qǐng)求的Url正好是一個(gè)Router要攔截的,則把此請(qǐng)求交給這個(gè)Router,框架不再把請(qǐng)求向下傳遞。Router得到請(qǐng)求后,分析Url,通過Url里的信息把請(qǐng)求交給對(duì)應(yīng)的ActionSupport的子類來處理。

                這里攔截采用Filter來處理,這跟多數(shù)的web框架一樣,使用FilterServlet有更多的能力進(jìn)行請(qǐng)求的分發(fā)。首先在一個(gè)web工程的web.xml文件中配置框架的UrlFilter類來攔截所有的請(qǐng)求。需要注意的一點(diǎn)是dispatcher 要設(shè)置為request,如果設(shè)置了forward的話,由框架內(nèi)部進(jìn)行的forward又會(huì)被框架攔截,從而造成無限的循環(huán)。Url-pattern設(shè)置為/*,表示所有的請(qǐng)求都會(huì)攔截,從而把對(duì)url分發(fā)的權(quán)利交由框架本身,而不是采用jsp規(guī)范里的url分發(fā)策略。框架在處理所有請(qǐng)求的url 時(shí),依次交給各個(gè)Router類來處理,如果Router類判斷是符合自己的url格式,則分發(fā)給 action 處理。如果不能處理再交給下一級(jí)的Router,最后url經(jīng)由所有Router處理完,剩下的資源文件的url,如http://xxx.xxx.xxx.jpg,則框架調(diào)用filterdoChain()方法,通過filter的過濾去訪問web里的資源。

          <filter>

                <filter-name>unicornWeb</filter-name>

                <filter-class>com.mh.mvc.filter.UrlFilter</filter-class>

          </filter>

          <filter-mapping>

              <filter-name>unicornWeb</filter-name>

              <url-pattern>/*</url-pattern>

              <dispatcher>REQUEST</dispatcher>

          </filter-mapping>

                大致的原理就是這樣,在下篇介紹框架的詳細(xì)實(shí)現(xiàn)。

          posted @ 2011-11-25 11:43 馬航 閱讀(350) | 評(píng)論 (0)編輯 收藏

                我承認(rèn)有點(diǎn)標(biāo)題黨了,不過題目中所說的幾項(xiàng)技術(shù)確實(shí)有其相似之處,欲知事情原委,且聽我詳細(xì)道來。

                項(xiàng)目一開始只是不滿 struts 龐大的體積,于是想自己根據(jù)其原理實(shí)現(xiàn)一個(gè)tiny 版。后來的開發(fā)中覺得,完全可以把上述的ajax、Restuful web service的一些思想加入進(jìn)來。經(jīng)過幾周的努力,便開發(fā)出了一個(gè)基本成型的web 框架,暫且起名為unicorn(獨(dú)角獸,吼吼)。下文開始便對(duì)這個(gè)自編寫的框架做一些列的介紹,并且初步打算是將其開源,希望能一起交流和完善它。

                首先,為了能快速了解它是什么,先來看一下配置文件:

          <?xml version="1.0" encoding="UTF-8" ?>
          <config>
          <routers>
          <router class="com.mh.router.MySessionCheckRouter"></router>
          </routers>
          <actions>
          <action class="com.mh.action.UserAction"></action>
          <action class="com.mh.action.InformationAction"></action>
          <action class="com.mh.action.MobileAction"></action>
          </actions>
          </config>

                上面就是整個(gè)工程的配置文件,可以看出需要配置的東西非常少,只需要制定action類 和 router類有哪些就Ok。框架奉行約定大于配置的思想,至于請(qǐng)求如何分發(fā),這個(gè)不需要人工配置,框架自動(dòng)解決。這里要介紹兩個(gè)概念A(yù)ction 和 Router,熟悉Struts的肯定都知道Action,Action替代Servlet、JSP時(shí)代的Servlet,所有提交的請(qǐng)求由struts分發(fā)給不同的Action來處理。這里道理也是一樣的,Action就是經(jīng)過框架處理后的請(qǐng)求接受者。再來說一下Router,字面意思路由器,學(xué)過計(jì)算機(jī)網(wǎng)絡(luò)的都知道,ip數(shù)據(jù)包在網(wǎng)絡(luò)上之所以能夠順利到達(dá),就是因?yàn)槁酚善鞲鶕?jù)路由表來來確定出來傳輸?shù)耐緩健_@里Router也是這個(gè)作用,根據(jù)訪問服務(wù)器的URL來制定分發(fā)策略。Router是完全可以自定義的,用戶可以定義自己的Router來制定URL分發(fā)的策略,并且用戶自定義的Router比系統(tǒng)默認(rèn)的Router有更高的優(yōu)先權(quán)。

          二、URL上做文章

          /UserAction/login/username=admin&&password=admin

                先來介紹系統(tǒng)MethodRouter的處理方式。上面的url根據(jù)"/"分為三個(gè)部分,第一部分是請(qǐng)求的Action類,第二部分是類中的方法Method,第三部分是提交的參數(shù)Param。這一點(diǎn)受上篇文章優(yōu)酷的架構(gòu)里URL設(shè)計(jì)的啟發(fā)。

                經(jīng)過這樣的設(shè)計(jì),就明白在上述配置文件中為何可以如此簡(jiǎn)單了。

                當(dāng)然也可以不以這樣的方式,框架提供自定義Router的支持。比如你想這樣處理URL:/前臺(tái)頁(yè)面/子欄目/子欄目

                想實(shí)現(xiàn)上面的方式,就可以自己定義Router,在Router里面獲取上述的URL,然后做處理、forward到相應(yīng)的jsp頁(yè)面。

          三、使用Json傳輸數(shù)據(jù)

                Ajax請(qǐng)求很容易處理json數(shù)據(jù),ajax可以與系統(tǒng)輕松交互。

                當(dāng)初Web Service使用SOAP的xml格式傳輸數(shù)據(jù),如今也有人指責(zé)這是大費(fèi)周折。Restful方式提倡遵循HTTP語(yǔ)義,完全使用URL結(jié)合GET、POST、PUT、DELETE來傳輸請(qǐng)求,結(jié)果在roil陣營(yíng)里廣泛使用,認(rèn)為是web service更優(yōu)雅的方式。所以本框架也吸取他們的優(yōu)點(diǎn),也完全可以通過url傳輸請(qǐng)求的數(shù)據(jù),如上述URL中的Param部分。不過沒有遵循Restful強(qiáng)調(diào)的Http語(yǔ)義,全部使用Get和POST的請(qǐng)求方式,當(dāng)然也可以制定為其他,這完全看你的心情,因?yàn)檫@對(duì)功能實(shí)現(xiàn)無關(guān)緊要。而且我覺得統(tǒng)一使用一種,更避免了需要指定請(qǐng)求方式的麻煩。

                數(shù)據(jù)的返回使用json格式,比SOAP更為輕量簡(jiǎn)潔和優(yōu)雅,而且有更多的平臺(tái)直接支持。如在android平臺(tái),本身就支持json格式的處理, 如果使用web service 的SOAP,你可能還要導(dǎo)入KSOAP的第三方庫(kù)。

                在非瀏覽器的客戶端,可以借助編寫的工具類,來完成web service方式的操作,

          public interface IWebService {
          	public List<LiteInformationDTO> getInformationsOfOwnerApp(String ownerApp, int start, int limit) throws SocketTimeoutException;
          }
          

                經(jīng)過這樣的封裝,已經(jīng)與使用web service毫無差別,而且還會(huì)更加高效,因?yàn)樘幚韏son總比處理SOAP的xml要容易。

           

                先簡(jiǎn)單寫這么多,之后的續(xù)篇詳細(xì)介紹。

          posted @ 2011-11-22 16:22 馬航 閱讀(285) | 評(píng)論 (0)編輯 收藏

          記得以前給大家介紹過視頻網(wǎng)站龍頭老大YouTube的技術(shù)架構(gòu),相信大家看了都會(huì)有不少的感觸,互聯(lián)網(wǎng)就是這么一個(gè)神奇的東西。今天我突然想到,優(yōu)酷網(wǎng)在國(guó)內(nèi)也算是視頻網(wǎng)站的老大了,不知道他的架構(gòu)相對(duì)于YouTube是怎么樣的,于是帶著這個(gè)好奇心去網(wǎng)上找了優(yōu)酷網(wǎng)架構(gòu)的各方面資料,雖然談得沒有YouTube那么詳細(xì),但多少還是挖掘了一點(diǎn),現(xiàn)在總結(jié)一下,希望對(duì)喜歡架構(gòu)的朋友有所幫助。

          一、網(wǎng)站基本數(shù)據(jù)概覽

          • 據(jù)2010年統(tǒng)計(jì),優(yōu)酷網(wǎng)日均獨(dú)立訪問人數(shù)(uv)達(dá)到了8900萬,日均訪問量(pv)更是達(dá)到了17億,優(yōu)酷憑借這一數(shù)據(jù)成為google榜單中國(guó)內(nèi)視頻網(wǎng)站排名最高的廠商。
          • 硬件方面,優(yōu)酷網(wǎng)引進(jìn)的戴爾服務(wù)器主要以 PowerEdge 1950與PowerEdge 860為主,存儲(chǔ)陣列以戴爾MD1000為主,2007的數(shù)據(jù)表明,優(yōu)酷網(wǎng)已有1000多臺(tái)服務(wù)器遍布在全國(guó)各大省市,現(xiàn)在應(yīng)該更多了吧。

          二、網(wǎng)站前端框架

          從一開始,優(yōu)酷網(wǎng)就自建了一套CMS來解決前端的頁(yè)面顯示,各個(gè)模塊之間分離得比較恰當(dāng),前端可擴(kuò)展性很好,UI的分離,讓開發(fā)與維護(hù)變得十分簡(jiǎn)單和靈活,下圖是優(yōu)酷前端的模塊調(diào)用關(guān)系:

          這樣,就根據(jù)module、method及params來確定調(diào)用相對(duì)獨(dú)立的模塊,顯得非常簡(jiǎn)潔。下面附一張優(yōu)酷的前端局部架構(gòu)圖:

           

          三、數(shù)據(jù)庫(kù)架構(gòu)

          應(yīng)該說優(yōu)酷的數(shù)據(jù)庫(kù)架構(gòu)也是經(jīng)歷了許多波折,從一開始的單臺(tái)MySQL服務(wù)器(Just Running)到簡(jiǎn)單的MySQL主從復(fù)制、SSD優(yōu)化、垂直分庫(kù)、水平sharding分庫(kù),這一系列過程只有經(jīng)歷過才會(huì)有更深的體會(huì)吧,就像MySpace的架構(gòu)經(jīng)歷一樣,架構(gòu)也是一步步慢慢成長(zhǎng)和成熟的。

          1、簡(jiǎn)單的MySQL主從復(fù)制:

          MySQL的主從復(fù)制解決了數(shù)據(jù)庫(kù)的讀寫分離,并很好的提升了讀的性能,其原來圖如下:

          其主從復(fù)制的過程如下圖所示:

          但是,主從復(fù)制也帶來其他一系列性能瓶頸問題:

          1. 寫入無法擴(kuò)展
          2. 寫入無法緩存
          3. 復(fù)制延時(shí)
          4. 鎖表率上升
          5. 表變大,緩存率下降

          那問題產(chǎn)生總得解決的,這就產(chǎn)生下面的優(yōu)化方案,一起來看看。

           

          2、MySQL垂直分區(qū)

          如果把業(yè)務(wù)切割得足夠獨(dú)立,那把不同業(yè)務(wù)的數(shù)據(jù)放到不同的數(shù)據(jù)庫(kù)服務(wù)器將是一個(gè)不錯(cuò)的方案,而且萬一其中一個(gè)業(yè)務(wù)崩潰了也不會(huì)影響其他業(yè)務(wù)的正常進(jìn)行,并且也起到了負(fù)載分流的作用,大大提升了數(shù)據(jù)庫(kù)的吞吐能力。經(jīng)過垂直分區(qū)后的數(shù)據(jù)庫(kù)架構(gòu)圖如下:

          然而,盡管業(yè)務(wù)之間已經(jīng)足夠獨(dú)立了,但是有些業(yè)務(wù)之間或多或少總會(huì)有點(diǎn)聯(lián)系,如用戶,基本上都會(huì)和每個(gè)業(yè)務(wù)相關(guān)聯(lián),況且這種分區(qū)方式,也不能解決單張表數(shù)據(jù)量暴漲的問題,因此為何不試試水平sharding呢?

           

          3、MySQL水平分片(Sharding)

          這是一個(gè)非常好的思路,將用戶按一定規(guī)則(按id哈希)分組,并把該組用戶的數(shù)據(jù)存儲(chǔ)到一個(gè)數(shù)據(jù)庫(kù)分片中,即一個(gè)sharding,這樣隨著用戶數(shù)量的增加,只要簡(jiǎn)單地配置一臺(tái)服務(wù)器即可,原理圖如下:

          如何來確定某個(gè)用戶所在的shard呢,可以建一張用戶和shard對(duì)應(yīng)的數(shù)據(jù)表,每次請(qǐng)求先從這張表找用戶的shard id,再?gòu)膶?duì)應(yīng)shard中查詢相關(guān)數(shù)據(jù),如下圖所示:

          但是,優(yōu)酷是如何解決跨shard的查詢呢,這個(gè)是個(gè)難點(diǎn),據(jù)介紹優(yōu)酷是盡量不跨shard查詢,實(shí)在不行通過多維分片索引、分布式搜索引擎,下策是分布式數(shù)據(jù)庫(kù)查詢(這個(gè)非常麻煩而且耗性能)

           

          四、緩存策略

          貌似大的系統(tǒng)都對(duì)“緩存”情有獨(dú)鐘,從http緩存到memcached內(nèi)存數(shù)據(jù)緩存,但優(yōu)酷表示沒有用內(nèi)存緩存,理由如下:

          1. 避免內(nèi)存拷貝,避免內(nèi)存鎖
          2. 如接到老大哥通知要把某個(gè)視頻撤下來,如果在緩存里是比較麻煩的

          而且Squid 的 write() 用戶進(jìn)程空間有消耗,Lighttpd 1.5 的 AIO(異步I/O) 讀取文件到用戶內(nèi)存導(dǎo)致效率也比較低下。

          但為何我們?cè)L問優(yōu)酷會(huì)如此流暢,與土豆相比優(yōu)酷的視頻加載速度略勝一籌?這個(gè)要?dú)w功于優(yōu)酷建立的比較完善的內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN),它通過多種方式保證分布在全國(guó)各地的用戶進(jìn)行就近訪問——用戶點(diǎn)擊視頻請(qǐng)求后,優(yōu)酷網(wǎng)將根據(jù)用戶所處地區(qū)位置,將離用戶最近、服務(wù)狀況最好的視頻服務(wù)器地址傳送給用戶,從而保證用戶可以得到快速的視頻體驗(yàn)。這就是CDN帶來的優(yōu)勢(shì),就近訪問,有關(guān)CDN的更多內(nèi)容,請(qǐng)大家Google一下。

          好了,就總結(jié)這么多了,有興趣的同學(xué)接著補(bǔ)充,雖然很多資料圖片都來自網(wǎng)絡(luò),但整理也不容易,歡迎轉(zhuǎn)載,轉(zhuǎn)載留個(gè)出處:青藤屋 原文鏈接

          posted @ 2011-11-02 11:23 馬航 閱讀(289) | 評(píng)論 (0)編輯 收藏

          windows系統(tǒng)使我們經(jīng)常使用的操作系統(tǒng)怎么才能使用我們現(xiàn)在經(jīng)常使用的操作系統(tǒng)不變的情況下繼續(xù)我們的SVN之旅,我們?cè)诰C合了好動(dòng)種方法的同時(shí)感覺這些內(nèi)容非常貼近我們SVN在Windows種的應(yīng)用與配置.

          1.下載文件,

          下載最新版本subversion,我這里選擇svn-1.4.5-setup.exe

          下載 "Subversion Windows Service" 軟件包

          下載 TortoiseSVN shell integration utility

          2.安裝Subversion 服務(wù)器

          由于我下載的是setup.exe版本,安裝程序安裝后會(huì)自動(dòng)設(shè)置系統(tǒng)變量.如果你下載的是zip版就需要手動(dòng)設(shè)置系統(tǒng)變量.

          setup.exe版直接安裝就可以了.安裝到D:/Program Files/Subversion

          首先創(chuàng)建SVN儲(chǔ)存庫(kù)(repository)

          svnadmin create F:/svn/

          repository創(chuàng)建完畢后會(huì)在目錄下生成若干個(gè)文件和文件夾,dav目錄是提供給Apache與mod_dav_svn使用的目錄,讓它們存儲(chǔ)內(nèi)部數(shù)據(jù);db目錄就是所有版本控制的數(shù)據(jù)文件;hooks目錄放置hook腳本文件的目錄;locks用來放置Subversion文件庫(kù)鎖定數(shù)據(jù)的目錄,用來追蹤存取文件庫(kù)的客戶端;format文件是一個(gè)文本文件,里面只放了一個(gè)整數(shù),表示當(dāng)前文件庫(kù)配置的版本號(hào);

          3.配置SVN服務(wù)器

          (這個(gè)位置就是在你建儲(chǔ)存庫(kù)的地方F:/svn)

          打開/conf/目錄,打開svnserve.conf找到一下兩句:

          # [general]

          # password-db = passwd

          # anon-access = none

          # auth-access = write

          去之每行開頭的#,其中第二行是指定身份驗(yàn)證的文件名,即passwd文件.anon-access = none 是匿名用戶不能訪問,必須要有用戶名和密碼。(注意:?jiǎn)栴}就出在這,一定要注意格式去掉注釋后要頂格不能有空)

          同樣打開passwd文件,將

          # [users]

          # harry = harryssecret

          # sally = sallyssecret

                 格式為“用戶名 = 密碼”,如可插入一行:admin = admin888,即為系統(tǒng)添加一個(gè)用戶名為admin,密碼為admin888的用戶

          4.運(yùn)行SVN服務(wù)器

          運(yùn)行SVN服務(wù)

          在命令行執(zhí)行

          svnserve --daemon --root F:/svn

          服務(wù)啟動(dòng),--daemon可簡(jiǎn)寫為-d,--root可簡(jiǎn)寫為-r,可以建立一個(gè)批處理文件并放在windows啟動(dòng)組中便于開機(jī)就運(yùn)行SVN服務(wù)(注意:這是臨時(shí)打開的服務(wù),命令執(zhí)行后不能關(guān)閉窗口)

          也可以制定subversion工作的端口:svnserve -d -r f:/svn --listen-port 9999

          用后臺(tái)服務(wù)的方式可以設(shè)置開機(jī)自動(dòng)執(zhí)行。

          D:/Program Files/Subversion/bin>sc create svnservice binpath= "C:/Program Files/Subversion/bin/svnserve.exe --service -r f:/svn  --listen-port 9999"

          就可以用net svnservice stop 或者start來啟動(dòng)服務(wù)了 也可以在Sevices.msc來啟動(dòng)了。

          5、用客戶端訪問

          格式:svn://服務(wù)器IP

           

          ---------------------------------------------------------------------------------------------------------

           

          基于svnserve的服務(wù)器,權(quán)限文件authz配置的常見問題及解答  
           最近在我用Subversion論壇(http://www.iUseSVN.com/bbs)經(jīng)常有人提到這樣的問題: 
          為什么我的客戶端沒有寫權(quán)限? 
          為什么我的權(quán)限沒有起作用?

          總結(jié)他們的配置,發(fā)現(xiàn) 
          都是用svnserve作為服務(wù)器, 
          都在svnserve.conf中使用了authz-db選項(xiàng)

          原因可能如下:

          1,配置authz時(shí),沒有注意svnserve啟動(dòng)參數(shù)-r所指定的目錄。 
          這里有兩種情況: 
          A:-r直接指定到版本庫(kù)(稱之為單庫(kù)svnserve方式) 
          比如,有一個(gè)庫(kù)project1,位于D:/svn/project1 
          使用以下命令啟動(dòng)svnserve

          [Copy to clipboard] [ - ]CODE: 
          svnserve -d -r D:/svn/project1 
          在這種情況下,一個(gè)svnserve只能為一個(gè)版本庫(kù)工作 
          authz文件如果配置成下面這樣就是錯(cuò)的,

          [Copy to clipboard] [ - ]CODE: 
          [groups] 
          admin=user1 
          dev=user2 
          [project1:/] 
          @admin=rw 
          @doc=r 
          應(yīng)該配置成

          [Copy to clipboard] [ - ]CODE: 
          [groups] 
          admin=user1 
          dev=user2 
          [/] 
          @admin=rw 
          @doc=r 
          因?yàn)閇project1:/]表示庫(kù)project1的根目錄,而按上面的啟動(dòng)參數(shù),是沒有庫(kù)的概念的。 
          使用類似這樣的URL:svn://192.168.0.1/ 即可訪問project1

          B:-r指定到版本庫(kù)的上級(jí)目錄(稱之為多庫(kù)svnserve方式) 
          同樣,有一個(gè)庫(kù)project1,位于D:/svn/project1 
          如果使用以下命令啟動(dòng)svnserve

          [Copy to clipboard] [ - ]CODE: 
          svnserve -d -r D:/svn 
          這種情況,一個(gè)svnserve可以為多個(gè)版本庫(kù)工作, 
          這時(shí)如果想限制指定庫(kù)的指定目錄,就應(yīng)該指定具體的庫(kù),像這樣

          [Copy to clipboard] [ - ]CODE: 
          [groups] 
          admin=user1 
          dev=user2 
          [project1:/] 
          @admin=rw 
          @doc=r 
          如果此時(shí)你還用[/],則表示所有庫(kù)的根目錄,同理,[/src]表示所有庫(kù)的根目錄下的src目錄 
          使用類似這樣的URL:svn://192.168.0.1/project1 即可訪問project1 
          這樣的URL:svn://192.168.0.1/project2 即可訪問project2

          2,對(duì)中文目錄進(jìn)行權(quán)限控制時(shí),沒有將權(quán)限文件authz改為utf-8格式。

          svn對(duì)于非英文文件名和目錄名使用utf-8格式編碼處理,要對(duì)中文目錄進(jìn)行正確控制, 
          應(yīng)該使用無BOM的utf-8格式,如何將默認(rèn)的文件轉(zhuǎn)為utf-8, 
          我使用的是UltraEdit的菜單"ASCII to UTF-8 (Unicode Editing)"。在UltraEdit的配置中,可以設(shè)置有無BOM  

          posted @ 2011-10-12 17:26 馬航 閱讀(424) | 評(píng)論 (0)編輯 收藏

          SIP協(xié)議

           

          SIP協(xié)議過程概念及分析

           

          SIP入門開發(fā)之路(含SIP開發(fā)需要學(xué)習(xí)的資源及網(wǎng)址)

           

          SIP揭密(中文版)

           

          使用Java的SIP Servlet進(jìn)行SIP開發(fā)

           

           

          Asterisk:

           

          Asterisk安裝及測(cè)試

           

          Asterisk十問十答

           

          Asterisk入門教程

           

          Asterisk介紹-Asterisk RealTime SIP

          asterisk配置文件列表及常用指令

           

          asterisk 官方文檔

           

          asterisk目錄及配置說明

           

          Asterisk功能整理

           

          Asterisk使用ODBC實(shí)現(xiàn)語(yǔ)音信箱

           

          使用Asterisk實(shí)現(xiàn)可視的語(yǔ)音交換

           

           

          OpenSIPS

           

          開源SIP服務(wù)器OpenSIPS應(yīng)用介紹

           

          Opensips 安裝

           

          Opensips 配置文件

           

          Mediaproxy的安裝及其在OpenSIPS中的配置

           

          Opensips文檔之MediaProxy模塊

           

          使用OpenSIPS構(gòu)建電話通信系統(tǒng)-8媒體服務(wù)整合

           

          使用OpenSIPS構(gòu)建電話通信系統(tǒng)-4腳本及路由基礎(chǔ)

           

          Opensips文檔之TM模塊

           

          Opensips文檔之RR模塊

           

          Opensips文檔之TEXTOPS 模塊

           

          Opensips文檔之AVPOPS模塊

           

           

          NAT穿透(即SIP打洞)

           

          使用OpenSIPS構(gòu)建電話通信系統(tǒng)-SIP穿透NAT

           

          NAT穿透問題探討

           

          完美的NAT穿透技術(shù)ICE介紹

           

          ICE-SIP穿透NAT問題的終極解決方案

           

          NAT穿透技術(shù)ICE基礎(chǔ)教程

          posted @ 2011-10-07 20:31 馬航 閱讀(455) | 評(píng)論 (0)編輯 收藏

               摘要: Android中有一控件是ExpandableListView,比ListView更高級(jí),ExpandableListView的效果很實(shí)用,比如因?yàn)樾枰榭匆欢盐募哪夸浗Y(jié)構(gòu)或者開發(fā)像QQ好友那樣的界面,就應(yīng)該使用Expandablelistview。 本文最終效果如下: 首先是Activity代碼,實(shí)際開發(fā)中數(shù)據(jù)(包括父item,子item及圖片,Expandablelistview...  閱讀全文

          posted @ 2011-10-06 09:37 馬航 閱讀(5471) | 評(píng)論 (0)編輯 收藏

          前言:做完了手機(jī)全能播放器的項(xiàng)目, 又要告別幾個(gè)月來并肩作戰(zhàn),即將去北京發(fā)展的Manager zhu。準(zhǔn)備把做過的3GP/FLV/AVI格式整理一遍, 算是對(duì)幾個(gè)月辛苦成果的總結(jié), 也為后來者提供一些參考。

          1. 概述

          流行的文件格式背后都有大公司的支持。FLV得益于ADOBE公司推動(dòng)的網(wǎng)絡(luò)視頻分享風(fēng)潮,而AVI則是MICROSOFT首創(chuàng)的RIFF即視頻和音頻交織在一起同步播放。 3GP/MP4是APPLE提出并得到ISO標(biāo)準(zhǔn)支持作為NOKIA等手機(jī)的默認(rèn)視頻格式。3GP是MP4格式在手機(jī)上的簡(jiǎn)化版。MP4的codec組合一般是mpeg4 + AAC, 3GP則按版本演進(jìn)分為3gpp r5(h.263/mpeg4 + AMR-NB/AMR WB), 3gpp r6(增加h.264視頻和aacPlus音頻支持)。

          有人會(huì)把MP4和MPEG4搞混, 前者是文件容器(container),后者是視頻編碼格式, 容器的作用是把壓縮編碼后的視頻和音頻數(shù)據(jù)盡可能緊湊的排布,就好像阿甘的巧克力盒子,你并不知道盒子里有什么, 但你可以按照既定的線索解開文件,取出你需要的數(shù)據(jù)。

          文件格式一般包括以下三要素:

          header: 標(biāo)記文件類型,音視頻碼流的基本屬性信息
          index: 索引表,每個(gè)frame有對(duì)應(yīng)的offset,size,timestamp.
          stream: 真正的音視頻流數(shù)據(jù)。
          任何文件格式都應(yīng)該有以上3要素。 當(dāng)然AVI視頻沒有索引也能播放,但不能拖放seek,需要自己重建索引。解析器(demuxer)根據(jù)frame_id找到其在文件中的offset和size,然后讀取出來解碼并播放。

          2. 文件格式分析

          下面來分析一下3GP/MP4文件格式。APPLE的格式有2個(gè)特點(diǎn),1. 排布緊湊幾乎沒有冗余數(shù)據(jù)(AVI則有很多junk數(shù)據(jù)),2.音視頻碼流數(shù)據(jù)可隨意存放而不需按時(shí)間順序排布。

          3gp文件由一系列的box(atom)組成。每個(gè)box的結(jié)構(gòu)都是4字節(jié)的size,4字節(jié)的type, 還有一些data數(shù)據(jù)。用mp4info查看3gp文件的數(shù)據(jù)排布如下圖:

          如上圖, ftyp是表示文件的版本信息, mdat存放文字,音視頻等數(shù)據(jù)。你可能要問,這些音視頻數(shù)據(jù)怎么找到呢? 是通過moov box里的子box trak,里面存放著音視頻的屬性描述以及每個(gè)sample的索引。

          3. 關(guān)于sample atoms

             video和audio的碼流屬性(如視頻width/height,codec id, 音頻采樣率聲道數(shù)等)存放在stsd box里; 下面著重介紹MP4高效壓縮的精華:stts,stss,stsc,stsz,stco五個(gè)box。對(duì)比AVI的索引表是每個(gè)sample都有對(duì)應(yīng)的id,flag,offset,size,3GP的高效索引方式可以把AVI轉(zhuǎn)碼成同碼率的MP4后,文件size減小成原來的20-30%!

          1. stts atom(time to sample atoms,見quicktime format 文檔圖2-28 標(biāo)準(zhǔn)文檔點(diǎn)擊下載): 存儲(chǔ)了sample的時(shí)間信息。stts能讓很方便的根據(jù)timestamp找到對(duì)應(yīng)的sample,或者獲取某個(gè)sample對(duì)應(yīng)的timestamp. sttstable記錄著有相同duration的sample的數(shù)量count和時(shí)長(zhǎng)dutation。

          2. stss atom(sync sample atom,見文檔圖2-31): 存儲(chǔ)了每個(gè)關(guān)鍵幀的sample id。 stss能讓你很方便的找到當(dāng)前幀最近的關(guān)鍵幀。

          3. stsc atom(sample to chunk atom): sample存放在chunk里為了允許優(yōu)化的數(shù)據(jù)讀取。比如音頻sample size都很?。╝mr-nb sample size為32字節(jié)), 每次讀取一個(gè)sample開銷太大, 可一次性讀所在chunk里一堆sample。

          4. stsz atom(sample size atom): stsz可以描述每個(gè)sample的size.

          5. stco atom(chunk offset atoms): stco描述了每個(gè)chunk在文件中的絕對(duì)偏移位置。該offset可以是32位的

          也可以是64位的,后者用來支持處理超大文件。

          4 .使用sample atoms來處理播放流程

          · 查找sample         

          1.確定時(shí)間,相對(duì)于媒體時(shí)間坐標(biāo)系統(tǒng)

          2.檢查time-to-sample atom來確定給定時(shí)間的sample序號(hào)。

          3.檢查sample-to-chunk atom來發(fā)現(xiàn)對(duì)應(yīng)該sample的chunk。

          4.從chunk offset atom中提取該trunk的偏移量。

          5.利用sample size atom找到sample在trunk內(nèi)的偏移量和sample的大小。

          例如,如果要找第1秒的視頻數(shù)據(jù),過程如下:

          1. 第1秒的視頻數(shù)據(jù)相對(duì)于此電影的時(shí)間為600

          2. 檢查time-to-sample atom,得出每個(gè)sample的duration是40,從而得出需要尋找第600/40 = 15 + 1 = 16個(gè)sample

          3. 檢查sample-to-chunk atom,得到該sample屬于第5個(gè)chunk的第一個(gè)sample,該chunk共有4個(gè)sample

          4. 檢查chunk offset atom找到第5個(gè)trunk的偏移量是20472

          5. 由于第16個(gè)sample是第5個(gè)trunk的第一個(gè)sample,所以不用檢查sample size atom,trunk的偏移量即是該sample的偏移量20472。如果是這個(gè)trunk的第二個(gè)sample,則從sample size atom中找到該trunk的前一個(gè)sample的大小,然后加上偏移量即可得到實(shí)際位置。

          6. 得到位置后,即可取出相應(yīng)數(shù)據(jù)進(jìn)行解碼,播放

          ·       查找關(guān)鍵幀      

          查找過程與查找sample的過程非常類似,只是需要利用sync sample atom來確定key frame的sample序號(hào)

          確定給定時(shí)間的sample序號(hào) 
          檢查sync sample atom來發(fā)現(xiàn)這個(gè)sample序號(hào)之后的key frame 
          檢查sample-to-chunk atom來發(fā)現(xiàn)對(duì)應(yīng)該sample的chunk 
          從chunk offset atom中提取該trunk的偏移量 
          利用sample size atom找到sample在trunk內(nèi)的偏移量和sample的大小


          5 .3GP/MP4相關(guān)資源

               quicktime file format specification: 最權(quán)威的格式文檔 點(diǎn)擊下載
               開源的3GP/MP4解析器: ffmpeg, GPAC, helix, google opencore等 

          posted @ 2011-10-03 10:54 馬航 閱讀(696) | 評(píng)論 (0)編輯 收藏

          Android開發(fā)又將帶來新一輪熱潮,很多開發(fā)者都投入到這個(gè)浪潮中去了,創(chuàng)造了許許多多相當(dāng)優(yōu)秀的應(yīng)用。其中也有許許多多的開發(fā)者提供了應(yīng)用開 源項(xiàng)目,貢獻(xiàn)出他們的智慧和創(chuàng)造力。學(xué)習(xí)開源代碼是掌握技術(shù)的一個(gè)最佳方式。下面推薦幾個(gè)應(yīng)用開源項(xiàng)目,這些項(xiàng)目不僅提供了優(yōu)秀的創(chuàng)意,也可以直接掌握 Android內(nèi)核的接口使用:

          1、Android團(tuán)隊(duì)提供的示例項(xiàng)目

          如果不是從學(xué)習(xí)Android SDK中提供的那些樣例代碼開始,可能沒有更好的方法來掌握在Android這個(gè)框架上開發(fā)。由Android的核心開發(fā)團(tuán)隊(duì)提供了15個(gè)優(yōu)秀的示例項(xiàng)目,包含了游戲、圖像處理、時(shí)間顯示、開始菜單快捷方式等。
          地址:http://code.google.com/p/apps-for-android/

          2、 Remote Droid

          RemoteDroid是一個(gè)Android應(yīng)用,能夠讓用戶使用自己的無線網(wǎng)絡(luò)使用無線鍵盤、觸摸屏操作手機(jī)。這個(gè)項(xiàng)目為開發(fā)者提供了如網(wǎng)絡(luò)連接、觸摸屏手指運(yùn)動(dòng)等很好的樣例。
          地址:http://code.google.com/p/remotedroid/

          3、 TorProxy和Shadow

          TorProxy應(yīng)用實(shí)現(xiàn)了Android手機(jī)無線電電傳通訊(TOR),和Shadow應(yīng)用一起使用,可以使用手機(jī)匿名上網(wǎng)。從該項(xiàng)目源代碼中,可以掌握socket連接、管理cookie等方法。
          地址:http://www.cl.cam.ac.uk/research/dtg/code/svn/android-tor/

          4、 Android SMSPopup

          SMSPopup可以截獲短信內(nèi)容顯示在一個(gè)泡泡形狀的窗口中。從這個(gè)項(xiàng)目中可以掌握到如何使用內(nèi)置的短信SMS接口。
          地址:http://code.google.com/p/android-smspopup/

          5、 Standup Timer

          Standup Timer應(yīng)用用于控制站立會(huì)議時(shí)間,類似秒表倒計(jì)時(shí),可以提醒每個(gè)人的講話時(shí)間已到,從而保證每個(gè)與會(huì)者使用時(shí)間一樣。從該項(xiàng)目的代碼中,可以學(xué)會(huì)如何使用時(shí)間函數(shù)。另外,這個(gè)項(xiàng)目的代碼是采用視圖view、模型model嚴(yán)格分離的設(shè)計(jì)思路。
          地址:http://github.com/jwood/standup-timer

          6、 Foursquare

          是Foursquare.com的一個(gè)客戶端應(yīng)用,該應(yīng)用主要分為兩個(gè)模塊:API(com.joelapenna.foursquare)和界面前端 (com.joelapenna.foursquared)兩部分。從該項(xiàng)目代碼中,可以學(xué)會(huì)如何同步、多線程、HTTP連接等技術(shù)。
          地址:http://code.google.com/p/foursquared/

          7、 Pedometer

          Pedometer應(yīng)用用于記錄你每天走路步數(shù)的。盡管記錄不一定精準(zhǔn),但是從這個(gè)項(xiàng)目中,可以學(xué)習(xí)幾個(gè)不同的技術(shù):加速器交互、語(yǔ)音更新、后臺(tái)運(yùn)行服務(wù)等。
          地址:http://code.google.com/p/pedometer/

          8、 OpenSudoku-android

          OpenSudoku是一個(gè)簡(jiǎn)單的九宮格數(shù)獨(dú)游戲。從代碼中可以學(xué)習(xí)到如何在視圖中顯示表格數(shù)據(jù),以及如何和一個(gè)網(wǎng)站交互等技術(shù)。
          地址:http://code.google.com/p/opensudoku-android/

          9、 ConnectBot

          ConnectBot是Android平臺(tái)的一個(gè)客戶端安全殼應(yīng)用。從該項(xiàng)目代碼中,可以學(xué)習(xí)到很多Android安全方面的內(nèi)容,這些是你在開發(fā)應(yīng)用時(shí)經(jīng)常需要考慮的安全問題。
          地址:http://code.google.com/p/connectbot/

          10、 WordPress的Android應(yīng)用

          當(dāng)然在最后不能不提WordPress的Android應(yīng)用了,這是WordPress官方開發(fā)團(tuán)隊(duì)提供的一個(gè)項(xiàng)目。從代碼中可以學(xué)習(xí)到XMLRPC調(diào)用(當(dāng)然還有更多的優(yōu)秀內(nèi)容)。
          地址:http://android.svn.wordpress.org/trunk/

          posted @ 2011-10-03 09:47 馬航 閱讀(482) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共4頁(yè): 1 2 3 4 下一頁(yè) 
          主站蜘蛛池模板: 崇仁县| 金湖县| 临湘市| 裕民县| 文成县| 巴塘县| 石河子市| 渑池县| 谢通门县| 凌海市| 松江区| 玉山县| 古丈县| 五莲县| 廉江市| 宜章县| 抚州市| 望城县| 调兵山市| 方正县| 城步| 灵武市| 合作市| 陆川县| 巴东县| 黄骅市| 凭祥市| 汉川市| 安达市| 五家渠市| 陈巴尔虎旗| 志丹县| 龙井市| 德昌县| 天祝| 兰考县| 巴塘县| 班戈县| 土默特左旗| 浦北县| 汉寿县|