在账户登录错误时,如果显示弹出框则会显得很难看而且不友好。当然使用Toast也是不错的选择。在这里我们提供一种Animation的动画效果来提示输入错误。
当用户名或者密码错误时,输入框会左右震动,来表示“用户名或者密码错误”。同时,通过这个小案例,来初步了解Animation动画。
Animation的XML
在项目的res目录下新建anim文件夹,用来存放Animation动画的XML。
新建shake.xml如下:
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:toXDelta="10" android:duration="1000" android:interpolator="@anim/cycle_7" />
Animation shake = AnimationUtils.loadAnimation(this, R.anim.shake); findViewById(R.id.editText2).startAnimation(shake); findViewById(R.id.editText1).startAnimation(shake);
package com.app; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.EditText; import android.widget.Toast; @SuppressLint("NewApi") public class MyQQActivity extends Activity implements View.OnClickListener{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.qq_login); findViewById(R.id.button1).setOnClickListener(this); findViewById(R.id.button2).setOnClickListener(this); findViewById(R.id.button3).setOnClickListener(this); } public void onClick(View v) { EditText editText1 = (EditText) findViewById(R.id.editText1); String text1 = editText1.getText().toString(); EditText editText2 = (EditText) findViewById(R.id.editText2); String text2 = editText2.getText().toString(); switch (v.getId()) { case R.id.button1: if (text1.equals(text2)) { Intent intent2 = new Intent(); intent2.setClass(MyQQActivity.this,Tabs.class ); startActivity(intent2); int version = Integer.valueOf(android.os.Build.VERSION.SDK); if(version >= 5) { overridePendingTransition(R.anim.zoomin, R.anim.zoomout); } } else { Toast.makeText(MyQQActivity.this, "账号或密码错误,请重新输入!", Toast.LENGTH_LONG).show(); Animation shake = AnimationUtils.loadAnimation(this, R.anim.shake); findViewById(R.id.editText2).startAnimation(shake); findViewById(R.id.editText1).startAnimation(shake); editText2.setText(null); } break; case R.id.button2: //注册账号 Uri uri1 = Uri.parse("http://zc.qq.com/chs/index.html"); Intent it1 = new Intent(Intent.ACTION_VIEW,uri1); startActivity(it1); break; case R.id.button3: //忘记密码 Uri uri2 = Uri.parse("https://aq.qq.com/cn2/findpsw/pc/pc_find_pwd_input_account"); Intent it2 = new Intent(Intent.ACTION_VIEW,uri2); startActivity(it2); break; default: break; } } }
layout文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/qqlogin" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@drawable/phone_call_bg" android:padding="10dp"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView android:id="@+id/imageView1" android:layout_width="fill_parent" android:layout_height="120dp" android:layout_marginTop="15dp" android:src="/blog_article/@drawable/ic_launcher/index.html" /> </LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="15dp" android:background="@drawable/login_management_background" android:padding="10dp" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="15dp" android:layout_marginTop="10dp" android:layout_marginBottom="5dp" android:text="账号" android:textSize="20dp" android:textColor="#333"/> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/textView1" android:layout_alignLeft="@+id/textView1" android:layout_marginLeft="15dp" android:layout_marginTop="5dp" android:layout_marginBottom="10dp" android:text="密码" android:textSize="20dp" android:textColor="#333"/> <EditText android:id="@+id/editText1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="3dp" android:layout_alignBaseline="@+id/textView1" android:layout_alignBottom="@+id/textView1" android:layout_marginLeft="22dp" android:drawableTop="#fff" android:hint="用户名/邮箱" android:phoneNumber="true" android:layout_toRightOf="@+id/textView1" android:ems="10" > <requestFocus /> </EditText> <EditText android:id="@+id/editText2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/textView2" android:layout_alignBottom="@+id/textView2" android:layout_alignLeft="@+id/editText1" android:ems="10" android:phoneNumber="true" android:drawableTop="#fff" android:hint="请输入密码" android:inputType="textPassword" /> </RelativeLayout> <LinearLayout android:layout_width="wrap_content" android:layout_marginTop="15dp" android:layout_height="40dp" android:orientation="horizontal" > <Button android:id="@+id/button1" android:layout_width="170dp" android:layout_height="40dp" android:layout_marginLeft="60dp" android:background="@drawable/bg_alibuybutton" android:text="登 录" android:gravity="center" android:textSize="20sp" android:textColor="#000000"/> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_marginTop="100dp" android:gravity="center_horizontal" android:orientation="horizontal" android:layout_height="wrap_content" > <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:text="注册账号" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:text="忘记密码" /> </LinearLayout> </LinearLayout>
效果图
作者:Younger Liu,
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。
原文地址:http://lwn.net/Articles/396657/
1. 简介连续内存分配器(CMA - Contiguous Memory Allocator)是一个框架,允许建立一个平台无关的配置,用于连续内存的管理。然后,设备所需内存都根据该配置进行分配。
这个框架的主要作用不是分配内存,而是解析和管理内存配置,以及作为在设备驱动程序和可插拔的分配器之间的中间组件。因此,它是与任何内存分配方法和分配策略没有依赖关系的。
2. 为什么需要?在嵌入式设备中,很多设备都没有支持scatter-getter和IO map,都需要连续内存块的操作。如设备:摄像机,硬件视频解码器,编码器等。
这些设备往往需要较大的内存缓冲区(如:一个200万像素的高清帧摄像机,需要超过6M的内存),该kmalloc内存分配机制对于这么大的内存是没有效果的。
一些嵌入式设备对缓冲区有一些额外的要求,比如:在含有多个内存bank的设备中,要求只能在特定的bank中中分配内存;而还有一些要定内存边界对齐的缓存区。
近来,嵌入式设备有了较大的发展(特别是V4L领域),并且这些驱动都有自己的内存分配代码。它们众多的大多数都是采用bootmem分配方法。CMA框架企图采用统一的连续内存分配机制,并为这些设备驱动提供简单的API,而且是可以定制化和模块化的。
3. 设计
CMA主要设计目标是提供一个可定制的模块化框架,并且是可以配置的,以适应个别系统的需要。配置指定的内存区域,然后将这些内存分配给制定的设备。这些内存区域可以共享给多个设备驱动,也可以专门分配一个。这是通过以下方式实现的:
1. CMA的核心不是处理内存分配和空闲空间管理。专用分配器是用来处理内存分配和空闲内存管理的。因此,如果现有的不符合给定的系统,那么可以开发一种新的算法,这种算饭可以很容易地插入到CMA框架中。
所提出的中包括一个最适算法(best-fit)的一个实现。
2. CMA允许运行时配置即将分配的内存区域。内存区域都是经由命令行给出的,所以可以很容易地改变它,而不需要重新编译内核。
每个地区都有自己的大小,对齐标准,起始地址(物理地址)和对应该内存区域的内存分配算法。
这意味着同一时刻可以有多中机制在运行,如果在一个平台上同时运行多个不同的设备,这些设备具有不同的存储器使用特性,那么局可以匹配最好的算法。
3. 当设备请求内存时,设备必须“自我介绍”,即附带自己的信息以告知CMA。这样CMA可以知道谁分配内存。这允许系统架构师来指定哪个移动设备应该使用哪些存储区。
3a. 设备也可以指定一个“类”内存区域,这使得系统更容易配置,进而一个单一的设备可能使用来自不同内存区域的内存。例如,一个视频解码器驱动程序可能要分配一些共享的缓冲区,那么从第一个bank中分配一些,再从第二个bank中分配一些,可以获得尽可能高的内存吞吐量。
4. 使用场景
虚构一个使用了CMA的系统,来观察一下其是如何使用和配置的。
有一个携带硬件视频解码器和摄像机的平台,每个设备在最坏的情况下可能需要20M的内存。在该系统中,这两个设备是不会同时使用的,并且内存是可能共享的。使用下面的两个命令行:
cma=r=20M cma_map=video,camera=r
第一个CMA指令是分配20M的内存,并且内存分配器是有效的;第二个表示,名称为“video”和“camera”的两个驱动从之前定义的内存区域中分配内存。
因为两者共享同一内存区域,相比于每个设备保留20M的内存区域,使得系统节省了20M的内存空进。
但是随着系统的发展和进化,平台上可能同时运行视频解码器和摄像机,那么20M的内存区域就不能满足需要了。那么可以通过命令快速解决:
cma=v=20M,c=20M cma_map=video=v;camera=c
从该中也可以看出CMA是如何为每一个设备分配所需的私有内存池的。
分配机制也能通过一种相似的方式进行替换。在测试中发现,当给定的内存区域大小为40M时,系统运行一段时间后,碎片会成为一个问题。因此,为了满足所需要求的缓存区大小,需要预留一个较大的缓存区。
但是不幸的是,你需要w设置一个新的分配算法——Neat Allocation Algorithm(简写na),这两个设备对于内存有30M的需求:
cma=r=30M:na cma_map=video,camera=r
从上述示例可以看出,当CMA提供的算法不满足要求时,如何配置自己的分配算法,而不需要修改CMA或重编内核。
5. 技术细节 5.1 命令行参数
如上图所示,CMA有两个参数“cma”和“cma_map”;其中“cma”指定要为CMA保留的内存大小,参数“cma_map”用于指定该区域分配给为哪一个设备使用。
参数“cma”格式如下:
cma ::= "cma=" regions [ ';' ]
regions ::= region [ ';' regions ]
region ::= reg-name
'=' size
[ '@' start ]
[ '/' alignment ]
[ ':' [ alloc-name ] [ '(' alloc-params ')' ] ]
reg-name ::= a sequence of letters and digits
//内存区的名称
size ::= memsize // 内存区的大小
start ::= memsize // 期望的内存区的起始地址
alignment ::= memsize // 起始地址的对齐倍数
alloc-name ::= a non-empty sequence of letters and digits
// 将要使用的分配器的名称
alloc-params ::= a sequence of chars other then ')' and ';'
// 分配器的参数
memsize ::= whatever memparse() accepts
参数"cma_map" 的格式如下:
cma-map ::= "cma_map=" rules [ ';' ]
rules ::= rule [ ';' rules ]
rule ::= patterns '=' regions
patterns ::= pattern [ ',' patterns ]
regions ::= reg-name [ ',' regions ]
// 与device相匹配的内存区的名称
pattern ::= dev-pattern [ '/' kind-pattern ]
| '/' kind-pattern
// pattern request must match for this rule to
// apply to it; the first rule that matches is
// applied; if dev-pattern part is omitted
// value identical to the one used in previous
// pattern is assumed
dev-pattern ::= pattern-str
// pattern that device name must match for the
// rule to apply.
kind-pattern ::= pattern-str
// pattern that "kind" of memory (provided by
// device) must match for the rule to apply.
pattern-str ::= a non-empty sequence of characters with '?'
meaning any character and possible '*' at
the end meaning to match the rest of the
string
示例 (whitespace added for better readability):
cma = r1 = 64M // 64M区域
@512M // 开始于512M (或尽可能接近)
/1M // 确保内存区域1M对齐
:foo(bar); // 使用带参bar的分配器foo
r2 = 64M // 64M区域
/1M; // 确保内存区域1M对齐
// 使用第一个有效分配器
r3 = 64M // 64M区域
@512M // 开始于512M (或尽可能接近)
:foo; // 使用不带参的分配器foo
cma_map = foo = r1; // kind==NULL的foo设备使用区域r1
foo/quaz = r2; // OR:
/quaz = r2; // kind == "quaz"的设备foo使用区域r2
foo/* = r3; // OR:
/* = r3; // 任何kinde的设备foo均可使用区域r3
bar/* = r1,r2; // 任何kind的设备bar均可使用区域r1和r2
baz?/a* , baz?/b* = r3;
// 任何名如baz?且kind为a或b的设备都可以区域r3,其中?代表任意字符
5.2 设备与内存类别
设备名称来自device结构体。如果一个驱动没有注册为device,那么它是不能使用CMA的(通常,至少提供一个伪设备)。
无论设备何时申请内存,内存的类别都是一个可选的参数。在很多场景下,这个参数是可以忽略的,但是有时某些设备也可能需要。
比如:当前有两个内存bank,由于性能的原因,这两个内存bank设备都会使用,那么此时,设备驱动需要为这两个不同的buffer定义两个不同的名称。
cma=a=32M@0,b=32M@512M cma_map=foo/a=a;foo/b=b
但是无论何时,驱动分配内存都需要指定为某一个内存区域:
buffer1 = cma_alloc(dev, 1 << 20, 0, "a");
buffer2 = cma_alloc(dev, 1 << 20, 0, "b");
当然,如果需要(比如,当指定的内存已经使用完),需要允许驱动从其他的内存bank中分配。命令行参数可以改为:
cma=a=32M@0,b=32M@512M cma_map=foo/a=a,b;foo/b=b,a
换句话说,如果同一个设备在某一个系统上只使用一个内存bank,命令行参数:
cma=r=64M cma_map=foo/*=r
驱动无需做出任何更改。
5.3 API接口
CMA框架为设备提供了四个接口,分配接口cma_alloc():
unsigned long cma_alloc(const struct device *dev,
const char *kind,
unsigned long size,
unsigned long alignment);
如果需要,设备可能需要指定对齐规格(这个规格是内存chunk需要满足的),必须是2的幂次或0.而chunks一般至少一PAGE对齐的。(page大小一般为4k)。
参数kind指定内存区域名称,如果没有指定,则采用NULL。调用示例:
addr = cma_alloc(dev, NULL, size, 0);
该函数返回的是物理地址,一般需要判断返回值的有效性:
unsigned long addr = cma_alloc(dev, size);
if (IS_ERR_VALUE(addr))
return (int)addr;
/* Allocated */
(Make sure to include <linux/err.h> which contains the definition of the IS_ERR_VALUE() macro.)
释放函数cma_put():
int cma_put(unsigned long addr);
参数为内存块的物理地址,实现机制是递减引用计数,如果引用计数变为0,则释放该内存块。绝大部分时候,用户无需关注引用计数,只需要简单地调用cma_put()就可以了。
当该内存块共享给其他的驱动时,就需要调用cma_get()递增引用计数:
int cma_put(unsigned long addr);
最后一个函数是cma_info(),返回分配给指定(dev, kind)的内存缓存区的描述信息。原型如下:
int cma_info(struct cma_info *info,
const struct device *dev,
const char *kind);
通过这个函数可以获取内存区的边间,大小和对应于(dev, kind)内存区的个数。
5.4 分配操作
为CMA创建一个分配器需要实现四个函数。
前两个是用来初始化和卸载分配器:
int cma_foo_init(struct cma_region *reg);
void cma_foo_done(struct cma_region *reg);
第一个函数在平台初始化时调用。结构体cma_region记录了缓存区的起始地址、大小,也会记录经由命令行传入的alloc_params域。
当调用函数cma_foo_done()时,均认为改缓存区的内存已经全部释放。
另外两个函数式块分配和释放函数:
struct cma_chunk *cma_foo_alloc(struct cma_region *reg,
unsigned long size,
unsigned long alignment);
void cma_foo_free(struct cma_chunk *chunk);
每一个函数,都是唯一线程访问的。因此,分配器不需要担心并发。
当分配器已经实现后,剩下的就是注册了,在文件"mm/cma-allocators.h"中定义如下语句:
CMA_ALLOCATOR("foo", foo)
第一个foo是命令行使用的分配器的名称,第二个是函数名字。
5.5 平台集成
在平台初始化过程,会调用函数cma_regions_allocate():
void cma_regions_allocate(int (*alloc)(struct cma_region *reg));
会遍历命令行提过所有的内存区并为他们保留内存。只有一个参数cma_region用于保留内存。传入NULL会调用cma_region_alloc()函数来通过bootmem来分配内存。
平台也会提过cma_defaults()提供默认的cma和cma_map参数:
int cma_defaults(const char *cma, const char *cma_map)
6. 展望
在将来,CMA机制能够实现:CMA空闲区域能够用于page cache,文件系统buffer或设备交换区。如此,内存将不再会被浪费。
因为CMA的内存是经由CMA框架分配和释放的,所以CMA框架能够知道哪些内存分配了,哪些内存释放了。因此,可以跟踪未使用的内存,使得CMA可以将未使用的内存用于其他目的,如page cache,IO缓存,交换缓存等等。
作者:Younger Liu,
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。
1.设计线程安全类的过程中,需要包含以下三个基本要素
a,找出构成对象状态的所有变量。
b,找出约束状态变量的不变性条件
c,建立对象状态的并发访问管理策略
要分析对象的状态,首先从对象的域开始,如果对象中所有的域都是基本类型的变量,那么这些域将构成对象的全部状态。
2.要确保类的线程安全性,就需要确保它的不变性条件不会在并发访问的情况下被破坏,这就需要对其状态进行推断。
3.ServletContext为servlet提供了类似于Map形式的对象容器服务,在ServletContext中可以通过名称来注册或获取应用程序对象,由Servlet容器实现的ServletContext对象必须是线程安全的,因为它肯定会被多个线程同时访问。
4.实例封闭
如果某对象不是线程安全的,那么可以通过多种技术使其在多线程程序中安全使用。你可以确保该对象只能由单个线程访问(线程封闭),或者通过一个锁来保护对该对象的所有访问。
封装简化了线程安全类的实现过程,它提供了一种实力封闭机制。当一个对象被封装到另一个对象中时,能够访问被封装对象的所有代码路径都是已知的。
将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。
5.使用私有的锁对象而不是对象的内置锁(或者其他可通过公有方式访问的锁),有许多优点。私有的锁对象可以将锁封装起来,使客户端代码无法得到锁,但客户端代码可以通过公有方法来访问锁,以便参与到他的同步策略中。
6.线程安全的委托:线程安全类的状态委托给单个线程安全的状态变量。还可以将线程安全性委托给多个状态变量,只要这些变量是彼此独立的,即组合而成的类并不会在其包含的多个状态变量上增加任何不变性的条件。