首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

C++ template 学习归纳2

关于c++中的类模板,常见的形式为: template<typename T> class className{ //... }; 比如笔者在这里举一个例子: #include <iostream> #include<vector> #include <stdexcept> template<typename T> class stack{ private: std::vector<T> elems; public: void push(T const&); void pop(); T top() const; bool empty() const{ return elems.empty(); } }; template<typename T> void stack<T>::push(T const& a){ elems.push_back(a); } template<typename T> void stack<T>::pop(){ if(elems.empty()){ throw std::out_of_range("stack<T>::pop empty"); } return elems.pop_back(); } template<typename T> T stack<T>::top()const{ if(elems.empty()){ throw std::out_of_range("stack<T>::top empty"); } return elems.back(); } int main(){ return 0; } 从上面的代码可以看出,为了定义成员函数,我们使用了下面的形式,比如: template<typename T> void stack<T>::push(T const& a){ elems.push_back(a); } 本来在此处有一个问题的,也就是关于类成员函数的实现位置的问题,这个问题我们稍后会提到。 现在我们为了测试上面的例子可以使用下面的函数代码: int main(){ stack<int> intStack; intStack.push(1); intStack.push(2); std::cout<<intStack.top()<<std::endl; stack<std::string> strStack; strStack.push("hello"); std::cout<<strStack.top()<<std::endl; return 0; } 我们有些时候会遇到这样的代码,比如: stack<stack<int> > intStack; 在这种情况下,我们要注意就是两个“>”中间的那个空格,那个是必须的,否则的话,编译器会认为是“>>”。这一点大家写代码的时候要注意。 下面我们来看看类模板的特化: 当我们想特化一个类模板的时候,我们就需要用template<>开头,后面更上我们希望特化的代码。比如: template<> class myStack<std::string>{ //... }; 对于特化类模板而言,就如同编写普通的类成员函数一样,比如: void myStack<int>::pop(int const& a){ //... } 下面我们呢来看看类模板的局部特化。 例如对于下面的代码: template<typename T1,typename T2> class myClass{ /... }; 而言,以下几种像是的局部特化都是正确 的: //局部特化 两个参数一致 template<typename T> class myClass<T,T>{ //... }; //局部特化 第二个为int template<typename T> class myClass<T,int>{ //... }; //两个参数都为指针类型 template<typename T1,typename T2> class myClass<T1*,T2*>{ //... }; 在下面的例子中: myClass<int float>mif; //将使用<T1,T2> myClass<float,float>mif; //将使用<T> myClass<float,int>mif; //将使用<T,int> myClass<int*,double*>mp; //将使用<T1*,T2*> 大家要注意下面的错误代码, myClass<int,int>m; myClass<int*,int*>m; z这两个代码在上面的这个例子中都是错误的。前者因为会和myClass<T,T>产生二义性。后者会和myClass<T,T>产生二义性、使得编译器不知道应该匹配哪一个。 如果想解决上面的第二个二义性的话,我们可以专门特化下面的代码: template<typename T> class myClass<T*,T*>{ //... }; 接下来我们来看看预设模板参数: 我们先来看看代码再说,大家注意和上面的例子进行比较: #include <iostream> #include<vector> #include <deque> #include <cstdlib> #include <stdexcept> template<typename T,typename CONT=std::vector<T> > class stack{ private: CONT elems; public: void push(T const&); void pop(); T top() const; bool empty() const{ return elems.empty(); } }; template<typename T,typename CONT> void stack<T,CONT>::push(T const& a){ elems.push_back(a); } template<typename T,typename CONT> void stack<T,CONT>::pop(){ if(elems.empty()){ throw std::out_of_range("stack<T>::pop empty"); } return elems.pop_back(); } template<typename T,typename CONT> T stack<T,CONT>::top()const{ if(elems.empty()){ throw std::out_of_range("stack<T>::top empty"); } return elems.back(); } int main(){ try{ stack<int> intStack; stack<double,std::vector<double> > douStack; //注意,这里千万不能写下面的这一行代码: // stack<double,std::vector<int> > douStack; intStack.push(1); std::cout<<intStack.top()<<std::endl; douStack.push(1.1); std::cout<<douStack.top()<<std::endl; }catch(std::exception const& ex){ std::cerr<<ex.what()<<std::endl; return EXIT_FAILURE; } return 0; } 我们使用 stack<double,std::vector<double> > douStack; 来声明了一个double stack,他的内部使用的是std:eque<>来管理。 ============================================================================== 本文转自被遗忘的博客园博客,原文链接:http://www.cnblogs.com/rollenholt/archive/2012/03/07/2383729.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

c++ template归纳学习5

双重模板参数: 我们还是以前几篇中的Stack为例子i:代码如下: template <typename T, template <typename ELEM> class CONT = std::deque > class Stack { private: CONT<T> elems; // elements public: void push(T const&); // push element void pop(); // pop element T top() const; // return top element bool empty() const { // return whether the stack is empty return elems.empty(); } }; 在这段代码中,我们的第二个模板参数修改为:template <typename ELEM> class CONT 和往常一样,你可以使用class替代typename,但是CONT必须是class。 template <typename T, template <class ELEM> class CONT = std::deque> // OK class Stack { … }; but the following is not: template <typename T, template <typename ELEM> typename CONT = std::deque> class Stack { // ERROR … }; 由于上面例子中的ELEM实际中没有用到,其实是可以省略的。 我们实现一个成员函数看看: template <typename T, template <typename> class CONT> void Stack<T,CONT>::push (T const& elem) { elems.push_back(elem); // append copy of passed elem } 但是如果试图使用上面的stack,编译器会说deque不符合CONT的要求,问题在于,std中的deque要求不只是一个参数,第二个参数是一个配置器,他虽然有预设值,但是当它被用来匹配CONT参数的时候,预设值被编译器忽略了。 对于这个问题,我们可以修改,使得CONT参数要求一个带两个参数的容器。 template <typename T, template <typename ELEM, typename ALLOC = std::allocator<ELEM> > class CONT = std::deque> class Stack { private: CONT<T> elems; // elements … }; 最终版本如下: // basics/stack8.hpp #ifndef STACK_HPP #define STACK_HPP #include <deque> #include <stdexcept> #include <allocator> template <typename T, template <typename ELEM, typename = std::allocator<ELEM> > class CONT = std::deque> class Stack { private: CONT<T> elems; // elements public: void push(T const&); // push element void pop(); // pop element T top() const; // return top element bool empty() const { // return whether the stack is empty return elems.empty(); } // assign stack of elements of type T2 template<typename T2, template<typename ELEM2, typename = std::allocator<ELEM2> >class CONT2> Stack<T,CONT>& operator= (Stack<T2,CONT2> const&); }; template <typename T, template <typename,typename> class CONT> void Stack<T,CONT>::push (T const& elem) { elems.push_back(elem); // append copy of passed elem } template<typename T, template <typename,typename> class CONT> void Stack<T,CONT>::pop () { if (elems.empty()) { throw std::out_of_range("Stack<>::pop(): empty stack"); } elems.pop_back(); // remove last element } template <typename T, template <typename,typename> class CONT> T Stack<T,CONT>::top () const { if (elems.empty()) { throw std::out_of_range("Stack<>::top(): empty stack"); } return elems.back(); // return copy of last element } template <typename T, template <typename,typename> class CONT> template <typename T2, template <typename,typename> class CONT2> Stack<T,CONT>& Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2) { if ((void*)this == (void*)&op2) { // assignment to itself? return *this; } Stack<T2> tmp(op2); // create a copy of the assigned stack elems.clear(); // remove existing elements while (!tmp.empty()) { // copy all elements elems.push_front(tmp.top()); tmp.pop(); } return *this; } #endif // STACK_HPP 测试如下: // basics/stack8test.cpp #include <iostream> #include <string> #include <cstdlib> #include <vector> #include "stack8.hpp" int main() { try { Stack<int> intStack; // stack of ints Stack<float> floatStack; // stack of floats // manipulate int stack intStack.push(42); intStack.push(7); // manipulate float stack floatStack.push(7.7); // assign stacks of different type floatStack = intStack; // print float stack std::cout << floatStack.top() << std::endl; floatStack.pop(); std::cout << floatStack.top() << std::endl; floatStack.pop(); std::cout << floatStack.top() << std::endl; floatStack.pop(); } catch (std::exception const& ex) { std::cerr << "Exception: " << ex.what() << std::endl; } // stack for ints using a vector as an internal container Stack<int,std::vector> vStack; … vStack.push(42); vStack.push(7); std::cout << vStack.top() << std::endl; vStack.pop(); } 程序的输出为: 7 42 Exception: Stack<>::top(): empty stack 7 ============================================================================== 本文转自被遗忘的博客园博客,原文链接:http://www.cnblogs.com/rollenholt/archive/2012/03/08/2384921.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

C++ template学习总结6

对于基本类型来说,并没有一个default模式来讲他们初始化为有意义的值,没有初始化的变量,其指都是未定义的,但是在模板这一块呢?我们可以采用下面的形式: template <typename T> void foo() { T x = T(); // x is zero (or false)ifT is a built-in type } 对于class template我们可以采用下面例子的方式: template <typename T> class MyClass { private: T x; public: MyClass() : x() { // ensures that x is initialized even for built-in types } … }; 通过引用形式将字串字面常数传递给模板,有时候会遇到错误: #include <string> // note: reference parameters template <typename T> inline T const& max (T const& a, T const& b) { return a < b ? b : a; } int main() { std::string s; ::max("apple","peach"); // OK: same type ::max("apple","tomato"); // ERROR: different types ::max("apple",s); // ERROR: different types } 问题出在于这几个字串字面常数的长度不同,因此其底层的Array也是不同的。换句话说“apple” 是char const[6],”tomato“是char const[7]. 但是如果你使用传值形式的话,那么久是可以的。 #include <string> // note: nonreference parameters template <typename T> inline T max (T a, T b) { return a < b ? b : a; } int main() { std::string s; ::max("apple","peach"); // OK: same type ::max("apple","tomato"); // OK: decays to same type ::max("apple",s); // ERROR: different types } 这种方式之所以可以,是因为在引数的推倒过程中,只有当参数不是一个引用的时候,【array转换为pointer】的动作才会发生,这个规则我们用下面的例子来说明: #include <typeinfo> #include <iostream> template <typename T> void ref (T const& x) { std::cout << "x in ref(T const&): " << typeid(x).name() << '\n'; } template <typename T> void nonref (T x) { std::cout << "x in nonref(T): " << typeid(x).name() << '\n'; } int main() { ref("hello"); nonref("hello"); } 结果为: 其实在这个例子中,最好的方式是对string进行重载,这么做尤其必要的,如果不这么做的弧啊,对于两个字串字面常熟调用max()是合法的,但是max()会以operator<比较两个指针的大小,所比较的其实是两个指针的地址,而不是其字面值,这也就是为什么使用std::string比C-style字串要好的原因之一。 只有当你以by value的方式使用字串字面常熟的时候,字串底部的array才会被转型(退化)为一个字元指针。也就是array to pointer。 ============================================================================== 本文转自被遗忘的博客园博客,原文链接:http://www.cnblogs.com/rollenholt/archive/2012/03/08/2384977.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

JavaScript学习记录day1

JavaScritps 是什么? JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。 在Web世界里,只有JavaScript能跨平台、跨浏览器驱动网页,与用户交互。 编写JS的流程 布局:HTML+CSS 属性:确定要修改哪些属性 事件:确定用户做哪些操作(产品设计) 编写JS:在事件中,用JS来修改页面元素的样式 1. 快速入门 JavaScript代码可以直接嵌在网页的任何地方,不过通常我们都把JavaScript代码放到<head>中: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>我的第一个JS</title> <script> function myFirstScript() { alert("Hello word!"); } myFirstScript(); </script></head><body></body></html> 由<script>...</script>包含的代码就是JavaScript代码,它将直接被浏览器执行。 第二种方法是把JavaScript代码放到一个单独的.js文件,然后在HTML中通过引入这个文件: <html><head> <script src="/static/js/abc.js"></script></head><body> ...</body></html> 这样,/static/js/abc.js就会被浏览器执行。 把JavaScript代码放入一个单独的.js文件中更利于维护代码,并且多个页面可以各自引用同一份.js文件。 可以在同一个页面中引入多个.js文件,还可以在页面中多次编写<script> js代码... </script>,浏览器按照顺序依次执行。 2. 编程工具 我使用的是webstorm,这款工具非常好用,但是是收费软件,可以使用它的开源license。https://www.jetbrains.com/webstorm/ 3. 语法 JavaScript的语法和Java语言类似,每个语句以;结束,语句块用{...}。但是,JavaScript并不强制要求在每个语句的结尾加;,浏览器中负责执行JavaScript代码的引擎会自动在每个语句的结尾补上;。 注意:让JavaScript引擎自动加分号在某些情况下会改变程序的语义,导致运行结果与期望不一致。为了养成良好习惯,我们不要省略;,所有语句都添加;。 if (2 > 1) { x = 1; y = 2; z = 3; } 注意花括号{...}内的语句具有缩进,通常是4个空格。缩进不是JavaScript语法要求必须的,但缩进有助于我们理解代码的层次,所以编写代码时要遵守缩进规则。很多文本编辑器具有“自动缩进”的功能,可以帮助整理代码。 4. 注释 以//开头直到行末的字符被视为行注释,注释是给开发人员看到,JavaScript引擎会自动忽略: // 这是一行注释 alert('hello'); // 这也是注释//另一种块注释是用/*...*/把多行字符包裹起来,把一大“块”视为一个注释:/* 从这里开始是块注释 仍然是注释 仍然是注释 注释结束 */ 5. 大小写 请注意,JavaScript严格区分大小写,如果弄错了大小写,程序将报错或者运行不正常。 本文转自 ygqygq2 51CTO博客,原文链接:http://blog.51cto.com/ygqygq2/1917258,如需转载请自行联系原作者

优秀的个人博客,低调大师

SpringCloud学习之eureka集群配置

一。集群方案及部署思路: 如果是单节点的注册中心,是无法保证系统稳定性的,当然现在项目部署架构不可能是单节点的。 集群节点的部署思路:通过运行多个实例并请求他们相互注册,来完成注册中心的高可用性(结伴注册) 注意: 用于本机模拟的前期准备工作:将电脑上hosts 添加如下配置 (linux下位置:/etc/hosts): 127.0.0.1 localhost server1 server2 View Code 二 设计步骤 在这里简单创建一个项目:register-center-node1的项目工程,和我们先前的register-center项目工程一模一样 register-center-node1的application.yml配置: 1 server: 2 port: 8081 3 spring: 4 application: 5 name: register-center 6 eureka: 7 client: 8 service-url: 9 defaultZone: http://server1:8080/eureka 10 fetch-registry: true 11 instance: 12 hostname: server2 View Code register-center的application.yml配置: 1 server: 2 port: 8080 3 spring: 4 application: 5 name: register-center 6 eureka: 7 client: 8 service-url: 9 defaultZone: http://server2:8081/eureka 10 instance: 11 hostname: server1 View Code 注意以下几点: 与先前独立运行register-center不同,大家注意defaultZone属性,两个注册中心地址都指向对方进行结伴注册 去掉fetch-registry 与register-with-eureka配置(其实这样做就会取对应的默认值,两个值均为true) 启动第一个注册中心时会报Cannot execute request on any known server的错误,暂时不管它,实际上eureka注册中心的ui界面是能打开的 所有注册中心的节点的spring.application.name必须保持一致。 当需要往注册中心集群注册服务时的写法:defaultZone:http://server1:8080/eureka,http://server2:8081/eureka 启动完毕后,访问地址:http://localhost:8080 得到如下界面: 我们可以看到注册中心地址已经标记为集群模式了

优秀的个人博客,低调大师

Android被逼学习小例子1

这个程序的功能就是当点击图片的时候,就会自动切换到下一个图片。对,就是这么简单的一个功能,高手请不要鄙视。 主要的代码如下: HelloWorldActivity.java代码为: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package hello.com; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.LinearLayout; public class HelloWorldActivity extends Activity { /** Called when the activity is first created. */ int [] images = new int [] { R.drawable.ajax, R.drawable.java, R.drawable.ee, R.drawable.classic, R.drawable.xml, }; int currentImage = 0 ; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); LinearLayout mainLayout = (LinearLayout) findViewById(R.id.root); final ImageView image = new ImageView( this ); mainLayout.addView(image); image.setImageResource(images[ 0 ]); image.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { if (currentImage >= 4 ) { currentImage = - 1 ; } image.setImageResource(images[++currentImage]); } }); } } main.xml代码为: 1 2 3 4 5 6 7 <? xml version="1.0" encoding="utf-8"?> < LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > </ LinearLayout > 其余的没多大变化,只是图片放在drawable-mdpi目录下面。 运行结果为: ============================================================================== 本文转自被遗忘的博客园博客,原文链接:http://www.cnblogs.com/rollenholt/archive/2012/05/16/2505357.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

ABP理论学习之多租户

本篇目录 什么是多租户 ABP中的多租户 什么是多租户 维基百科:“软件多租户是指一种软件架构,在这种软件架构中,软件的一个实例运行在服务器上并且为多个租户服务”。一个租户是一组共享该软件实例特定权限的用户。有了多租户架构,软件应用被设计成为每个租户提供一个专用的实例包括该实例的数据的共享,还可以共享配置,用户管理,租户自己的功能和非功能属性。多租户和多实例架构相比,多租户分离了代表不同的租户操作的多个实例。 多租户用于创建Saas(Software as-a service)应用(云处理)。有几种类型的多租户: 多部署-多数据库 这实际上不是多租户。但是,如果我们为每个具有分开数据库的客户(租户)运行该应用的一个实例,那么我们可以在单个服务器上为多个租户提供服务。我们可以确定该应用的多个实例在相同的服务器环境不会相互冲突。 这个对于一个不是为多租户设计的已存在应用也是可能的。创建这么一个应用更容易,因为该应用不需要了解多租户。但这种方式存在安装,使用和维护问题。 单部署-多数据库 在这种情况下,我们可以在一个服务器上运行应用的单个实例。对于每个登录用户,我们从master database中检测该用户的租户,并获得该租户的数据库信息(连接字符串)。然后我们可以将连接字符串存储到像session一样的变量中,同时,使用这个租户特定的连接字符串执行所有的数据库操作。 某种程度上,这样的应用应该设计成多租户。但是大多数的应用都独立于多租户。这种方式也存在一些安装,使用和维护问题。我们应该为每个租户创建并维护一个分离的数据库。 单部署-单数据库 这是最真实的多租户架构:我们只将具有单个数据库应用的单个实例部署到单个服务器上。在(RDBMS)每个表中,都存在一个TenantId(或相似)字段,该字段用于分离每个租户之间的数据。 这种方法安装和维护都很简单,但唯独创建这么一个应用很难,因为我们必须要阻止一个租户读取或写入其他租户的数据。我们可以为每个数据库的读取(select)操作添加一个TenantId过滤器。而且,我们可以在每次写入的时候检查一下该实体是否和当前的租户相关。这是乏味而易于出错的,但ABP通过使用自动的数据过滤帮助我们处理这个事情。 如果我们有很多具有大量数据的租户,那么这种方法可能会有性能问题。我们可以使用关系型数据库的表分割特征或者将租户按组分到不同的服务器上。 ABP中的多租户 ABP提供了创建单部署,单数据库,多租户架构的基础设施。 开启多租户 多租户默认是关闭的。我们可以在模块的PreInitialize方法中开启,如下所示: Configuration.MultiTenancy.IsEnabled = true; 租主vs租户 首先,我们应该定义多租户系统中的两个条目: 租主(Host):租主是单例的(只有一个租主)。租主会对创建和管理租户负责。因此,一个“租主用户”比所有的租户等级更高,并独立于所有租户,同时还能控制他们。 租户(Tenant):租主的一个客户,具有自己的用户角色,权限,设置等。每个租户都可以完全独立于其他租户使用应用。一个多租户应用会有一个或多个租户。如果是一个CRM应用,那么不同的租户也有它们自己的账户,契约,产品和订单。因此,当我们说“**租户用户”的时候,意思就是一个租户拥有的用户。 Session ABP定义了一个获取当前用户和租户id的IAbpSession接口。该接口用于多租户获取当前的租户id。因此,它可以基于当前的租户id过滤数据。ABP中有以下规则: 如果UserId和TenantId都是null,那么当前的用户没有登录到系统。因此,我们可以不知道当前用户是否是一个租主用户还是一个租户用户。在这种情况下,用户不能访问授权的内容。 如果UserId不是null,TenantId是null,那么当前用户是一个租主用户。 如果UserId不是null,TenantId也不是null,那么当前用户是租户用户。 更多关于session的信息请看后面的Session一节。 数据过滤器 当从数据库中检索实体时,我们必须添加一个TenantId过滤器来只获得当前的租户实体。当你为实体实现了IMustHaveTenant和IMayHaveTenant两个接口之一时,ABP会自动地完成数据过滤。 IMustHaveTenant接口 该接口通过定义TenantId属性来区分不同租户的实体。一个实现了IMustHaveTenant的实体例子如下: public class Product : Entity, IMustHaveTenant { public int TenantId { get; set; } public string Name { get; set; } //...其他属性 } 这样,ABP知道这是一个特定租户的实体,并且会自动地将一个租户的实体从其他实体中分离出来。 IMayHaveTenant接口 我们可能需要在租户和租户之间共享一个实体类型。因此,一个实体可能会被一个租户或租主拥有。IMayHaveTenant接口也定义了TenantId(类似于IMustHaveTenant),但在这种情况下是nullable。实现了IMayHaveTenant的一个实体例子: public class Role : Entity, IMayHaveTenant { public int? TenantId { get; set; } public string RoleName { get; set; } //...其他属性 } 我们可能会使用相同的Role类来存储租主角色和租户角色。这种情况下,TenantId表明这是一个租户实体还是一个租主实体。null值表示这是一个租主实体,非null值表示这被一个租户拥有,该租户的Id是TenantId。 IMayHaveTenant不像IMustHaveTenant一样常用。比如,一个Product类可以不实现IMayHaveTenant接口,因为Product和实际的应用功能相关,和管理租户不相干。因此,要小心使用IMayHaveTenant接口,因为它更难维护租户和租主共享的代码。 保存实体 一个租户用户不应该创建或编辑其他租户的实体。如果相关的数据过滤器开启了,那么ABP会检查该实体相对于数据库的改变。 想要获得更多关于数据过滤器的信息,请看后面关于数据过滤器的博客。 本文转自tkbSimplest博客园博客,原文链接:http://www.cnblogs.com/farb/p/ABPMultiTenancy.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

【NGN学习笔记】5 IMS技术

1.移动网络的发展--引子 20世纪80年代,商业性移动通信网络得到发展 第一代移动通信系统:TACS、NMT等模拟系统 第二代移动通信系统:GSM、IS-95、DECT、IS-136等数字系统,目前建设的2G移动网络使用两种主要技术,GSM网络使用TDMA技术,大约占70%,IS-95网络基于CDMA技术,大约占25%。 1992年开始对3G移动通信系统进行研究:R99、R4、R5、R6、R7、R8、R9…… 2G (GSM), 基于电路交换 2.5G (GPRS), 引入分组交换 3G时代的到来 1992年ETSI发起对即将到来的3G移动通信系统进行研究。采用2GHz附近的频率,支持多种空中接口: WCDMA——使用2GHz频谱的宽带码分多址接入 EDGE——用于GSM演进的增强数据传输速率 CDMA2000(1xRTT)——多载波SCDMA系统,用于在与IS-95相同的频带上建设CDMA网络 TD-SCDMA——由中国提出 支持分组数据业务,采用通用体系结构——UMTS(由3GPP负责制定),已有6个版本: 3GPP R99、R4、R5、R6、R7和R8 具体的: 3G (R99), 引入全新的UTRAN 3G (R4),电路域采用移动软交换 3G (R5), 引入IMS域 3G (R6),引入WLAN接入 3G (R7), 功能增强: CSI (Combination of CS and IMS services):研究如何将CS承载和IMS结合起来为用户提供统一的业务,CS传递实时业务,IMS分组域传递非实时业务 VCC (Voice Call Continuity):解决CS域和IMS之间语音业务切换的连续性问题 SMSIP: 研究如何通过IPCAN来提供短消息/多媒体消息业务 FBI: IMS如何支持固定接入,借鉴TISPAN的研究成果 LCS3: 如何在WLAN接入IMS的系统中提供定位业务 E2EQos: 研究端到端的Qos机制,研究IMS中计费和策略控制框架的合并 EC: 研究通过IMS如何提供紧急呼叫业务 2.IMS与现有通信系统之间的关系 标准化组织在IMS上的协作 3.IMS与软交换 IMS在3GPPRelease 5版本中提出,是对IP多媒体业务进行控制的网络核心层逻辑功能实体的总称。3GPP R5主要定义IMS的核心结构,网元功能、接口和流程等内容:R6版本增加了部分IMS业务特性、IMS与其他网络的互通规范和无线局域网(WLAN)接入特性等;R7版本加强了对固定、移动融合的标准化制订,要求IMS支持数字用户线(xDSL)、电缆调制解调器等固定接入方式。 软交换技术从1998年就开始出现并且已经历了实验、商用等多个发展阶段,目前已比较成熟。全球范围早已有多家电信运营商开展了软交换试验,发展至今,软交换技术已经具备了替代电路交换机的能力,并具备一定的宽带多媒体业务能力。在软交换技术已发展如此成熟的今天,IMS的出路在何方?又该如何发展和定位呢?首先需要对IMS和软交换进行较为全面的比较和分析。 如果从采用的基础技术上看,IMS和软交换有很大的相似性:都是基于IP分组网;都实现了控制与承载的分离;大部分的协议都是相似或者完全相同的;许多网关设备和终端设备甚至是可以通用的。 IMS和软交换最大的区别在于以下几个方面。 (1)在软交换控制与承载分离的基础上,IMS更进一步的实现了呼叫控制层和业务控制层的分离; (2)IMS起源于移动通信网络的应用,因此充分考虑了对移动性的支持,并增加了外置数据库——归属用户服务器(HSS),用于用户鉴权和保护用户业务触发规则; (3)IMS全部采用会话初始协议(SIP)作为呼叫控制和业务控制的信令,而在软交换中,SIP只是可用于呼叫控制的多种协议的一种,更多的使用媒体网关协议(MGCP)和H.248协议。 总体来讲,IMS和软交换的区别主要是在网络构架上。软交换网络体系基于主从控制的特点,使得其与具体的接入手段关系密切,而IMS体系由于终端与核心侧采用基于IP承载的SIP协议,IP技术与承载媒体无关的特性使得IMS体系可以支持各类接入方式,从而使得IMS的应用范围从最初始的移动网逐步扩大到固定领域。此外,由于IMS体系架构可以支持移动性管理并且具有一定的服务质量(QoS)保障机制,因此IMS技术相比于软交换的优势还体现在宽带用户的漫游管理和QoS保障方面。 3.IMS的特点和体系结构 1)特点: 采用分层的架构,便于网络演进和业务部署:接入层、承载层、会话控制层、业务应用层 接入无关性,提供优越的融合特性:核心功能与接入技术无关 基于SIP的会话控制,具备良好的多媒体业务支持能力 采用分布式开放性的体系结构,提供良好的可扩展性:开放的业务控制接口,拥有SIP的灵活和可扩展性 统一的用户数据管理(HSS) 归属网络服务控制,提供一致的用户通信体验 基于iFC(初始过滤规则)的业务触发机制,实现呼叫控制和业务逻辑的分离 2)分层体系结构: 3)功能实体简要归类 a)会话管理和路由类 服务呼叫会话控制功能S-CSCF 询问呼叫会话控制功能I-CSCF 代理呼叫会话控制功能S-CSCF b)数据库 归属用户服务器HSS 用户定位功能SLF c)网间配合实体 出口网关控制功能BGCF 媒体网关控制功能MGCF IP多媒体媒体网关功能IM-MGW 信令网关SGW d)服务 应用服务器AS 媒体资源控制功能 媒体资源处理功能 e)支撑实体 拓扑隐藏功能 THIG 安全网关SEG 策略决策功能 f)计费 4.功能实体详解 1)应用服务器 AS位于用户的归属网络或第三方网络 负责提供应用逻辑和应用的运行环境 处理和影响从IMS发来的SIP会话 发起SIP请求 发送计费信息给CCF和OCS 根据提供业务的方式不同,AS分三类 SIP AS:提供基于SIP的多媒体业务,如Presence OSA AS:由第三方提供基于OSA API开发的应用 CSE:提供传统的智能网业务 2)CSCF——Call Session Control Function IMS系统中完成呼叫控制功能的核心组件,主要功能:信令路由,会话管理,资源分配,安全认证,业务触发,计费控制。 a)P-CSCF:代理呼叫会话控制功能 IMS系统中用户的第一个接触点,主要功能: 作为SIP Proxy,在UE和S/I-CSCF间转发SIP请求和响应信令消息 异常会话释放/注册时,充当SIP UA发起SIP事务 检测紧急会话 向CCF(计费采集功能)提供计费信息 SIP信令完整性和机密性保护 SIP消息的压缩和解压 向S-CSCF订阅注册事件包 执行媒体监管 维护会话计时器 b)I-CSCF:问询呼叫会话控制功能 运营商归属网络中为所有连接到该运营商网络的用户的连接提供的一个联系点,主要功能: 注册阶段指定S-CSCF 获取S-CSCF的名字 前转SIP消息和响应给S-CSCF 向CCF提供计费信息 拓扑隐藏功能 c)S-CSCF:服务呼叫会话控制功能 IMS的核心所在,位于归属网络,提供注册和呼叫控制服务,主要功能: 注册服务功能 基于AKA的认证 从HSS下载用户信息和业务数据 消息路由和转发 会话控制功能 与业务平台交互 E.164号码与SIP URI之间的转换 维护会话计时器、执行媒体监管、支持紧急呼叫 向CCF或OCS提供计费信息 d)HSS:归属用户服务器 IMS中所有与用户和业务相关数据的主要存储器,主要数据包括:用户身份、注册信息、接入参数、业务触发信息。 HSS功能包括三部分: IMS功能 CS域HLR/AUC功能 PS域HLR/AUC功能 e)MRF:媒体资源功能,多用于多方会话 MRFC:媒体资源功能控制器--对IMS域内部的媒体资源进行控制,通过H.248协议维护、控制MRFP中的媒体资源,与S-CSCF交互,间接接受AS的控制,向CCF或OCS提供计费信息。 MRFP:媒体资源功能处理器--对IMS域内部的媒体资源进行处理,接受MRFC的控制:H.248,完成对媒体流的编解码、转换、混合和播放等功能。 f)网关功能 BGCF:外出网关控制功能--负责在IMS与PSTN/CS域互通时候选择到CS域的出口的位置,即选择MGCF或BGCF MGCF:媒体网关控制功能--IMS用户与CS用户互通的控制网关,控制IMS-MGW中媒体信道的连接,选择CSCF,进行协议转换:SIP与ISUP,并与SGW交互 IMS-MGW:IMS媒体网关功能--在IMS与CS间提供用户平面链路,在MGCF的控制下,完成媒体协议转换、回声消除、转码等功能 SGW:信令网关--SIP与SS7之间的转换,支持SIGTRAN 5.参考点 ISC:S-CSCF与AS平台之间,提供IMS业务控制机制的重要接口,基于SIP协议,传送AS提供的业务相关的SIP消息。 Cx:CSCF与HSS之间,提供S-CSCF分配、路由查询、认证授权、业务过滤控制等功能,基于Diameter协议。 Gm:UE与P-CSCF之间,基于SIP协议,完成注册、呼叫控制、事务处理等功能。 Mw:CSCF与CSCF之间,基于SIP协议,完成注册、呼叫控制、事务处理等功能。 Mg:MGCF与CSCF之间,基于SIP协议,负责将边缘功能MGCF连接到IMS上。 Mr:CSCF与MRFC之间,基于SIP协议,支持S-CSCF与MRFC之间的交互,是IMS域内实现多方会议的通道。 Mp:MRFC与MRFP之间,MRF内部通道,兼容H.248/Megaco,支持MRFC对MRFP提供的资源的控制。 Mn:MGCF与IMS-MGW之间,采用H.248,用于控制用户平面资源。 Mi:CSCF与BGCF之间,基于SIP协议,是IMS域内部与CS域互通的通道。 Mj:BGCF与MGCF之间,基于SIP协议,是IMS内部与CS域互通的通道。 Sh:HSS与AS(SIP AS及OSA AS)之间,基于Diameter协议,完成数据处理、订购通知。 Si:HSS与CAMEL SE之间,采用MAP协议,传输CAMEL订购关系信息。 6.IMS的主要协议 主要是SIP,SDP,H.248/Megaco,Diameter。 1)IMS对SIP的扩展 SIP压缩:SigComp 安全 CSCF路由 网络发起的呼叫释放 RFC3455定义了专用于3GPP IMS的SIP头 P-Charging-Vector P-Charging-Function-Address P-Visited-Network-ID P-Access-Network-Info P-Associated-URI 2)Diameter协议 Diameter是IETF开发的用于认证、授权和计费(AAA)的协议,基于远程拨入用户认证服务(RADIUS)协议构建而成。 Diameter基本协议基于RFC3588,客户/服务器协议,传递Diameter数据单元、协商能力集、处理错误并提供可扩展性,基于TCP或SCTP进行传输。 Diameter应用:定义了特定应用的功能和数据单元,有Diameter移动IP应用和Diameter SIP应用,应用于IMS系统。 3)Diameter SIP应用 定义了一个被SIP服务器用来实现对不同SIP资源进行授权的应用。 应用的接口: Cx:I/S-CSCF与HSS之间 Dx:SLF与I-CSCF之间 Sh:HSS与SIP AS及OSA AS之间 Dh:SLF与AS之间 命令: 7.IMS标识 1)IMS用户标识:私有用户身份和公共用户身份 a)私有用户身份: 由归属网络运营商定义的具有唯一性的全球身份,可用于在归属网络中从网络的角度唯一地标识用户。私有用户身份并不是标识用户本身,而是标识了用户与运营商的订购/签约关系,主要用于注册过程中对用户进行认证,也可以用于计费和管理目的。存储在ISIM (身份模块) 应用中,采用NAI (网络接入标识符) 的形式:form_user@realm。 b)公共用户身份: IMS网络中的用户身份,用于请求与其他用户进行通信时使用的身份,可以被公布到号码簿、主页等上,ISIM中至少要存储一个公共用户身份。通过注册过程在网络中注册,网络不对公共用户身份进行认证,公共用户身份的两种形式:SIP URI sip:joe.doer@rims.example.com 、 tel URL tel:+358 50 1234567。 公共用户身份与私有用户身份之间的关系: c)公共业务身份: 公共业务身份,对AS提供的业务进行标识,由用户根据需要在AS中创建,使用之前不需要进行注册。采用SIP URI形式,如标识消息列表业务的公共业务身份:sip:messagelist_joe@ims.example.com d)网络实体标识: 处理SIP路由的网络节点通过SIP URI标识,例子: sip:finland.scscf@ims.example.com。 2)IMS身份模块——ISIM ISIM是位于UICC上的应用,用于存储由运营商提供的IMS专用数据。 8.IMS的业务控制机制 1)IMS 用户配置 用户与运营商确定订购关系时,由运营商给用户分配IMS用户配置,永久存储在HSS中。 2)初始过滤规则 初始过滤规则用来表示业务触发信息,描述了S-CSCF何时将到来的SIP消息进一步路由到一个特定的应用服务器。 初始过滤规则的创建: 初始过滤规则在用户获得IMS订购关系时创建 创建初始过滤规则需要考虑的问题:触发点是什么?当触发点被匹配时,正确的AS是什么?各初始过滤规则的优先级是什么?如果应用服务器没有应答,应该怎么做? 初始过滤规则的结构: 3)IMS业务提供 IMS本身不是业务,而是基于SIP的控制体系,IMS业务触发由S-CSCF完成。IMS为业务的提供提供了必要的方法,包含3个步骤: 定义可能的业务或业务集 在用户注册时,以初始过滤规则的形式创建用户特定的业务配置数据 根据业务配置数据中的初始过滤规则将到达S-CSCF的SIP请求转发给AS 4)AS的选择与AS的行为 AS的选择(业务控制):S-CSCF下载初始过滤规则,注册阶段完成;S-CSCF进行业务控制,选择AS;存在多个过滤规则时,需要进行多次业务控制。 AS的可能行为:UAC、UAS、Proxy Server、Redirect Server、B2BUA。 9.IMS的典型流程 1)P-CSCF的发现 a)使用DHCP和DNS发现P-CSCF:IP-CAN作为DHCP中继代理,通过DHCP机制给出P-CSCF的域名或IP地址 b)使用PDP上下文激活信令发现P-CSCF 2)用户注册/注销 注册:特点是归属域注册,注册前后及注册期间的信息存储: a)用户初始注册: b)用户刷新注册/注销: c)隐性注册 通过一次注册过程注册多个公共用户身份的机制。需先在HSS中定义隐性注册集,通过订阅/通知机制,UE可以获得隐性注册的公共用户身份。 d)多个UE共享单个公共用户身份进行通信 前提:IMS支持多个UE注册相同的公共用户身份 多个UE共享单个公共用户身份的方法: 基于用户定义的优先级 基于SIP的Forking机制(顺序、并行) 3)会话建立 漫游移动发起端MO: 归属移动发起端MO: 漫游移动终止端MT: 归属移动终止端MT: 4)会话建立(S-CSCF到S-CSCF) a)会话的发起和终止由不同的网络运营商来服务 b)会话的发起和终止由相同网络运营商来服务 c)IMS会话建立 d)PSTN->IMS e)IMS-> PSTN 附:IMS典型网络互通架构 网络类型1:演进ISP 提供电话、VoD和标准ISP业务,通过热点也完成对无线的接入,所有的业务最终都由IMS进行控制。 网络类型2:传统电信运行商 其中一部分是软交换与PSTN互通,其中的应用服务器可以被其他网络结构使用。 网络类型3:3GPP移动网络 主要是接入方式的不同,GERAN或UTRAN。 网络类型4:WIMAX 这里没有IMS域,它是由一些SIP服务器等完成相应功能的。 以下是摘自http://blog.ixpub.net/html/96/12690296-146875.html的一篇关于IMS和软交换的对比,写的不错: 一、业务提供方式对比 1.软交换业务架构 由于软交换并未完全实现业务与控制的分离,因此传统的基本业务和一些补充业务仍然是通过软交换来实现(方式一)。但对于更多的增值业务,软交换还提供了多种方式进行业务提供,如图1所示。 图1软义换多方式业务提供示意图 方式二:智能网业务方式。业务逻辑在SCP中驻留和运行,软交换设备实现业务交换功能(SSP)触发智能业务到智能网SCP,与SCP配合实现智能网提供的业务。 方式三:SIP应用服务器方式。由运营商提供SIP应用服务器,其中的业务逻辑可直接访问和控制软交换设备提供的网络能力,通常实现运营商的自营业务。 方式四:第三方应用服务器方式。由运营商或者第三方服务提供商提供Parlay应用服务器,而运营商提供业务能力网关,Parlay应用服务器中的业务逻辑通过业务能力网关间接访问和控制软交换设备提供的网络能力,多用于运营商与第三方合作的业务,也可用于运营商自营业务。 2.IMS业务架构 在IMS系统中,实现了业务与控制的完全分离,所有的业务都是通过应用服务器来提供的,业务逻辑驻留于应用服务器中,用户数据统一存储在HSS,如图2所示。图中的应用服务器包括三类:SIP应用服务器,OSA应用服务器及IM-SSF。SIP应用服务器(SIP AS)用于实现基于SIP的增值应用;SIP/OSA应用服务器(SIP/OSA AS)通过OSA业务能力服务器(OSA SCS)与IMS核心网进行交互;而IM-SSF用于支持IMS用户使用现有智能网的能力。目前IM-SSF支持使用CAMEL的业务能力,为考虑固定网络的智能网业务,IM-SSF的功能可以扩展到对INAP的支持。 图2IMS业务架构 在这一架构中,IMS的核心网元S-CSCF只通过ISC接口与应用平台交互,从而使得业务接口和触发方式得以标准化,进而获得更好的开放性。对三种应用服务器的具体应用如下: (1)基于SIP的应用服务器(SIP AS) SIP应用服务器和S-CSCF之间直接利用SIP及其扩展的呼叫信令协议,因此不需要进行呼叫信令协议之间的转换工作。另外由于基于SIP可以非常方便的实现语音、数据以及视频等多媒体类的会话,因此基于SIP的应用服务器可以高效率的提供各种新型的融合业务。 (2)基于OSA的应用服务器(OSA SCS) 在对第三方应用方面,IMS采用Parlay/OSA API。在Parlay/OSA API中,提供给应用的网络功能实体被定义为若干业务能力特征(SCF)的集合,由业务能力服务器(SCS)支持,这些SCF为应用开发提供了必要的网络接入功能。第三方应用服务器通过Parlay/OSA API和SCS相连接,而OSA SCS则在第三方应用服务器和S-CSCF之间负责API和ISC之间的映射。 (3)基于智能网的业务服务器(IM-SSF) 现有的移动智能网以及固定智能网都已经提供了丰富的智能网业务,并且有着庞大的用户群,为了兼容已有的网络资源,实现技术和服务提供的平滑过渡,IMS需要接入现有的智能网,并且还可以实现将现有智能网的业务延伸到IMS用户。目前通过IM-SSF接入现有移动智能网CAMEL已经完备。从CAMEL角度来看,IM SSF是智能网的服务交换节点SSP,相当于CAMEL中业务交换功能SSF和呼叫控制功能CCF。IM SSF在S-CSCF和CAMEL之间实现SIP消息和CAP信令的映射。为支持固定智能网,IM-SSF需要支持INAP协议,相关的研究还正在进行中。 从上面的分析可以看出,软交换和IMS都可以实现智能网提供业务和应用服务器提供业务,并且都可以开放接口给第三方使用,供第三方开发业务。但是由于软交换与不同的业务平台之间采用的接口不同,而IMS则统一采用ISC接口,另外对于IMS,由于3GPP,OMA等组织形成了大量的基于应用服务器的业务规范,并可以作为业务引擎,供其他的业务调用。因此IMS在业务提供的方便性、灵活性以及扩展性等方面都显示出了优势。 二、业务触发机制对比 1.软交换触发机制 软交换触发机制有两类: (1)基于智能网的触发机制 软交换可作为SSP,根据用户的主叫属性以及被叫属性(DP点触发)完成业务触发,在SCP或者应用服务器的智能业务逻辑控制下实现智能业务。 (2)基于应用服务器的触发机制 根据用户的profile或呼叫相关信息触发到应用服务器。应用服务器提供标准接口与前端服务器连接,接受来自前端服务器的业务触发信息和交互业务处理过程的信息。业务触发信息可能包括鉴权信息(如主叫号码、账号、密码)、发起方主机IP地址、被叫号码、业务标识、业务配置信息。 2.IMS触发机制 IMS的业务触发机制是基于iFC(Initial Filter Criteria)实现的。业务的触发在S-CSCF中完成,业务数据在注册阶段从HSS中下载到S-CSCF中,包括Initial Filter Criteria。在收到用户会话请求后,S-CSCF首先检查Initial Filter Criteria中的触发标准是否满足,然后再进行到用户的路由和呼叫控制。如果满足则通过ISC接口的SIP消息,将业务触发到对应的业务平台。业务平台然后根据业务的既定逻辑流程,通过SIP消息对S-CSCF中的业务进行后续控制。 从上述分析可以看出,软交换的业务触发机制表现为多样性,既有基于传统智能网模式的又有基于用户业务数据的。而对于基于应用服务器的业务,软交换与IMS的业务触发机制有很多的相似之处,但由于软交换在标准化方面的局限性,造成了业务触发方面无法规范化,也就决定了其在实际应用中只能由设备供应商自行提供解决方案。相比较,IMS就提供了比较完善的标准化,从而使得业务实现和互通更加规范统一。 三、业务实现流程对比 尽管基于软交换和基于IMS都可以提供宽带多媒体类的业务,但由于软交换在提供宽带多媒体业务方面缺乏相关的规范,因此现有的各种业务多是厂家基于自己的平台进行开发的,并对协议进行了私有扩展,业务之间的互通性较差。而在IMS网络架构下,由于IMS的业务规范好,各种部件的标准化程度高,因此业务之间的互通性好。下面以目前最常用的即时消息(IM)和状态呈现(Presence)两种业务的流程说明其区别: 1.基于软交换的业务流程示例 以国内某主流软交换提供厂商为例,在其基于软交换实现宽带多媒体业务时,用户与业务应用服务器采用HTTP和SIP两种消息的进行交互。对于数据业务采用HTTP协议,用户可使用Web客户端,最大程度实现业务的可达性,另外可通过HTTPS,实现安全的传输机制。对于媒体业务采用SIP,完成与呼叫相关的业务处理。为完成用户与应用服务器之间消息的实时传送,该厂商对HTTP协议进行了修改,实现用户Web浏览器与应用服务器之间建立一条持续的HTTP连接(Persistent HTTP connection)。采用这种方式实现即时消息和状态通知的流程如图3、4所示: 图3基于软交换的业务流程示意:即时消息 图4基于软交换的业务流程示意:状态通知 2.基于IMS的业务流程示例 对于基于IMS提供的业务,由于目前业务的标准化程度高,特别是IMSinOMA Enabler规范的实施,更好的协调了OMA与3GPP/3GPP2的IMS之间的合作关系,目前已经发布了Presence,PoC,XDMS等一系列符合IMSinOMA框架的规范,成为业界遵循的标准。例如对于即时消息,在SIP消息中定义了Message消息可以直接用于即时消息的传递,具体流程如图5所示: 图5基于IMS的业务流程示意:即时消息(Page-Mode) 对于Presence业务,则规定了状态订阅使用SIP的SUBSCRIBE消息,状态更新使用SIP的PUBLISH消息,状态更新通知使用SIP的NOTIFY消息。如果用户的状态有更新,则通过PUBLISH消息更新自己的状态信息到Presence服务器;用户希望知道好友的状态,需要首先到Presence服务器订阅好友的状态,Presence服务器对用户进行权限检查,如果好友允许该用户知道自己的状态信息,好友的状态变化后,Presence服务器再把状态变化信息通知给用户。业务流程如下面的图6、7所示: 图6基于IMS的业务流程示意:发布状态(Publish) 图7基于IMS的业务流程示意:订阅/通知状态变化(Subscriber/Notify) 因此从业务流程上可以看出,尽管基于软交换与基于IMS都使用了SIP协议,但在使用的信令消息上有一定的区别。另外在具体的信令消息参数上也有所不同 本文转自gnuhpc博客园博客,原文链接:http://www.cnblogs.com/gnuhpc/archive/2012/12/11/2813494.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android Service学习之本地服务

Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中 通过<service>来声明。可以通过contect.startservice和contect.bindserverice来启动。 Service和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现。 service的两种模式(startService()/bindService()不是完全分离的): 本地服务Local Service 用于应用程序内部。 它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。 用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。 远程服务Remote Service 用于android系统内部的应用程序之间。 它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。 可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。 生命周期 Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当我们第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。 而启动service,根据onStartCommand的返回值不同,有两个附加的模式: 1. START_STICKY 用于显示启动和停止service。 2. START_NOT_STICKY或START_REDELIVER_INTENT用于有命令需要处理时才运行的模式。 服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。 1. 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。 如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。 如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。 采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。 2. 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。 onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。 采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。 看看官方给出的比较流程示意图: 官方文档告诉我们,一个service可以同时start并且bind,在这样的情况,系统会一直保持service的运行状态如果service已经start了或者BIND_AUTO_CREATE标志被设置。如果没有一个条件满足,那么系统将会调用onDestory方法来终止service.所有的清理工作(终止线程,反注册接收器)都在onDestory中完成。 拥有service的进程具有较高的优先级 官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。 1. 如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。 2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed. 3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。 4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。 如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。 本地service 1.不需和Activity交互的本地服务 public classLocalService extendsService{ private static finalString TAG = "LocalService"; @Override publicIBinder onBind(Intent intent) { Log.i(TAG, "onBind"); return null; } @Override public voidonCreate() { Log.i(TAG, "onCreate"); super.onCreate(); } @Override public voidonDestroy() { Log.i(TAG, "onDestroy"); super.onDestroy(); } @Override public voidonStart(Intent intent, intstartId) { Log.i(TAG, "onStart"); super.onStart(intent, startId); } } Activity: public classServiceActivity extendsActivity { @Override protected voidonCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.servicedemo); ((Button) findViewById(R.id.startLocalService)).setOnClickListener( newView.OnClickListener(){ @Override public voidonClick(View view) { // TODO Auto-generated method stub startService(newIntent("com.demo.SERVICE_DEMO")); } }); ((Button) findViewById(R.id.stopLocalService)).setOnClickListener( newView.OnClickListener(){ @Override public voidonClick(View view) { // TODO Auto-generated method stub stopService(newIntent("com.demo.SERVICE_DEMO"));} }); } } 在AndroidManifest.xml添加: < service android:name =".LocalService" > < intent-filter > < action android:name ="com.demo.SERVICE_DEMO" /> < category android:name ="android.intent.category.default" /> </ intent-filter > </ service > 否则启动服务时会提示new Intent找不到"com.demo.SERVICE_DEMO"。 对于这类不需和Activity交互的本地服务,是使用startService/stopService的最好例子。 运行时可以发现第一次startService时,会调用onCreate和onStart,在没有stopService前,无论点击多少次startService,都只会调用onStart。而stopService时调用onDestroy。再次点击stopService,会发现不会进入service的生命周期的,即不会再调用onCreate,onStart和onDestroy。 而onBind在startService/stopService中没有调用。 2.本地服务和Activity交互 对于这种case,官方的sample(APIDemo\app.LocalService)是最好的例子: /** * This is an example of implementing an application service that runs locally * in the same process as the application.The {@link LocalServiceController} * and {@link LocalServiceBinding} classes show how to interact with the * service. * * <p>Notice the use of the {@link NotificationManager} when interesting things * happen in the service.This is generally how background services should * interact with the user, rather than doing something more disruptive such as * calling startActivity(). */ public classLocalService extendsService { privateNotificationManager mNM; /** * Class for clients to access.Because we know this service always * runs in the same process as its clients, we don't need to deal with * IPC. */ publicclassLocalBinderextendsBinder { LocalService getService() { returnLocalService.this; } } @Override public voidonCreate() { mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); // Display a notification about us starting.We put an icon in the status bar. showNotification(); } @Override public intonStartCommand(Intent intent, intflags, intstartId) { Log.i( "LocalService", "Received start id "+ startId + ": "+ intent); // We want this service to continue running until it is explicitly // stopped, so return sticky. returnSTART_STICKY; } @Override public voidonDestroy() { // Cancel the persistent notification. mNM.cancel(R.string.local_service_started); // Tell the user we stopped. Toast.makeText( this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show(); } @Override public IBinder onBind(Intent intent) { return mBinder; } // This is the object that receives interactions from clients.See // RemoteService for a more complete example. privatefinalIBinder mBinder =newLocalBinder(); /** * Show a notification while this service is running. */ private voidshowNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.local_service_started); // Set the icon, scrolling text and timestamp Notification notification = newNotification(R.drawable.stat_sample, text, System.currentTimeMillis()); // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity( this, 0, newIntent( this, LocalServiceController. class), 0); // Set the info for the views that show in the notification panel. notification.setLatestEventInfo( this, getText(R.string.local_service_label), text, contentIntent); // Send the notification. // We use a layout id because it is a unique number.We use it later to cancel. mNM.notify(R.string.local_service_started, notification); } } 这里可以发现onBind需要返回一个IBinder对象。也就是说和上一例子LocalService不同的是, 1. 添加了一个public内部类继承Binder,并添加getService方法来返回当前的Service对象; 2. 新建一个IBinder对象——new那个Binder内部类; 3. onBind方法返还那个IBinder对象。 Activity: /** * <p>Example of binding and unbinding to the {@link LocalService}. * This demonstrates the implementation of a service which the client will * bind to, receiving an object through which it can communicate with the service.</p> */ public classLocalServiceBinding extendsActivity { private booleanmIsBound; privateLocalService mBoundService; @Override protected voidonCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.local_service_binding); // Watch for button clicks. Button button = (Button)findViewById(R.id.bind); button.setOnClickListener(mBindListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(mUnbindListener); } privateServiceConnection mConnection =newServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service){ // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service.Because we have bound to a explicit // service that we know is running in our own process, we can // cast its IBinder to a concrete class and directly access it. mBoundService = ((LocalService.LocalBinder)service).getService(); // Tell the user about this for our demo. Toast.makeText(LocalServiceBinding. this, R.string.local_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. // Because it is running in our same process, we should never // see this happen. mBoundService = null; Toast.makeText(LocalServiceBinding. this, R.string.local_service_disconnected, Toast.LENGTH_SHORT).show(); } }; privateOnClickListener mBindListener = newOnClickListener() { public voidonClick(View v) { // Establish a connection with the service.We use an explicit // class name because we want a specific service implementation that // we know will be running in our own process (and thus won't be // supporting component replacement by other applications). bindService( newIntent(LocalServiceBinding. this, LocalService. class), mConnection, Context.BIND_AUTO_CREATE); mIsBound = true; } }; privateOnClickListener mUnbindListener = newOnClickListener() { public voidonClick(View v) { if(mIsBound) { // Detach our existing connection. unbindService( mConnection); mIsBound = false; } } }; } 明显看出这里面添加了一个名为ServiceConnection类,并实现了onServiceConnected(从IBinder获取Service对象)和onServiceDisconnected(set Service to null)。 而bindService和unbindService方法都是操作这个ServiceConnection对象的。 AndroidManifest.xml里添加: < service android:name =".app.LocalService" /> < activity android:name =".app.LocalServiceBinding" android:label ="@string/activity_local_service_binding" > < intent-filter > < action android:name ="android.intent.action.MAIN" /> < category android:name ="android.intent.category.SAMPLE_CODE" /> </ intent-filter > </ activity > 这里没什么特别的,因为service没有需要什么特别的action,所以只是声明service而已,而activity和普通的没差别。 运行时,发现调用次序是这样的: bindService: 1.LocalService : onCreate 2.LocalService : onBind 3.Activity: onServiceConnected unbindService: 只是调用onDestroy 可见,onStart是不会被调用的,而onServiceDisconnected没有调用的原因在上面代码的注释有说明。 介绍onStartCommand()需要用到的几个常量 (引自官方文档) START_NOT_STICKY If the system kills the service after onStartCommand() returns, do notrecreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs. START_STICKY If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but do notredeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job. START_REDELIVER_INTENT If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as do wnloading a file. Running a Service in the Foreground 具体内容查看官方文档,主要是使用 startForeground() 和 stopForeground()方法。 本文转自 Icansoft 51CTO博客,原文链接http://blog.51cto.com/android/527314

优秀的个人博客,低调大师

Android开发学习笔记:浅谈ListView

LisView列表视图是Android开发中非常常用的一种视图组件,它是以垂直列表的方式列出需要显示的列表项。 创建ListView可以用ListView组件,也可以继承ListActivity。在使用过程中最重要的是如何设置ListView显示的内容,也就是怎样设置Adapter。Adapter类型可以分为三种:ArrayAdapter,SimpleCursorAdapter和SimpleAdapter。下面介绍这三种Adapter是如何添加列表视图内容的: 一.ArrayAdapter ArrayAdapter是这三种Adapter最简单的,只能展示一行文字,使用它来添加ListView显示的内容的步骤如下: 1.创建ArrayAdapter 2.调用ListActivity的setListAdapter( )方法来设置显示的内容 下面是具体的例子: MainActivity.java packagecom.android.listview1.avtivity; importandroid.app.ListActivity; importandroid.os.Bundle; importandroid.widget.ArrayAdapter; publicclassMainActivityextendsListActivity{ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); //列表项的数据 String[]strs={"Android","IOS","Mango","MeeGo","Symbian"}; //this,布局文件 //android.R.layout.simple_list_item_1,系统定义的布局文件 //strs,数据来源 ArrayAdapter<String>adapter=newArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strs); //为ListView设置适配器 setListAdapter(adapter); } } 效果图: 二.SimpleCursorAdapter SimpleCursorAdapter从游标得到的数据进行列表显示,并可以把指定的列映射到对应的TextView中。下面是使用它来显示Android中的联系人姓名的步骤: 1.调用getContentResolver( )方法返回ContentResolver( )对象,通过该对象查询得到Cursor对象 2.创建SimpleCursorAdapter对象 3.调用ListActivity的setListAdapter( )方法显示列表项 下面是具体的例子: MainActivity.java packagecom.android.listview2.activity; importandroid.app.ListActivity; importandroid.database.Cursor; importandroid.os.Bundle; importandroid.provider.Contacts.People; importandroid.widget.ListAdapter; importandroid.widget.SimpleCursorAdapter; publicclassMainActivityextendsListActivity{ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); //获得一个指向系统通讯录数据库的Cursor对象获得数据来源 Cursorcur=getContentResolver().query(People.CONTENT_URI,null,null,null,null); startManagingCursor(cur); //实例化列表适配器 ListAdapteradapter=newSimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cur, newString[]{People.NAME},//含数据库的列的String型数组 newint[]{android.R.id.text1});//包含布局文件中对应组件id的int型数组 //为ListView设置适配器 setListAdapter(adapter); } } 在AndroidManifest.xml文件中添加17行的声明 <?xmlversion="1.0"encoding="utf-8"?> <manifestxmlns:android="http://schemas.android.com/apk/res/android" package="com.android.listview2.activity" android:versionCode="1" android:versionName="1.0"> <uses-sdkandroid:minSdkVersion="10"/> <applicationandroid:icon="@drawable/icon"android:label="@string/app_name"> <activityandroid:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> <uses-permissionandroid:name="android.permission.READ_CONTACTS"></uses-permission> </manifest> 效果图: 三.SimpleAdapter SimpleAdapter可以定义各种不同的布局,可以添加ImageView(图片),Button(按钮),CheckBox(复选框)等。 下面是添加图片的例子: MainActivity.java packagecom.android.listview3.activity; importjava.util.ArrayList; importjava.util.HashMap; importjava.util.List; importjava.util.Map; importandroid.app.ListActivity; importandroid.os.Bundle; importandroid.widget.SimpleAdapter; publicclassMainActivityextendsListActivity{ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); SimpleAdapteradapter=newSimpleAdapter(this,getData(), R.layout.main,//设置界面布局 newString[]{"title","img"},//列表上的文字信息,图片信息 newint[]{R.id.title,R.id.img});//引用文字资源,图片资源 //布局文件的各组件分别映射到HashMap的各元素上,完成适配 //为ListView设置适配器 setListAdapter(adapter); } privateList<Map<String,Object>>getData(){ //map.put(参数名字,参数值) List<Map<String,Object>>list=newArrayList<Map<String,Object>>(); Map<String,Object>map=newHashMap<String,Object>(); map.put("title","摩托罗拉"); map.put("img",R.drawable.pic1); list.add(map); map=newHashMap<String,Object>(); map.put("title","诺基亚"); map.put("img",R.drawable.pic2); list.add(map); map=newHashMap<String,Object>(); map.put("title","三星"); map.put("img",R.drawable.pic3); list.add(map); returnlist; } } main.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5px" /> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#ffffff" android:textSize="20px" /> </LinearLayout> 效果图: 注:上面的程序很多地方用到泛型编程,有关Java泛型编程的资料请看: http://liangruijun.blog.51cto.com/3061169/636850 http://www.cnblogs.com/panjun-Donet/archive/2008/09/27/1300609.html 本文转自 lingdududu 51CTO博客,原文链接: http://blog.51cto.com/liangruijun/646049

优秀的个人博客,低调大师

Swift UI学习UITableView and protocol use

Models: UserModel.swift Views: UserInfoCell.swift Controllers: RootViewController.swift, DetailViewController.swift AppDelegate.swift: import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary? ) -> Bool { self.window = UIWindow(frame: UIScreen.mainScreen().bounds) // let rootController = RootViewController(style: UITableViewStyle.Plain) let rootNav = UINavigationController(rootViewController: rootController) self.window!.rootViewController = rootNav // self.window!.backgroundColor = UIColor.whiteColor() self.window!.makeKeyAndVisible() return true } } UserModel.swift import Foundation // // @brief The model of user, using to store user datas // @author huangyibiao // class UserModel : NSObject { var userName: String ///< store user's name, optional var userID: Int ///< store user's ID var phone: String? ///< store user's telephone number var email: String? ///< store user's email // designated initializer init(userName: String, userID: Int, phone: String?, email: String?) { self.userName = userName self.userID = userID self.phone = phone self.email = email super.init() } } UserInfoCell.swift: import Foundation import UIKit // // @brief The cell of showing user infos // @author huangyibiao // class UserInfoCell : UITableViewCell { var userNameLabel : UILabel! var phoneLabel : UILabel! var emailLabel : UILabel! init(style: UITableViewCellStyle, reuseIdentifier: String!) { super.init(style: style, reuseIdentifier: reuseIdentifier) userNameLabel = UILabel(frame: CGRectMake(30, 0, 100, 44)) userNameLabel.backgroundColor = UIColor.clearColor() userNameLabel.font = UIFont.systemFontOfSize(14) self.contentView.addSubview(userNameLabel) phoneLabel = UILabel(frame: CGRectMake(120, 0, 200, 20)) phoneLabel.backgroundColor = UIColor.clearColor() phoneLabel.font = UIFont.systemFontOfSize(12) self.contentView.addSubview(phoneLabel) emailLabel = UILabel(frame: CGRectMake(120, 20, 200, 20)) emailLabel.backgroundColor = UIColor.clearColor() emailLabel.font = UIFont.systemFontOfSize(12) self.contentView.addSubview(emailLabel) } func configureCell(userModel: UserModel?) { if let model = userModel { userNameLabel.text = model.userName phoneLabel.text = model.phone emailLabel.text = model.email } } } RootViewController.swift: import Foundation import UIKit // // @brief 作为窗体的rootViewControllor // @author huangyibiao // class RootViewController : UITableViewController, DetailViewControllerDelegate { var dataSource = NSMutableArray() var currentIndexPath: NSIndexPath? override func viewDidLoad() { super.viewDidLoad() for index in 0...12 { let model = UserModel(userName: "name:\(index + 1)", userID: index, phone: "13877747982", email: "632840804@qq.com") dataSource.addObject(model) } self.title = "UITableViewDemo" } override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int { return dataSource.count } override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! { // can't use static? let cellIdentifier: String = "UserInfoCellIdentifier" // may be no value, so use optional var cell: UserInfoCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UserInfoCell if cell == nil { // no value cell = UserInfoCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier) } let model: UserModel? = dataSource[indexPath.row] as? UserModel cell!.configureCell(model) return cell } override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) { let detail = DetailViewController() detail.userModel = dataSource[indexPath.row] as? UserModel detail.delegate = self currentIndexPath = indexPath self.navigationController.pushViewController(detail, animated: true) } func changeItem(forUserModel userModel: UserModel?) { var index = 0 for index = 0; index < dataSource.count; index++ { let model = dataSource[index] as UserModel if model.userID == userModel?.userID { model.phone = userModel? .phone model.email = userModel?.email tableView.reloadRowsAtIndexPaths([currentIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade) break } } } } DetailViewController.swift: import Foundation import UIKit // this delegate needs a @objc, because @optional is only for objective-c, not for swift @objc protocol DetailViewControllerDelegate : NSObjectProtocol { @optional func changeItem(forUserModel userModel: UserModel?) } class DetailViewController : UIViewController { var userModel: UserModel? var delegate: DetailViewControllerDelegate? override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor.whiteColor() self.title = userModel? .userName let button = UIButton(frame: CGRectMake(10, 200, 300, 40)) button.setTitle("change", forState:UIControlState.Normal) button.backgroundColor = UIColor.redColor() button.addTarget(self, action: "onChangeButtonClick:", forControlEvents: UIControlEvents.TouchUpInside) self.view.addSubview(button) } func onChangeButtonClick(sender: UIButton!) { if userModel { userModel!.userName = "ChangeName" // changeItem needs to add a ? to the end, before (), because // this function is optional // delegate? 表示可能没有代理。而changeItem? 表示方法可能没有实现,这样写就算没有实现也没有问题 delegate?.changeItem? (forUserModel: userModel) self.navigationController.popViewControllerAnimated(true) } } } 效果图: 版权声明:本文博客原创文章,博客,未经同意,不得转载。 本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/4727952.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

【Android进阶学习】Popupwindow的应用

PopupWindow 是一种阻塞式的弹出窗口,这就意味着在我们退出这个弹出框之前,程序会一直等待。它可以浮动在当前Activity的任何的位置上。 需要注意的是,PopupWindow必须触发某个焦点或者某个事件才会显示出来,不然总会会出现错误。 下面是使用PopupWindow 弹出自定义菜单的例子 当我们点击Menu键的时候,会在当前的Activity最下方弹出一个菜单。 效果图如下: PopupMenuDemo.java packagecom.lingdududu.popupWindow; importandroid.app.Activity; importandroid.content.Context; importandroid.graphics.drawable.BitmapDrawable; importandroid.os.Bundle; importandroid.view.Gravity; importandroid.view.KeyEvent; importandroid.view.LayoutInflater; importandroid.view.Menu; importandroid.view.MenuItem; importandroid.view.View; importandroid.view.ViewGroup; importandroid.widget.BaseAdapter; importandroid.widget.GridView; importandroid.widget.ImageView; importandroid.widget.LinearLayout; importandroid.widget.PopupWindow; importandroid.widget.TextView; importandroid.widget.LinearLayout.LayoutParams; publicclassPopupMenuDemoextendsActivity{ privateint[]resArray=newint[]{ R.drawable.icon_menu_addto,R.drawable.icon_menu_audioinfo, R.drawable.icon_menu_findlrc,R.drawable.icon_menu_scan }; privateString[]title=newString[]{ "添加","信息","搜索","查看" }; privatePopupWindowpw=null; /**Calledwhentheactivityisfirstcreated.*/ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override publicbooleanonCreateOptionsMenu(Menumenu){ menu.add(""); returnsuper.onCreateOptionsMenu(menu); } /** *在此方法中实现自定义菜单栏 */ publicbooleanonMenuOpened(intfeatureId,Menumenu){ if(pw!=null){ if(pw.isShowing()){ //就关闭弹出窗口 pw.dismiss(); } else{ //在指定位置弹出窗口 pw.showAtLocation(findViewById(R.id.tv),Gravity.CENTER,0,300); } } else{ LayoutInflaterinflater=(LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); Viewview=inflater.inflate(R.layout.menu,null); GridViewgrid1=(GridView)view.findViewById(R.id.menuGridChange); grid1.setAdapter(newImageAdapter(this)); //生成PopupWindow对象 pw=newPopupWindow(view,LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT); //在指定位置弹出窗口 pw.showAtLocation(findViewById(R.id.tv),Gravity.CENTER,0,300); } //此处返回false,系统不会执行onCreateOptionsMenu中添加的菜单 returnfalse; } publicbooleanonKeyDown(intkeyCode,KeyEventevent){ if(keyCode==KeyEvent.KEYCODE_BACK){ pw.dismiss(); returnfalse; } returnsuper.onKeyDown(keyCode,event); } @Override publicbooleanonOptionsItemSelected(MenuItemitem){ returnsuper.onOptionsItemSelected(item); } publicclassImageAdapterextendsBaseAdapter{ privateContextcontext; publicImageAdapter(Contextcontext){ this.context=context; } @Override publicintgetCount(){ returnresArray.length; } @Override publicObjectgetItem(intarg0){ returnresArray[arg0]; } @Override publiclonggetItemId(intarg0){ returnarg0; } @Override publicViewgetView(intarg0,Viewarg1,ViewGrouparg2){ LinearLayoutlinear=newLinearLayout(context); LinearLayout.LayoutParamsparams=newLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); linear.setOrientation(LinearLayout.VERTICAL); ImageViewiv=newImageView(context); iv.setImageBitmap(((BitmapDrawable)context.getResources().getDrawable(resArray[arg0])).getBitmap()); LinearLayout.LayoutParamsparams2=newLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); params2.gravity=Gravity.CENTER; linear.addView(iv,params2); TextViewtv=newTextView(context); tv.setText(title[arg0]); LinearLayout.LayoutParamsparams3=newLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); params3.gravity=Gravity.CENTER; linear.addView(tv,params3); returnlinear; } } } main.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/tv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout> menu.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/tv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout> menu_bg_frame.xml 这个文件是菜单背景的样式文件,在drawable-hdpi文件下面 <?xmlversion="1.0"encoding="UTF-8"?> <shapeandroid:shape="rectangle"xmlns:android="http://schemas.android.com/apk/res/android"> <solidandroid:color="#b4000000"/> <strokeandroid:width="2.0dip"android:color="#b4ffffff"android:dashWidth="3.0dip"android:dashGap="0.0dip"/> <paddingandroid:left="7.0dip"android:top="7.0dip"android:right="7.0dip"android:bottom="7.0dip"/> <cornersandroid:radius="8.0dip"/> </shape> 本文转自 lingdududu 51CTO博客,原文链接: http://blog.51cto.com/liangruijun/739459

优秀的个人博客,低调大师

MongoDB权威指南学习笔记01

章节:第一章 简介 时间:2013-07-18 内容: 丰富的数据模型 面向文档的数据库,非关系型数据库,是为了获得更加方便的扩展性 面向文档的方式可以将文档或者数据内嵌进来,所以用一条记录就可以表示非常复杂的层次关系 MongoDB没有模式,文档的键不会事先定义也不会固定不变 容易扩展 面向文档的数据模型使其可以自动在多台服务器之间分割数据 还可以平衡集群的数据和负载,自动编排文档 丰富的功能 索引:支持通用辅助索引,也提供唯一的、复合的地理空间索引能力 存储Javascript:不必使用存储过程 聚合:支持MapReduce和其它聚合工具 固定集合:集合的大小是有上限的,对某些类型的数据(如日志)特别有用 文件存储:支持用一种容易使用的协议存储大型文件和文件的元数据 某些关系型数据库的功能并不具备,如联接(join)和复杂的多行事务 不牺牲速度 使用MongoDB传输协议作为与服务器交互的主要方式(与之对应的协议需要更多的开销,如HTTP/REST) 对文档进行动态填充,预分配数据文件,用空间换取性能的稳定 默认的存储引擎中使用了内存映射文件,将内存管理工作交给操作系统去处理 动态查询优化器会记住执行查询最高效的方式 简便的管理 让服务器自治来简化数据库管理 本文转自 xxrenzhe11 51CTO博客,原文链接:http://blog.51cto.com/xxrenzhe/1252217,如需转载请自行联系原作者

优秀的个人博客,低调大师

学习OpenStack之 (1):安装devstack

1. 系统准备 ubuntu 12.04 server 虚拟机。 2G内存。 依次运行以下命令来安装git: sudo apt-get update sudo apt-get upgrade sudo apt-get install git 2. 下载安装包 $gitclonehttps: //github .com /openstack-dev/devstack .git #克隆devstack的Git代码仓库 3. 配置 从2013/10起OpenStack引入新的配置方式即使用配置文件 local.conf。从官方网站看这个文件应该存在于devstack目录中,找了后发现在samples目录中。创建 local.conf包含下面内容: [[local|localrc]] ADMIN_PASSWORD=secrete #Admin密码 DATABASE_PASSWORD=$ADMIN_PASSWORD //预设密码,这样在运行stack.sh的时候就不会要求输入密码 RABBIT_PASSWORD=$ADMIN_PASSWORD //同上 SERVICE_PASSWORD=$ADMIN_PASSWORD //同上SERVICE_TOKEN=a682f596-76f3-11e3-b3b2-e716f9080d50其他可使用配置项:安装路径: 默认: DEST=/opt/stack 示例:DEST=/opt/mystack 屏幕输出目录: 默认只是console输出:SCREEN_LOGDIR=”“ 示例:SCREEN_LOGDIR=$DEST/logs/screen #输出到文件,每个screen输出用时间戳命名的一个文件日志:默认: ``LOGFILE=”” LOGDAYS=7 LOG_COLOR=True`` //日志相关。默认日志仅在console输出 示例local.conf文件: [[local|localrc]] LOGDAYS=1 LOGFILE=$DEST/logs/stack.sh.log SCREEN_LOGDIR=$DEST/logs/screen ADMIN_PASSWORD=1111 DATABASE_PASSWORD=$ADMIN_PASSWORD RABBIT_PASSWORD=$ADMIN_PASSWORD SERVICE_PASSWORD=$ADMIN_PASSWORD SERVICE_TOKEN=a682f596-76f3-11e3-b3b2-e716f9080d50 4. 以非root用户运行以下命令 cd devstack ./stack.sh 注意: 1. 不可以使用root用户运行该命令,可使用 /devstack/tools/create-stack-user.sh命令创建一个用户,比如stack 2. 保证该用户下sudo可以免密码运行,否则脚本执行过程中会出现权限问题 方法:修改/etc/sudoers, 在行root ALL=(ALL:ALL) ALL后添加stack ALL=(ALL) ALL然后保存文件 遇到的一些问题及解决办法: 1. 网速太慢,git clone失败 要么用网速快的机器;要么找一个已经装好devstack的机器,将/opt/stack所有文件拷贝到你的环境中,必要的话将git clone全部注释掉,再运行stack.sh 2. 一些依赖的包没有安装,比如下面的mysql-server-5.5:在ubuntu上装上这些包重新运行stack.sh即可 Reading package lists... Building dependency tree... Reading state information... Some packages could not be installed. This may mean that you have requested an impossible situation or if you are using the unstable distribution that some required packages have not yet been created or been moved out of Incoming. The following information may help to resolve the situation: The following packages have unmet dependencies: mysql-server : Depends: mysql-server-5.5 but it is not going to be installed E: Unable to correct problems, you have held broken packages. 成功: Horizon is now available at http://9.*.236.90/ Keystone is serving at http://9.*.236.90:5000/v2.0/ Examples on using novaclient command line is in exercise.sh The default users are: admin and demo The password: 1111 This is your host ip: 9.*.236.90 stack.sh中的执行顺序: 支持OS类型包括Ubuntu 12.04或以上;Fedora F18或以上 禁止使用root运行 读取local.conf 检查stackrc文件是否存在 检查Devstack是不是已经在运行。如果在运行,则退出 配置目标安装目录,包括创建目录,设置权限 配置hostname,logging等 读取各组件的安装和启动script 如果没有配置密码,则需要用户输入各密码 配置数据库 配置Keystone 安装各pre-condition包 安装client包 安装和配置keystone,swift,glance,cinder,neutron,nova,horizon,ceilometer,heat,CA 配置数据库 配置screen 创建个组件使用的账号 初始化和启动horizon 启动swift,glance, 安装images 启动swift,nova_api,neutron,nova,cinder,ceilometer,heat 5. 在浏览器中输入 http://9.*.236.90/ 打开dashboard, 用admin和1111登录 参考: http://docs.openstack.org/developer/devstack/configuration.html 6. 重启机器后重新run OpenStack:执行rejoin_stack.sh screen语法,执行完rejoin_stack.sh后,需要使用screen语法来控制openstack的进程 帮助 ctrl+a+? 查看screen导航 ctrl+a+" 注需要使用shift键 退出screen,有两种方法: 方法1:attach screen ctrl+a+d 方法2:exit screen ctrl+a+K 查看下一个screen ctrl+a+n 查看上一个screen ctrl+a+p 保存screen的日志到文件 ctrl+a+H,再按一次停止保存。 查看screen [root@cheeliang ~]# screen -ls There is a screen on: 2678.stack (Attached) 1 Socket in /var/run/screen/S-root. 重连接Re-attach screen screen -r 2678 本文转自SammyLiu博客园博客,原文链接:http://www.cnblogs.com/sammyliu/p/4136466.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

【Android进阶学习】LayoutInflater的应用

LayoutInflater在Android中是“扩展”的意思,作用类似findViewById( ),它在Android开发中的作用是很大的。LayoutInflater经常在BaseAdapter的getView方法中用到,用来获取整个View并返回。 LayoutInflater与findViewById( )的不同点: LayoutInflater是将XML中的Layout转换为View放入.java代码中 findViewById()是找具体xml下的具体组件(如:Button,TextView,ImageView等)。 获得LayoutInflater的三种方法: 第一种: LayoutInflaterinflater=LayoutInflater.from(this); Viewlayout=inflater.inflate(R.layout.main,null); 第二种: LayoutInflaterinflater=getLayoutInflater(); Viewlayout=inflater.inflate(R.layout.main,null); 第三种: LayoutInflaterinflater=(LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE); Viewlayout=inflater.inflate(R.layout.main,null); getSystemService()是Android很重要的一个API,它是Activity的一个方法,根据传入 的NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。其中LAYOUT_INFLATER_SERVICE返回的对象是LayoutInflater,作用是取得XML定义的View。 第一个实例: main.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:id="@+id/text" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/btn1" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/btn2" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout> MainActivity.java packagecom.lingdududu.test; importandroid.app.Activity; importandroid.graphics.Color; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.widget.Button; importandroid.widget.EditText; publicclassMainActivityextendsActivity{ privateEditTextetx; privateButtonconfirmBtn; privateButtoncancleBtn; /**Calledwhentheactivityisfirstcreated.*/ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); LayoutInflaterinflater=getLayoutInflater(); Viewlayout=inflater.inflate(R.layout.main,null); etx=(EditText)layout.findViewById(R.id.text); etx.setBackgroundColor(Color.WHITE); etx.setHint("请输入你的学号"); confirmBtn=(Button)layout.findViewById(R.id.btn1); confirmBtn.setText("确定"); cancleBtn=(Button)layout.findViewById(R.id.btn2); cancleBtn.setText("取消"); setContentView(layout); } } 效果图: 第二个实例: 下面的一个简单的LayoutInflater应用实例,主布局main.xml里有一个TextView和一个Button,当点击Button,出现 Dialog,而这个Dialog的布局方式是我们在layout目录下定义的dialog.xml文件(里面左右分布,左边 ImageView,右边TextView)。 main.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="打开对话框" /> </LinearLayout> dialog.xml <?xmlversion="1.0"encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/dialog_txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试LayoutInflater" /> </LinearLayout> MainActivity.java packagecom.lingdududu.test; importandroid.app.Activity; importandroid.app.AlertDialog; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.widget.Button; importandroid.widget.ImageView; importandroid.widget.TextView; publicclassMainActivityextendsActivity{ privateButtonshowBtn; /**Calledwhentheactivityisfirstcreated.*/ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); showBtn=(Button)findViewById(R.id.btn); showBtn.setOnClickListener(newshowDialogListener()); } classshowDialogListenerimplementsOnClickListener{ @Override publicvoidonClick(Viewv){ //TODOAuto-generatedmethodstub showDialog(); } } publicvoidshowDialog(){ AlertDialog.Builderbuilder; AlertDialogdialog; LayoutInflaterinflater=(LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE); //LayoutInflaterinflater=getLayoutInflater(); //LayoutInflaterinflater=LayoutInflater.from(this); Viewlayout=inflater.inflate(R.layout.dialog,null); TextViewtx=(TextView)layout.findViewById(R.id.dialog_txt); tx.setText("测试LayoutInflater"); ImageViewimg=(ImageView)layout.findViewById(R.id.img); img.setImageResource(R.drawable.icon); builder=newAlertDialog.Builder(this); builder.setView(layout); dialog=builder.create(); dialog.show(); } } 效果图: 本文转自 lingdududu 51CTO博客,原文链接: http://blog.51cto.com/liangruijun/750495

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册