android头像设置:从本地照片库或拍照获取并剪裁

功能介绍

制作android应用时,用户注册的功能必不可少,往往还需要具备用户头像的编辑功能,设置过程如下图:

        界面设计

建立一个缩略图ImageView,点击时,弹出设置头像的对话框,设置完成后,刷新缩略图;

[code lang="xml"]</code>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#F3F1DA"
android:orientation="vertical" >

<!-- title -->

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/title_bg"
android:gravity="center"
android:orientation="horizontal" >

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/title_bar_txt"
android:textColor="@android:color/white" />
</LinearLayout>

<!-- image switch -->

<RelativeLayout
android:id="@+id/switch_face_rl"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="20dip"
android:background="@drawable/item_edit_bg"
android:clickable="true"
android:padding="5dip" >

<ImageView
android:id="@+id/face"
android:layout_width="42dip"
android:layout_height="42dip"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dip"
android:src="@drawable/mini_avatar" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginTop="5dip"
android:layout_toRightOf="@id/face"
android:layout_gravity="center"
android:text="点击设置头像"
android:textColor="@android:color/black" />
</RelativeLayout>

</LinearLayout>

[/code]

Activity设计

MainActivity.java:

[code lang="java"]</code>

package com.xzw.picture;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import com.xzw.utils.Tools;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

/**
*
* @author XuZhiwei (xuzw13@gmail.com) Create at 2012-8-17 上午10:14:40
*/
public class MainActivity extends Activity {
/* 组件 */
private RelativeLayout switchAvatar;
private ImageView faceImage;

private String[] items = new String[] { "选择本地图片", "拍照" };
/* 头像名称 */
private static final String IMAGE_FILE_NAME = "faceImage.jpg";

/* 请求码 */
private static final int IMAGE_REQUEST_CODE = 0;
private static final int CAMERA_REQUEST_CODE = 1;
private static final int RESULT_REQUEST_CODE = 2;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 去掉标题
setContentView(R.layout.main);
switchAvatar = (RelativeLayout) findViewById(R.id.switch_face_rl);
faceImage = (ImageView) findViewById(R.id.face);
// 设置事件监听
switchAvatar.setOnClickListener(listener);
}

private View.OnClickListener listener = new View.OnClickListener() {

@Override
public void onClick(View v) {
showDialog();
}
};

/**
* 显示选择对话框
*/
private void showDialog() {

new AlertDialog.Builder(this)
.setTitle("设置头像")
.setItems(items, new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
Intent intentFromGallery = new Intent();
intentFromGallery.setType("image/*"); // 设置文件类型
intentFromGallery
.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intentFromGallery,
IMAGE_REQUEST_CODE);
break;
case 1:
Intent intentFromCapture = new Intent(
MediaStore.ACTION_IMAGE_CAPTURE);
// 判断存储卡是否可以用,可用进行存储
if (Tools.hasSdcard()) {
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File file = new File(path,IMAGE_FILE_NAME);
intentFromCapture.putExtra(
MediaStore.EXTRA_OUTPUT,
Uri.fromFile(file));
}

startActivityForResult(intentFromCapture,CAMERA_REQUEST_CODE);
break;
}
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//结果码不等于取消时候
if (resultCode != RESULT_CANCELED) {

switch (requestCode) {
case IMAGE_REQUEST_CODE:
startPhotoZoom(data.getData());
break;
case CAMERA_REQUEST_CODE:
if (Tools.hasSdcard()) {
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File tempFile = new File(path,IMAGE_FILE_NAME);
startPhotoZoom(Uri.fromFile(tempFile));
} else {
Toast.makeText(MainActivity.this, "未找到存储卡,无法存储照片!",Toast.LENGTH_LONG).show();
}
break;
case RESULT_REQUEST_CODE: //图片缩放完成后
if (data != null) {
getImageToView(data);
}
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}

/**
* 裁剪图片方法实现
*
* @param uri
*/
public void startPhotoZoom(Uri uri) {

Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// 设置裁剪
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", 340);
intent.putExtra("outputY", 340);
intent.putExtra("return-data", true);
startActivityForResult(intent, RESULT_REQUEST_CODE);
}

/**
* 保存裁剪之后的图片数据
*
* @param picdata
*/
private void getImageToView(Intent data) {
Bundle extras = data.getExtras();
if (extras != null) {
Bitmap photo = extras.getParcelable("data");
Drawable drawable = new BitmapDrawable(this.getResources(),photo);
faceImage.setImageDrawable(drawable);
}
}

}
[/code]

Tools.java

[code lang="java"]</code>

package com.xzw.utils;

import android.os.Environment;
/**
*
* @author XuZhiwei (xuzw13@gmail.com)
* Create at 2012-8-17 上午10:14:40
*/
public class Tools {
/**
* 检查是否存在SDCard
* @return
*/
public static boolean hasSdcard(){
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
return true;
}else{
return false;
}
}
}
[/code]

天气预报接口API

国家气象局提供的天气预报接口

接口地址:

http://www.weather.com.cn/data/sk/101010100.html

http://www.weather.com.cn/data/cityinfo/101010100.html

http://m.weather.com.cn/data/101010100.html

 

对于第三个接口,返回信息比较全面,也是以json格式提供,格式如下

{“weatherinfo”: {
//基本信息;
“city”:”北京”,”city_en”:”beijing”,
“date_y”:”2012年2月16日”, “date”:””, “week”:”星期四”, “fchh”:”11″, “cityid”:”101010100″,
//摄氏温度
“temp1″:”2℃~-7℃”,
“temp2″:”1℃~-7℃”,
“temp3″:”4℃~-7℃”,
“temp4″:”7℃~-5℃”,
“temp5″:”5℃~-3℃”,
“temp6″:”5℃~-2℃”,
//华氏温度;
“tempF1″:”35.6℉~19.4℉”,
“tempF2″:”33.8℉~19.4℉”,
“tempF3″:”39.2℉~19.4℉”,
“tempF4″:”44.6℉~23℉”,
“tempF5″:”41℉~26.6℉”,
“tempF6″:”41℉~28.4℉”,
//天气描述;
“weather1″:”晴”,
“weather2″:”晴”,
“weather3″:”晴”,
“weather4″:”晴转多云”,
“weather5″:”多云”,
“weather6″:”多云转阴”,
//天气描述图片序号
“img1″:”0”,
“img2″:”99”,
“img3″:”0”,
“img4″:”99”,
“img5″:”0”,
“img6″:”99”,
“img7″:”0”,
“img8″:”1”,
“img9″:”1”,
“img10″:”99”,
“img11″:”1”,
“img12″:”2”,
“img_single”:”0″,
//图片名称;
“img_title1″:”晴”,
“img_title2″:”晴”,
“img_title3″:”晴”,
“img_title4″:”晴”,
“img_title5″:”晴”,
“img_title6″:”晴”,
“img_title7″:”晴”,
“img_title8″:”多云”,
“img_title9″:”多云”,
“img_title10″:”多云”,
“img_title11″:”多云”,
“img_title12″:”阴”,
“img_title_single”:”晴”,
//风速描述
“wind1″:”北风3-4级转微风”,
“wind2″:”微风”,
“wind3″:”微风”,
“wind4″:”微风”,
“wind5″:”微风”,
“wind6″:”微风”,
//风速级别描述
“fx1″:”北风”,
“fx2″:”微风”,
“fl1″:”3-4级转小于3级”,
“fl2″:”小于3级”,
“fl3″:”小于3级”,
“fl4″:”小于3级”,
“fl5″:”小于3级”,
“fl6″:”小于3级”,
//今天穿衣指数;
“index”:”冷”,
“index_d”:”天气冷,建议着棉衣、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣或冬大衣。”,
//48小时穿衣指数
“index48″:”冷”,
“index48_d”:”天气冷,建议着棉衣、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣或冬大衣。”,
//紫外线及48小时紫外线
“index_uv”:”弱”,
“index48_uv”:”弱”,
//洗车
“index_xc”:”适宜”,
//旅游
“index_tr”:”一般”,
//舒适指数
“index_co”:”较不舒适”,

“st1″:”1”,
“st2″:”-8″,
“st3″:”2”,
“st4″:”-4″,
“st5″:”5”,
“st6″:”-5″,
//晨练
“index_cl”:”较不宜”,
//晾晒
“index_ls”:”基本适宜”,
//过敏
“index_ag”:”极不易发”}}

 

全车各城市代码如下:

101010100=北京
101010200=海淀
101010300=朝阳
101010400=顺义
101010500=怀柔
101010600=通州
101010700=昌平
101010800=延庆
101010900=丰台
101011000=石景山
101011100=大兴
101011200=房山
101011300=密云
101011400=门头沟
101011500=平谷
101011600=八达岭
101011700=佛爷顶
101011800=汤河口
101011900=密云上甸子
101012000=斋堂
101012100=霞云岭

101020100=上海
101020200=闵行
101020300=宝山
101020400=川沙
101020500=嘉定
101020600=南汇
101020700=金山
101020800=青浦
101020900=松江
101021000=奉贤
101021100=崇明
101021101=陈家镇
101021102=引水船
101021200=徐家汇
101021300=浦东

101030100=天津
101030200=武清
101030300=宝坻
101030400=东丽
101030500=西青
101030600=北辰
101030700=宁河
101030800=汉沽
101030900=静海
101031000=津南
101031100=塘沽
101031200=大港
101031300=平台
101031400=蓟县

101040100=重庆
101040200=永川
101040300=合川
101040400=南川
101040500=江津
101040600=万盛
101040700=渝北
101040800=北碚
101040900=巴南
101041000=长寿
101041100=黔江
101041200=万州天城
101041300=万州龙宝
101041400=涪陵
101041500=开县
101041600=城口
101041700=云阳
101041800=巫溪
101041900=奉节
101042000=巫山
101042100=潼南
101042200=垫江
101042300=梁平
101042400=忠县
101042500=石柱
101042600=大足
101042700=荣昌
101042800=铜梁
101042900=璧山
101043000=丰都
101043100=武隆
101043200=彭水
101043300=綦江
101043400=酉阳
101043500=金佛山
101043600=秀山
101043700=沙坪坝

101050101=哈尔滨
101050102=双城
101050103=呼兰
101050104=阿城
101050105=宾县
101050106=依兰
101050107=巴彦
101050108=通河
101050109=方正
101050110=延寿
101050111=尚志
101050112=五常
101050113=木兰
101050201=齐齐哈尔
101050202=讷河
101050203=龙江
101050204=甘南
101050205=富裕
101050206=依安
101050207=拜泉
101050208=克山
101050209=克东
101050210=泰来
101050301=牡丹江
101050302=海林
101050303=穆棱
101050304=林口
101050305=绥芬河
101050306=宁安
101050307=东宁
101050401=佳木斯
101050402=汤原
101050403=抚远
101050404=桦川
101050405=桦南
101050406=同江
101050407=富锦
101050501=绥化
101050502=肇东
101050503=安达
101050504=海伦
101050505=明水
101050506=望奎
101050507=兰西
101050508=青冈
101050509=庆安
101050510=绥棱
101050601=黑河
101050602=嫩江
101050603=孙吴
101050604=逊克
101050605=五大连池
101050606=北安
101050701=大兴安岭
101050702=塔河
101050703=漠河
101050704=呼玛
101050705=呼中
101050706=新林
101050707=阿木尔
101050708=加格达奇
101050801=伊春
101050802=乌伊岭
101050803=五营
101050804=铁力
101050805=嘉荫
101050901=大庆
101050902=林甸
101050903=肇州
101050904=肇源
101050905=杜蒙
101051002=七台河
101051003=勃利
101051101=鸡西
101051102=虎林
101051103=密山
101051104=鸡东
101051201=鹤岗
101051202=绥滨
101051203=萝北
101051301=双鸭山
101051302=集贤
101051303=宝清
101051304=饶河

101060101=长春
101060102=农安
101060103=德惠
101060104=九台
101060105=榆树
101060106=双阳
101060201=吉林
101060202=舒兰
101060203=永吉
101060204=蛟河
101060205=磐石
101060206=桦甸
101060207=烟筒山
101060301=延吉
101060302=敦化
101060303=安图
101060304=汪清
101060305=和龙
101060306=天池
101060307=龙井
101060308=珲春
101060309=图们
101060310=松江
101060311=罗子沟
101060312=延边
101060401=四平
101060402=双辽
101060403=梨树
101060404=公主岭
101060405=伊通
101060406=孤家子
101060501=通化
101060502=梅河口
101060503=柳河
101060504=辉南
101060505=集安
101060506=通化县
101060601=白城
101060602=洮南
101060603=大安
101060604=镇赉
101060605=通榆
101060701=辽源
101060702=东丰
101060801=松原
101060802=乾安
101060803=前郭
101060804=长岭
101060805=扶余
101060901=白山
101060902=靖宇
101060903=临江
101060904=东岗
101060905=长白

101070101=沈阳
101070102=苏家屯
101070103=辽中
101070104=康平
101070105=法库
101070106=新民
101070107=于洪
101070108=新城子
101070201=大连
101070202=瓦房店
101070203=金州
101070204=普兰店
101070205=旅顺
101070206=长海
101070207=庄河
101070208=皮口
101070209=海洋岛
101070301=鞍山
101070302=台安
101070303=岫岩
101070304=海城
101070401=抚顺
101070403=清原
101070404=章党
101070501=本溪
101070502=本溪县
101070503=草河口
101070504=桓仁
101070601=丹东
101070602=凤城
101070603=宽甸
101070604=东港
101070605=东沟
101070701=锦州
101070702=凌海
101070703=北宁
101070704=义县
101070705=黑山
101070706=北镇
101070801=营口
101070802=大石桥
101070803=盖州
101070901=阜新
101070902=彰武
101071001=辽阳
101071002=辽阳县
101071003=灯塔
101071101=铁岭
101071102=开原
101071103=昌图
101071104=西丰
101071201=朝阳
101071202=建平
101071203=凌源
101071204=喀左
101071205=北票
101071206=羊山
101071207=建平县
101071301=盘锦
101071302=大洼
101071303=盘山
101071401=葫芦岛
101071402=建昌
101071403=绥中
101071404=兴城

101080101=呼和浩特
101080102=土默特左旗
101080103=托克托
101080104=和林格尔
101080105=清水河
101080106=呼和浩特市郊区
101080107=武川
101080201=包头
101080202=白云鄂博
101080203=满都拉
101080204=土默特右旗
101080205=固阳
101080206=达尔罕茂明安联合旗
101080207=石拐
101080301=乌海
101080401=集宁
101080402=卓资
101080403=化德
101080404=商都
101080405=希拉穆仁
101080406=兴和
101080407=凉城
101080408=察哈尔右翼前旗
101080409=察哈尔右翼中旗
101080410=察哈尔右翼后旗
101080411=四子王旗
101080412=丰镇
101080501=通辽
101080502=舍伯吐
101080503=科尔沁左翼中旗
101080504=科尔沁左翼后旗
101080505=青龙山
101080506=开鲁
101080507=库伦旗
101080508=奈曼旗
101080509=扎鲁特旗
101080510=高力板
101080511=巴雅尔吐胡硕
101080512=通辽钱家店
101080601=赤峰
101080602=赤峰郊区站
101080603=阿鲁科尔沁旗
101080604=浩尔吐
101080605=巴林左旗
101080606=巴林右旗
101080607=林西
101080608=克什克腾旗
101080609=翁牛特旗
101080610=岗子
101080611=喀喇沁旗
101080612=八里罕
101080613=宁城
101080614=敖汉旗
101080615=宝过图
101080701=鄂尔多斯
101080703=达拉特旗
101080704=准格尔旗
101080705=鄂托克前旗
101080706=河南
101080707=伊克乌素
101080708=鄂托克旗
101080709=杭锦旗
101080710=乌审旗
101080711=伊金霍洛旗
101080712=乌审召
101080713=东胜
101080801=临河
101080802=五原
101080803=磴口
101080804=乌拉特前旗
101080805=大佘太
101080806=乌拉特中旗
101080807=乌拉特后旗
101080808=海力素
101080809=那仁宝力格
101080810=杭锦后旗
101080811=巴盟农试站
101080901=锡林浩特
101080902=朝克乌拉
101080903=二连浩特
101080904=阿巴嘎旗
101080905=伊和郭勒
101080906=苏尼特左旗
101080907=苏尼特右旗
101080908=朱日和
101080909=东乌珠穆沁旗
101080910=西乌珠穆沁旗
101080911=太仆寺旗
101080912=镶黄旗
101080913=正镶白旗
101080914=正兰旗
101080915=多伦
101080916=博克图
101080917=乌拉盖
101080918=白日乌拉
101080919=那日图
101081000=呼伦贝尔
101081001=海拉尔
101081002=小二沟
101081003=阿荣旗
101081004=莫力达瓦旗
101081005=鄂伦春旗
101081006=鄂温克旗
101081007=陈巴尔虎旗
101081008=新巴尔虎左旗
101081009=新巴尔虎右旗
101081010=满洲里
101081011=牙克石
101081012=扎兰屯
101081014=额尔古纳
101081015=根河
101081016=图里河
101081101=乌兰浩特
101081102=阿尔山
101081103=科尔沁右翼中旗
101081104=胡尔勒
101081105=扎赉特旗
101081106=索伦
101081107=突泉
101081108=霍林郭勒
101081201=阿拉善左旗
101081202=阿拉善右旗
101081203=额济纳旗
101081204=拐子湖
101081205=吉兰太
101081206=锡林高勒
101081207=头道湖
101081208=中泉子
101081209=巴彦诺尔贡
101081210=雅布赖
101081211=乌斯太
101081212=孪井滩

101090101=石家庄
101090102=井陉
101090103=正定
101090104=栾城
101090105=行唐
101090106=灵寿
101090107=高邑
101090108=深泽
101090109=赞皇
101090110=无极
101090111=平山
101090112=元氏
101090113=赵县
101090114=辛集
101090115=藁城
101090116=晋洲
101090117=新乐
101090201=保定
101090202=满城
101090203=阜平
101090204=徐水
101090205=唐县
101090206=高阳
101090207=容城
101090208=紫荆关
101090209=涞源
101090210=望都
101090211=安新
101090212=易县
101090213=涞水
101090214=曲阳
101090215=蠡县
101090216=顺平
101090217=雄县
101090218=涿州
101090219=定州
101090220=安国
101090221=高碑店
101090301=张家口
101090302=宣化
101090303=张北
101090304=康保
101090305=沽源
101090306=尚义
101090307=蔚县
101090308=阳原
101090309=怀安
101090310=万全
101090311=怀来
101090312=涿鹿
101090313=赤城
101090314=崇礼
101090402=承德
101090403=承德县
101090404=兴隆
101090405=平泉
101090406=滦平
101090407=隆化
101090408=丰宁
101090409=宽城
101090410=围场
101090411=塞罕坎
101090501=唐山
101090502=丰南
101090503=丰润
101090504=滦县
101090505=滦南
101090506=乐亭
101090507=迁西
101090508=玉田
101090509=唐海
101090510=遵化
101090511=迁安
101090601=廊坊
101090602=固安
101090603=永清
101090604=香河
101090605=大城
101090606=文安
101090607=大厂
101090608=霸州
101090609=三河
101090701=沧州
101090702=青县
101090703=东光
101090704=海兴
101090705=盐山
101090706=肃宁
101090707=南皮
101090708=吴桥
101090709=献县
101090710=孟村
101090711=泊头
101090712=任丘
101090713=黄骅
101090714=河间
101090715=曹妃甸
101090801=衡水
101090802=枣强
101090803=武邑
101090804=武强
101090805=饶阳
101090806=安平
101090807=故城
101090808=景县
101090809=阜城
101090810=冀州
101090811=深州
101090901=邢台
101090902=临城
101090903=邢台县浆水
101090904=内邱
101090905=柏乡
101090906=隆尧
101090907=南和
101090908=宁晋
101090909=巨鹿
101090910=新河
101090911=广宗
101090912=平乡
101090913=威县
101090914=清河
101090915=临西
101090916=南宫
101090917=沙河
101090918=任县
101091001=邯郸
101091002=峰峰
101091003=临漳
101091004=成安
101091005=大名
101091006=涉县
101091007=磁县
101091008=肥乡
101091009=永年
101091010=邱县
101091011=鸡泽
101091012=广平
101091013=馆陶
101091014=魏县
101091015=曲周
101091016=武安
101091101=秦皇岛
101091102=青龙
101091103=昌黎
101091104=抚宁
101091105=卢龙
101091106=北戴河

101100101=太原
101100102=清徐
101100103=阳曲
101100104=娄烦
101100105=太原古交区
101100106=太原北郊
101100107=太原南郊
101100201=大同
101100202=阳高
101100203=大同县
101100204=天镇
101100205=广灵
101100206=灵邱
101100207=浑源
101100208=左云
101100301=阳泉
101100302=盂县
101100303=平定
101100401=晋中
101100402=榆次
101100403=榆社
101100404=左权
101100405=和顺
101100406=昔阳
101100407=寿阳
101100408=太谷
101100409=祁县
101100410=平遥
101100411=灵石
101100412=介休
101100501=长治
101100502=黎城
101100503=屯留
101100504=潞城
101100505=襄垣
101100506=平顺
101100507=武乡
101100508=沁县
101100509=长子
101100510=沁源
101100511=壶关
101100601=晋城
101100602=沁水
101100603=阳城
101100604=陵川
101100605=高平
101100701=临汾
101100702=曲沃
101100703=永和
101100704=隰县
101100705=大宁
101100706=吉县
101100707=襄汾
101100708=蒲县
101100709=汾西
101100710=洪洞
101100711=霍州
101100712=乡宁
101100713=翼城
101100714=侯马
101100715=浮山
101100716=安泽
101100717=古县
101100801=运城
101100802=临猗
101100803=稷山
101100804=万荣
101100805=河津
101100806=新绛
101100807=绛县
101100808=闻喜
101100809=垣曲
101100810=永济
101100811=芮城
101100812=夏县
101100813=平陆
101100901=朔州
101100902=平鲁
101100903=山阴
101100904=右玉
101100905=应县
101100906=怀仁
101101001=忻州
101101002=定襄
101101003=五台县豆村
101101004=河曲
101101005=偏关
101101006=神池
101101007=宁武
101101008=代县
101101009=繁峙
101101010=五台山
101101011=保德
101101012=静乐
101101013=岢岚
101101014=五寨
101101015=原平
101101100=吕梁
101101101=离石
101101102=临县
101101103=兴县
101101104=岚县
101101105=柳林
101101106=石楼
101101107=方山
101101108=交口
101101109=中阳
101101110=孝义
101101111=汾阳
101101112=文水
101101113=交城

101110101=西安
101110102=长安
101110103=临潼
101110104=蓝田
101110105=周至
101110106=户县
101110107=高陵
101110108=杨凌
101110200=咸阳
101110201=三原
101110202=礼泉
101110203=永寿
101110204=淳化
101110205=泾阳
101110206=武功
101110207=乾县
101110208=彬县
101110209=长武
101110210=旬邑
101110211=兴平
101110300=延安
101110301=延长
101110302=延川
101110303=子长
101110304=宜川
101110305=富县
101110306=志丹
101110307=安塞
101110308=甘泉
101110309=洛川
101110310=黄陵
101110311=黄龙
101110312=吴起
101110401=榆林
101110402=府谷
101110403=神木
101110404=佳县
101110405=定边
101110406=靖边
101110407=横山
101110408=米脂
101110409=子洲
101110410=绥德
101110411=吴堡
101110412=清涧
101110501=渭南
101110502=华县
101110503=潼关
101110504=大荔
101110505=白水
101110506=富平
101110507=蒲城
101110508=澄城

appcan 一种很方便的真机调试方法

这个方法绝对原创,也是自己在实践中总结的,可以避免如题那么的问题的放生。
操作如下:
第一,你在WEB模式里,创建一个http://192.168.0.88/index.html(自己服务器的IP)然后成成APK,步骤如下图:

第二,下载并安装APK到手机上
第三,安装配置IIS,新建一个网站,地址就指向你开发APPCAN的目录
OK了步骤都完成了,日后你就可以在计算机上开发代码,手机上直接运行那个APK直接看效果了,

避免了模拟题的不给力,避免了繁琐的生成安装等步骤。

以上是自己总结出来的东西,希望对开发的人有帮助,还有补充一下,如上操作必须有WIFI做支持,你懂的。

AppCan IDE 中常用的几个js

zy_anim.js
->function zy_anim_slide(p1,p2,t,cb);

说明:在同一个html中多个page之间切换的动画效果。由于AppCan的框架允许一个html中放多个page,每个page是一个div,可以实现在不刷新html的情况下,实现多个页面的切换展示。
p1:为当前显示page的id,
p2:为需要切换到的page的id,
t:为切换效果,枚举值为:slide_left,slide_right,slide_up,slide_down
cb:切换完成后的回调方法

zy_control.js
->function zy_fix(header,footer,run,cb)

说明:固定header区域和footer区域。如果你的界面布局是有header,content,footer区域,而且需要content区域具有 滚动条。那么Appcan提供方案是在当前窗口(window)中uexWindow.openSlibing两个辅窗口,分别作为header和 footer。这样,就能够固定住header和footer区域,避免在有些不支持position:fixed css属性的手机上,footer区域会跟中content区域滚动条一起滚动。
header:为header区域div的id
footer:为footer区域div的id
run:初始化完后需要再次调用uexWindow.Onload的话,run为“真”值,否则为“伪”值。
cb:为初始化完后的回调方法

->function zy_parse()
说明:解析url中“?”后的参数,以key-value形式存放到params对象中。

zy_tmpl.js
说明:zy_tmpl.js提供了一套模板,在跨域访问后获取回来的数据,需要迭代出html代码,然后赋值给某一div或ul等(常见的如获取新闻列表,返回json数据,然后解析给一个ul下)。

使用示例:
比如DEV文档中新浪微博的例子:
var tmpl='<div><div >${user.name}</div><div class=”ui-li-desc ui-smaller ui-float-right” >${cb:created_at}</div><p></p><h3 class=”ui-li-heading” style=”white-space:normal”>${text}</h3>${cb:thumbnail_pic}<div onclick=”event.preventDefault();” >来自${source}</div><div >转发${reposts_count}</div><div class=”ui-li-desc ui-smaller ui-float-right” >|</div><div class=”ui-li-desc ui-smaller ui-float-right” >收藏${favourites_count}</div><div >|</div><div >评论${comments_count}</div></div><p></p>’
return zy_tmpl_s(tmpl,d.retweeted_status,j2vCb);
JSON数据示例:
{
“statuses”: [
{
“created_at”: “Tue May 31 17:46:55 +0800 2011”,
“id”: 11488058246,
“text”: “求关注。”,
“source”: “<a href=”http://weibo.com” rel=”nofollow”>新浪微博</a>”,
“favorited”: false,
“truncated”: false,
“in_reply_to_status_id”: “”,
“in_reply_to_user_id”: “”,
“in_reply_to_screen_name”: “”,
“geo”: null,
“mid”: “5612814510546515491”,
“reposts_count”: 8,
“comments_count”: 9,
“annotations”: [],
“user”: {
“id”: 1404376560,
“screen_name”: “zaku”,
“name”: “zaku”,
“province”: “11”,
“city”: “5”,
“location”: “北京 朝阳区”,
“description”: “人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。”,
“url”: “http://blog.sina.com.cn/zaku”,
“profile_image_url”: “http://tp1.sinaimg.cn/1404376560/50/0/1”,
“domain”: “zaku”,
“gender”: “m”,
“followers_count”: 1204,
“friends_count”: 447,
“statuses_count”: 2908,
“favourites_count”: 0,
“created_at”: “Fri Aug 28 00:00:00 +0800 2009”,
“following”: false,
“allow_all_act_msg”: false,
“remark”: “”,
“geo_enabled”: true,
“verified”: false,
“allow_all_comment”: true,
“avatar_large”: “http://tp1.sinaimg.cn/1404376560/180/0/1”,
“verified_reason”: “”,
“follow_me”: false,
“online_status”: 0,
“bi_followers_count”: 215
}
},

],
“previous_cursor”: 0,
“next_cursor”: 11488013766,
“total_number”: 81655
}

返回的json数据,会以tmpl为模板,每个tmpl都是一条微博内容的html结构。调用zy_tmpl_s,会把d.retweeted_status里的json数据去替换${user.name}等以“$”开头,”{}”包围的区域。

用法说明:

${first: name}:碰到对象数据(如果多条)第一条数据的时,用name代替,其中name可以为字符串,也可以为对象数据的某一对象”name”。

${last:name}:碰到对象数据(如果多条)最后一条数据的时,用name代替,其中name可以为字符串,也可以为对象数据的某一对象”name”。

${index:}:返回对象数据(如果多条)当前迭代的索引值,从0开始。

${cb:name}:以content内容作为参数,去调用cb回调方法。

${name}:以对象数据里属性name对于的value值代替

->zy_tmpl(t,dd,l,cb)

说明:迭代多条数据,返回迭代好的html代码
t:为用户自定义模板
dd:为迭代数据,json对象或其它对象
l:为dd数据(多条数据情况下)的长度
cb:碰到${cb:name}时用来回调的方法

->zy_tmpl_s(t,dd,cb)

说明:迭代单条数据,返回迭代好的html代码
t:为用户自定义模板
dd:为迭代数据,json对象或其它对象
cb:碰到${cb:name}时用来回调的方法

zy_json.js
->$.getJSON(a,b[,c,d])

说明:跨域访问请求方法,当为本地html访问网络数据时,调用uexXmlHttpMgr;当为网络环境调用网络环境时,如果是跨域请求使用JSONP 方式调用,如果非跨域请求,使用ajax方式调用;当为本地html访问本地文件时,使用uexFileMgr读取数据内容。
a:为访问url
b:为成功回调方法
c:为返回数据格式,当为”text”时,返回字符串数据,当为”json”,返回json对象数据。默认为”json”
d:失败的回调方法

Google Guice使用入门

本文通过范例简单地介绍Google Guice的使用,通过下面的范例我们可以知道,Google Guice的使用非常简单。

Google Guice需要使用JDK1.5以上java环境。

下载Google Guice之后,有以下几个文件:

Java代码
  1. aopalliance.jar
  2. guice-1.0.jar
  3. guice-servlet-1.0.jar
  4. guice-spring-1.0.jar
  5. guice-struts2-plugin-1.0.jar

本例只使用到guice-1.0.jar文件,将其加入到class path中。

下面简单地介绍范例:

范例1:使用com.google.inject.Module接口实现类
 

文件名
说明
HelloGuice.java
业务逻辑接口定义文件
HelloGuiceImpl.java
业务逻辑接口实现文件
HelloGuiceModule.java
该文件必须实现com.google.inject.Module接口
TestGuice.java
测试文件

HelloGuice.java

Java代码
  1. package cn.jcourse.guice;
  2. /**
  3.  * @author zhangtao
  4.  * HelloGuice接口,用于表达问候
  5.  */
  6. public interface HelloGuice {
  7.     public void sayHello();
  8. }

上面接口中,我们定义了一个方法,sayHello,用于向用户问候。这里我只是做演示用,在实际的业务中,业务逻辑很可能不是这么简单。

HelloGuiceImpl.java

Java代码
  1. package cn.jcourse.guice.impl;
  2. import cn.jcourse.guice.HelloGuice;
  3. /**
  4.  * @author zhangtao
  5.  * HellGuice实现类
  6.  */
  7. public class HelloGuiceImpl implements HelloGuice {
  8.     /* (non-Javadoc)
  9.      * @see cn.jcourse.guice.HelloGuice#sayHello()
  10.      */
  11.     public void sayHello() {
  12.         System.out.println(“Hello Guice!”);
  13.     }
  14. }

该类是HelloGuice接口的实现类,这里我们仅仅是向控制台输出了一个Hello Guice!字符串。

HelloGuiceModule.java

Java代码
  1. package cn.jcourse.guice;
  2. import cn.jcourse.guice.impl.HelloGuiceImpl;
  3. import com.google.inject.Binder;
  4. import com.google.inject.Module;
  5. /**
  6.  * @author zhangtao
  7.  * HelloGuice模块
  8.  */
  9. public class HelloGuiceModule implements Module {
  10.     /*
  11.      * (non-Javadoc)
  12.      *
  13.      * @see com.google.inject.Module#configure(com.google.inject.Binder)
  14.      */
  15.     public void configure(Binder binder) {
  16.         binder.bind(HelloGuice.class).to(HelloGuiceImpl.class);
  17.     }
  18. }

上面的代码用于告知Guice将接口和实现类绑定。

TestGuice.java

Java代码
  1. package cn.jcourse.guice.test;
  2. import cn.jcourse.guice.HelloGuice;
  3. import cn.jcourse.guice.HelloGuiceModule;
  4. import com.google.inject.Guice;
  5. import com.google.inject.Injector;
  6. import junit.framework.TestCase;
  7. /**
  8.  * @author zhangtao
  9.  * 测试Guice
  10.  */
  11. public class TestGuice extends TestCase {
  12.     public void testHelloGuice() {
  13.         Injector injector = Guice.createInjector(new HelloGuiceModule());
  14.         HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
  15.         helloGuice.sayHello();
  16.     }
  17. }

上面的代码我们使用JUnit来进行单元测试,这里的代码也相对比较简单。

在编写完上述代码后,我们运行TestGuice类,将会发现它向控制台输出了Hello Guice!。

范例2:使用Java Annotation

范例1中,我们自己手工的去配置了绑定关系,当然我们也可以不用那么做。我们可以直接为HelloGuice加上@ImplementedBy注释,而省略掉对com.google.inject.Module的实现。

HelloGuice.java

Java代码
  1. package cn.jcourse.guice;
  2. import cn.jcourse.guice.impl.HelloGuiceImpl;
  3. import com.google.inject.ImplementedBy;
  4. /**
  5.  * @author zhangtao
  6.  * HelloGuice接口,用于表达问候
  7.  */
  8. @ImplementedBy(HelloGuiceImpl.class)
  9. public interface HelloGuice {
  10.     public void sayHello();
  11. }

这里我们使用了Guice提供的注解,ImplementedBy,表示该接口由HelloGuiceImpl类实现。这样我们就可以不手动的去配置依赖关系。再看看TestGuice.java。

TestGuice.java

Java代码
  1. package cn.jcourse.guice.test;
  2. import junit.framework.TestCase;
  3. import cn.jcourse.guice.HelloGuice;
  4. import com.google.inject.Guice;
  5. import com.google.inject.Injector;
  6. /**
  7.  * @author zhangtao
  8.  * 测试Guice
  9.  */
  10. public class TestGuice extends TestCase {
  11.     public void testHelloGuice() {
  12.         //Injector injector = Guice.createInjector(new HelloGuiceModule());
  13.         Injector injector = Guice.createInjector();
  14.         HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
  15.         helloGuice.sayHello();
  16.     }
  17. }

可以看出,我们不需要自己去new一个Module了,Guice会根据我们提供的注解自己来配置依赖关系。

我们运行例子的时候可以看出,它也输出了Hello Guice!到控制台。

 

 

 

通过 Guice 进行依赖项注入(1)

2009-01-12 作者:

zhangtaolv

本教程是转载教程,目的是让大家了解又一个很强大的依赖注入框架Guice.

Guice 是一个依赖项注入(DI)框架。几年来我一直建议开发人员使用 DI,因为它提高了可维护性、可测试性和灵活性。通过观察工程师对 Guice 的反馈,我发现说服程序员去采用一种新技术的最好方法是使这种技术简单易用。Guice 让 DI 变得很简单,因此 Google 采用了这种方法。我希望本文能帮助您轻松学习 Guice。

Guice 2.0 beta

在写这篇文章时,Guice 开发团队正在奋力编写 Guice 2.0,希望能在 2008 年底之前发布。早期的 beta 发布在 Google 代码下载站点。这是一个好消息,因为 Guice 团队添加了一些新功能,使 Guice 代码的使用和理解变得更简单。beta 版没有最终版中的一些功能,但是 beta 很稳定,质量也很好。事实上,Google 在产品软件中使用的是 beta 版。我建议您使用 beta 版。这篇文章是专门为 Guice 2.0 编写的,介绍了 Guice 的一些新功能,但没有讨论 1.0 中已经废弃的一些功能。Guice 团队向我保证:这里讨论的功能在最终发行版和当前 beta 版中都是一样的。

如果您已经了解了 DI,而且知道为什么要借助一个框架来使用 DI,那么您可以跳到 通过 Guice 进行基本注入 小节。否则,请继续阅读,了解 DI 的好处。

DI 案例

我将以一个例子开始。假设我正在编写一个超级英雄(superhero)应用程序,同时实现一个名为 Frog Man 的 hero(英雄)。清单 1 是相关代码和第一个测试(您一定明白编写单元测试的重要性,这里就不多说了)。

清单 1. 一个基本 hero 及其测试

Java代码
  1. public class FrogMan {
  2.   private FrogMobile vehicle = new FrogMobile();
  3.   public FrogMan() {}
  4.   // crime fighting logic goes here…
  5. }
  6. public class FrogManTest extends TestCase {
  7.  public void testFrogManFightsCrime() {
  8.     FrogMan hero = new FrogMan();
  9.     hero.fightCrime();
  10.     //make some assertions…
  11.   }
  12. }

似乎一切正常,但在运行测试时出现了如清单 2 所示的异常:

清单 2. 依赖项出现问题

Java代码
  1. java.lang.RuntimeException: Refinery startup failure.
  2.   at HeavyWaterRefinery.<init>(HeavyWaterRefinery.java:6)
  3.   at FrogMobile.<init>(FrogMobile.java:5)
  4.   at FrogMan.<init>(FrogMan.java:8)
  5.   at FrogManTest.testFrogManFightsCrime(FrogManTest.java:10)

似乎 FrogMobile 构建了一个 HeavyWaterRefinery,假设我不能在测试中构建其中一个依赖项。当然,我可以在生产环境中实现这一点,但是不能保证能在测试中构建第二个 提炼厂(refinery)。在现实生活中,您不可能提炼出氧化氘,但您可以依赖远程服务器和强大的数据库。原理是一样的:这些依赖项很难启动,交互起来 也很慢,这使得测试比平时更容易失败。

输入 DI

为了避免这个问题,您可以创建一个接口(例如 Vehicle),使 FrogMan 类接受 Vehicle 作为一个构造函数参数,如清单 3 所示:

清单 3. 依赖接口并注入它们

Java代码
  1. public class FrogMan {
  2.   private Vehicle vehicle;
  3.   public FrogMan(Vehicle vehicle) {
  4.     this.vehicle = vehicle;
  5.   }
  6.   // crime fighting logic goes here…
  7. }

这种用法就是 DI 的本质 — 使类通过引用接口而不是构建接口(或使用静态引用)来接受它们的依赖项。清单 4 显示了 DI 如何使测试变得更简单:

清单 4. 测试可以使用 mock 而不是麻烦的依赖项

Java代码
  1. static class MockVehicle implements Vehicle {
  2.   boolean didZoom;
  3.   public String zoom() {
  4.     this.didZoom = true;
  5.     return “Mock Vehicle Zoomed.”;
  6.   }
  7. }
  8. public void testFrogManFightsCrime() {
  9.   MockVehicle mockVehicle = new MockVehicle();
  10.   FrogMan hero = new FrogMan(mockVehicle);
  11.   hero.fightCrime();
  12.   assertTrue(mockVehicle.didZoom);
  13.   // other assertions
  14. }

这个测试使用了一个手动编写的 mock 对象来替换 FrogMobile。DI 不仅在测试中省去了麻烦的 refinery 启动过程,而且使测试不用了解 FrogMobile 具体细节。需要的仅是一个 Vehicle 接口。除了使测试变得更简单之外,DI 还有助于提高代码的总体模块性和可维护性。现在,如果想将 FrogMobile 切换为 FrogBarge,可以不修改 FrogMan。所有 FrogMan 都依赖于 Vehicle 接口。

不过这里有一个陷阱。如果您是第一次阅读 DI,可能会想:“这下好了,现在所有 FrogMan 的调用方 都必须知道 FrogMobile(refinery、refinery 的依赖项,依此类推……)”。但如果是这样,DI 就永远不会这么流行。您可以不增加调用方的负担,而是编写一些工厂 来管理对象及其依赖项的创建。

工厂是存放框架的地方。工厂需要大量冗长重复的代码。工厂往往会让程序员(和读者)很痛苦,他们甚至会嫌它麻烦而放弃编写。Guice 和其他 DI 框架可作为 “超级工厂”,您可以通过配置它们来构建对象。配置 DI 框架比自己编写工厂容易得多。因此,程序员编写的代码大部分是 DI 样式的。测试越多代码就越好,程序员以后也就越省事。

通过 Guice 进行基本注入

我希望在我的介绍之后,您会相信 DI 能为您的设计增加价值,而且使用框架会使工作更轻松。现在让我们从 @Inject 注释和模块开始深入讨论 Guice。

告诉 Guice 给类添加 @Inject

FrogMan 与 Guice 上的 FrogMan 之间的唯一区别是 @Inject。清单 5 显示了 FrogMan 带有注释的构造函数:

清单 5. FrogMan 已经加上 @Inject

Java代码
  1. @Inject
  2. public FrogMan(Vehicle vehicle) {
  3.   this.vehicle = vehicle;
  4. }

一些工程师不喜欢给类添加 @Inject。他们更喜欢一个完全忽略 DI 框架的类。这种说法有一定道理,但是我不大赞同。依赖项的注入会使注释的作用更加明显。@Inject 标记只在您要求 Guice 构建类时才有意义。如果不要求 Guice 构建 FrogMan,这个注释对代码行为就没有任何影响。这个注释恰当地指出了 Guice 将参与构建类。但是,使用它需要源代码级别的访问。如果这个注释带来不便,或者正在使用 Guice 创建无法控制其源代码的对象,那么 Guice 就会用一个替代机制。

告诉 Guice 您需要哪个依赖项

Guice 知道您的 hero 需要一个 Vehicle 后,它需要知道提供什么 Vehicle。清单 6 包含一个 Module:一个特殊的类,用于告诉 Guice 各个接口对应的实现。

清单 6. HeroModule 将 Vehicle 绑定到 FrogMobile

Java代码
  1. public class HeroModule implements Module {
  2.   public void configure(Binder binder) {
  3.     binder.bind(Vehicle.class).to(FrogMobile.class);
  4.   }
  5. }

模块就是一个具有某种单实例对象方法的接口。Guice 传递给模块的 Binder 用于告诉 Guice 您想如何构造对象。绑定程序 API 形成一种 区域特定语言。这种小语言允许您编写表达式代码,比如 bind(X).to(Y).in(Z)。后面将提供更多有关绑定程序作用的例子。每次调用 bind 都会创建一个绑定,Guice 将使用绑定集解析注入请求。

使用 Injector 启动

然后,使用 Injector 类启动 Guice。通常需要尽早在程序中创建注入器。这样 Guice 能够帮助您创建大部分对象。清单 7 包含一个以 Injector 开始的示例 main 程序:

清单 7 使用 Injector 启动应用程序

Java代码
  1. public class Adventure {
  2.   public static void main(String[] args){
  3.     Injector injector = Guice.createInjector(new HeroModule());
  4.     FrogMan hero = injector.getInstance(FrogMan.class);
  5.     hero.fightCrime();
  6.   }
  7. }

为了获取注入器,需要在 Guice 类上调用 createInjector。向 createInjector 传递一个模块列表,用于配置它本身(本例只有一个,但您可以添加一个配置 evildoer 的 VillainModule)。拥有注入器后,使用 getInstance 向它请求对象,传递您想返回的 .class(细心的读者会注意到您不需要告诉 Guice 有关 FrogMan 的信息。如果您请求一个具体类,而它有一个 @Inject 构造函数或公共非参数构造函数的话,Guice 就会创建这个类,而无需调用 bind)。

这是 Guice 构造对象的第一种方式:显式询问。但是,您不会希望在启动例程之外使用这个操作。更好、更简单的方式是让 Guice 注入依赖项、依赖项的依赖项,依此类推(正如谚语所说:“背起地球的海龟站在另一个海龟的背上”)。最初看来,这似乎比较麻烦,但您很快就会习惯这种用 法。例如,清单 8 显示了一个注入了 FuelSource 的 FrogMobile:

清单 8. FrogMobile 接受一个 FuelSource

Java代码
  1. @Inject
  2. public FrogMobile(FuelSource fuelSource){
  3.   this.fuelSource = fuelSource;
  4. }

这意味着,当您检索 FrogMan 时,Guice 会构建一个 FuelSource、一个 FrogMobile,最后是一个 FrogMan。即使应用程序与注入器只交互一次,也是如此。

当然,您并不总是有机会控制应用程序的 main 例程。例如,许多 Web 框架自动构建 “操作”、“模板” 或其他一些初始服务。总是可以找到一个地方插入 Guice,不管是使用该框架的一个插件,还是使用一些自己手动编写的代码(例如,Guice 项目发布了一个 Struts 2 插件,它允许 Guice 配置您的 Strut 操作)。

 

 

通过 Guice 进行依赖项注入(2)

2009-01-12 作者:

zhangtaolv

其他注入形式

到目前为止,我展示了 @Inject 应用于构造函数的用法。当 Guice 找到注释时,它会挑选构造函数参数,并试图为每个参数找到一个配置绑定。这称为 构造函数注入。根据 Guice 的最佳实践指南,构造函数注入是询问依赖项的首选方式。但这不是唯一的方式。清单 9 显示了配置 FrogMan 类的另一种方式:

清单 9. 方法注入

Java代码
  1. public class FrogMan{
  2.   private Vehicle vehicle;
  3.   @Inject
  4.   public void setVehicle(Vehicle vehicle) {
  5.     this.vehicle = vehicle;
  6.   }
  7. //etc. …

注意,我没有使用注入的构造函数,而是改用一个带有 @Inject 标记的方法。Guice 会在构造好 hero 之后立即调用此方法。Spring 框架的忠实用户可以将此方法视为 “setter 注入”。不过,Guice 只关心 @Inject;您可以任意命名这个方法,它可以带有多个参数。此方法可以是包保护的,也可以是私有方法。

如果您认为 Guice 访问私有方法不是很好,可以参见清单 10,其中 FrogMan 使用了字段注入:

清单 10. 字段注入

Java代码
  1. public class FrogMan {
  2.   @Inject private Vehicle vehicle;
  3.   public FrogMan(){}
  4. //etc. …

同样,所有 Guice 都只关心 @Inject 注释。字段注入查找注释的所有字段并试图注入相应的依赖项。

哪种方法最好

三个 FrogMan 版本都展示了相同的行为:Guice 在构建时注入相应的 Vehicle。不过,像 Guice 的作者一样,我更喜欢构造函数注入。下面简单分析这三种方式:

  • 构造函数注入 很简单。因为 Java 技术能保证构造函数调用,您不用担心出现未初始化的对象 — 不管是不是由 Guice 创建的。您还可以将字段标记为 final。
  • 字段注入 会影响可测试性,特别是将字段标记为 private 时。这破坏了使用 DI 的主要目的。应该尽量少使用字段注入。
  • 方法注入 在您不控制类的实例化时很有用。如果您有一个需要某些依赖项的超类,也可以使用方法注入(构造函数注入会使这种情况变得很复杂)。

选择实现

现在,假设应用程序中有多个 Vehicle。一样英勇的 Weasel Girl 无法驾驭 FrogMobile!同时,您不想在 WeaselCopter 上硬编码依赖项。清单 11 显示了 Weasel Girl 请求一种更快的传输模式:

清单 11. 使用注释请求某种特定的实现

Java代码
  1. @Inject
  2. public WeaselGirl(@Fast Vehicle vehicle) {
  3.   this.vehicle = vehicle;
  4. }

在清单 12 中,HeroModule 使用绑定函数告诉 Guice WeaselCopter 是 “很快” 的:

清单 12. 告诉 Guice Module 中的相关注释

Java代码
  1. public class HeroModule implements Module {
  2.  public void configure(Binder binder) {
  3.     binder.bind(Vehicle.class).to(FrogMobile.class);
  4.     binder.bind(Vehicle.class).annotatedWith(Fast.class).to(WeaselCopter.class);
  5.   }
  6. }

注意,我选择了一个注释,描述我想以抽象形式描述的工具种类(@Fast),而不是与实现太接近的注释(@WeaselCopter)。如果您使用 的注释将想要的实现描述得太精确,就让读者觉得创建一个隐式依赖项。如果使用 @WeaselCopter,而且 Weasel Girl 借用了 Wombat Rocket,就会对程序员阅读和调试代码造成混淆。

要创建 @Fast 注释,需要复制清单 13 中的模板:

清单 13. 复制粘贴这段代码以创建一个绑定注释

Java代码
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ElementType.FIELD, ElementType.PARAMETER})
  3. @BindingAnnotation
  4. public @interface Fast {}

如果您编写了大量 BindingAnnotations,就会得到许多这样的小文件,每个文件只是注释名称不同。如果您觉得这很繁琐,或者需要执行快速的原型设计,可以考 虑 Guice 的内置 @Named 注释,它接受一个字符串属性。清单 14 展示了这种替代方法:

清单 14. 使用 @Named 代替自定义注释

Java代码
  1. // in WeaselGirl
  2. @Inject
  3. public WeaselGirl(@Named(“Fast”) Vehicle vehicle) {
  4.   //…
  5. }
  6. // in HeroModule
  7. binder.bind(Vehicle.class)
  8.   .annotatedWith(Names.named(“Fast”)).to(WeaselCopter.class);

这种方法是可行的,但由于名称只在字符串内有效,所以这不能利用编译时检查和自动补齐。总的来说,我更愿意自己编写注释。

如果您根本不想使用注释,怎么办?即使添加 @Fast 或 @Named(“Fast”) 都会使类在某种程度上影响配置本身。如果想知道如何解决这个问题,请接着阅读。

provider 方法

如果每次探险都派遣 Frog Man,您可能会厌烦。您喜欢在每个场景中出现的 hero 是随机的。但是,Guice 的默认绑定程序 API 不允许出现 “每次调用时将 Hero 类绑定到一个不同的实现” 这样的调用。不过,您可以 告诉 Guice 使用一种特殊的方法来创建每个新的 Hero。清单 15 显示了将一个新方法添加到 HeroModule 中,并用特殊的 @Provides 注释进行注释:

清单 15. 使用 provider 编写自定义创建逻辑

Java代码
  1. @Provides
  2. private Hero provideHero(FrogMan frogMan, WeaselGirl weaselGirl) {
  3.   if (Math.random() > .5) {
  4.     return frogMan;
  5.   }
  6.   return weaselGirl;
  7. }

Guice 会自动发现具有 @Provides 注释的 Module 中的所有方法。根据 Hero 的返回类型,在您请求某个 hero 时,Guice 会进行计算,它应该调用 provider 方法来提供 hero。您可以为 provider 方法添加逻辑以构建对象并在缓存中查询它,或者通过其他方式获得它。provider 方法是将其他库集成到 Guice 模块中的很好方式。它们也是从 Guice 2.0 开始提供的新方法(Guice 1.0 中只编写自定义 provider 类,这比较乏味,而且更加繁琐。如果您已经决定使用 Guice 1.0,用户指南中有这种旧方法的文档,而且在本文随附的 示例代码 中,您可以找到一个自定义 provider)。

在清单 15 中,Guice 自动使用正确的参数注入 provider 方法。这意味着 Guice 将从它的绑定列表中找到 WeaselGirl 和 FrogMan,您无需在 provider 方法中手动构建它们。这演示了 “海龟背地球” 原则(海龟背地球,哪海龟下面是什么呢?是由另一只海龟背着,如此反复)。您依靠 Guice 来提供依赖项,即使是配置 Guice 模块本身。

请求一个 Provider 而不是一个依赖项

假设一个故事(Saga)中要有多个 hero。如果要求 Guice 注入一个 Hero,只会得到一个 hero。但如果您请求一个 “hero provider”,就可以根据需要创建任意多的 hero,如清单 17 所示:

清单 17. 注入 provider 来控制实例化

Java代码
  1. public class Saga {
  2.   private final Provider<Hero> heroProvider;
  3.   @Inject
  4.   public Saga(Provider<Hero> heroProvider) {
  5.     this.heroProvider = heroProvider;
  6.   }
  7.   public void start() throws IOException {
  8.     for (int i = 0; i < 3; i++) {
  9.       Hero hero = heroProvider.get();
  10.       hero.fightCrime();
  11.     }
  12.   }
  13. }

提供者也可以推迟英雄的出场时间,直到传奇真正开始。如果英雄依赖于时间敏感或上下文敏感的数据,这就会很方便。

Provider 接口有一个方法:get<T>。要访问提供的对象,调用这个方法即可。每次有没有获取新对象以及对象如何配置取决于 Guice 是如何配置的(参阅下面的 作用域 部分,了解单实例对象和其他长生命周期对象的详细信息)。在本例中,Guice 使用 @Provides 方法,因为它是构建新 Hero 的注册方式。这意味着该传奇应该由任意三位英雄混合而成。

不要把提供者与 provider 方法弄混淆了(在 Guice 1.0,这两者更难区分开来)。尽管该 Saga 是从自定义 @Provides 方法中获得它的英雄,但您可以请求任意 Guice 实例化依赖项的一个 Provider。如果需要,可以根据清单 18 重新编写 FrogMan 的构造函数:

清单 18. 请求 Provider 而不是依赖项

Java代码
  1. @Inject
  2. public FrogMan(Provider<Vehicle> vehicleProvider) {
  3.   this.vehicle = vehicleProvider.get();
  4. }

(注意您完全不用更改这个模块代码)。重新编写没有任何作用;只是说明您总是可以请求 Provider,而不用直接请求依赖项。

作用域

默认情况下,Guice 为每个请求的依赖项创建一个新实例。如果对象是轻量级的,这个策略可以很好地工作。但是,如果有一个创建开销很大的依赖项,就可能需要在几台客户机之间共 享实例。在清单 19 中,HeroModule 将 HeavyWaterRefinery 作为一个单实例对象绑定:

清单 19. 将 HeavyWaterRefinery 绑定为一个单实例对象

Java代码
  1. public class HeroModule implements Module {
  2.   public void configure(Binder binder) {
  3.     //…
  4.     binder.bind(FuelSource.class)
  5.       .to(HeavyWaterRefinery.class).in(Scopes.SINGLETON);
  6.   }
  7. }

这意味着 Guice 会一直保持 “提炼厂” 可用,只要另一个实例需要燃料源,Guice 就会注入相同 的 “提炼厂”。这避免了在应用程序中启动多个 “提炼厂”。

在选择作用域时,Guice 提供了一个选项。可以使用绑定程序配置它们,或者直接注释依赖项,如清单 20 所示:

清单 20. 改用注释选择作用域

Java代码
  1. @Singleton
  2. public class HeavyWaterRefinery implements FuelSource {…}

Guice 提供了超出范围的 Singleton 作用域,但它允许您定义自己的作用域(如果您愿意)。例如,Guice servlet 包提供了两个其他作用域:Request 和 Session,它们为 servlet 请求和 servlet 会话提供类的一个独特实例。

常量绑定和模块配置

HeavyWaterRefinery 需要一个许可密钥才能启动。Guice 可以绑定常量值和新实例。请查看清单 21:

清单 21. 在模块中绑定常量值

Java代码
  1. public class HeavyWaterRefinery implements FuelSource {
  2.   @Inject
  3.   public HeavyWaterRefinery(@Named(“LicenseKey”) String key) {…}
  4. }
  5. // in HeroModule:
  6. binder.bind(String.class)
  7.   .annotatedWith(Names.named(“LicenseKey”)).toInstance(“QWERTY”);

这里有必要使用绑定注释,否则 Guice 将不能区分不同的 String。

注意,尽管前面不推荐使用,我还是选择使用 @Named 注释。因为我想显示清单 22 所示的代码:

清单 22. 使用属性文件配置模块

Java代码
  1. //In HeroModule:
  2. private void loadProperties(Binder binder) {
  3.   InputStream stream =
  4.     HeroModule.class.getResourceAsStream(“/app.properties”);
  5.   Properties appProperties = new Properties();
  6.   try {
  7.     appProperties.load(stream);
  8.     Names.bindProperties(binder, appProperties);
  9.   } catch (IOException e) {
  10.     // This is the preferred way to tell Guice something went wrong
  11.     binder.addError(e);
  12.   }
  13. }
  14. //In the file app.properties:
  15. LicenseKey=QWERTY1234

这段代码使用 Guice Names.bindProperties 实用函数,通过恰当的 @Named 注释将 app.properties 文件中的每个属性与一个常量绑定。这本身就很好,而且还显示了您可以使模块代码更复杂。如果喜欢,可以从数据库或 XML 文件加载绑定信息。模块是纯 Java 代码,这提供了很大的 灵活性。

结束语

Guice 主要概念小结:

  • 使用 @Inject 请求依赖项。
  • 将依赖项与 Module 中的实现绑定。
  • 使用 Injector 引导应用程序。
  • 使用 @Provides 方法增加灵活性。

需要了解的 Guice 知识还很多,但您应该先掌握这篇文章中讨论的内容。我建议下载它,以及本文的 示例代码。当然,您也可以创建自己的示例应用程序,这就更好了。通过示例深入了解概念但又不用考虑生产代码是很有意思的。如果要了解更多 Guice 高级功能(比如面向方面编程支持),建议您访问 参考资料 中的一些链接。

说到生产代码,DI 的一个缺点是它可能感染病毒。注入一个类后,它会导致注入下一个类,依此类推。不过这很好,因为 DI 使代码更好。另一方面,这需要大量重构现有代码。为了使工作易于管理,可以将 Guice Injector 存储在某处并直接调用它。应该将这当作一根临时需要的拐杖,但最后一定可以摆脱它。

Guice 2.0 即将推出。有一些功能我还没有讨论,它可以使模块的配置更简单,而且能支持更大、更复杂的配置方案。

我希望您会考虑将 Guice 添加到工具包中。根据我的经验,DI 对于实现灵活的可测试代码库特别有用。Guice 使 DI 变得简单而有趣。还有什么比容易编写的、灵活的、可测试的代码更好呢?

Google Guice 小试牛刀

Google Guice是一个轻量级Dependency Injection依赖注入框架,能够提供动态注入,即当不知道注射的这个对象注射给谁呢,需要在运行时才能得到的的这个接口的实现,这是Spring DI所不具有的,Spring DI所有配置都是写死的,并且Spring DI在应用程序启动时所有依赖注入关系都会初始好,而Google Guice则可以根据需要进行依赖注入初始化,也就是说只有当需要时,就可以对依赖注入关系进行初始化。

 

引入Google Guice包,从这个网址可以下载http://google-guice.googlecode.com/files/guice-3.0.zip

 

一。CommentDao.java

[java]

package com.template.guice;

/**
* Created by IntelliJ IDEA.
* User: Zhong Gang
* Date: 11-8-2
* Time: 下午9:37
*/
public interface CommentDao {

public void comment(String message);
}
[/java]

二。CommentDaoImpl.java

[java]

package com.template.guice;

/**
* Created by IntelliJ IDEA.
* User: Zhong Gang
* Date: 11-8-2
* Time: 下午9:38
*/
public class CommentDaoImpl implements CommentDao {

public CommentDaoImpl() {
}

@Override
public void comment(String message) {
System.out.print(message);
}
}

[/java]

三。CommentService.java

[java]

package com.template.guice;

/**
* Created by IntelliJ IDEA.
* User: Zhong Gang
* Date: 11-8-2
* Time: 下午9:39
*/
public interface CommentService {

public void comment();
}

[/java]

四。CommentServiceImpl.java

[java]

package com.template.guice;

import com.google.inject.Inject;

/**
* Created by IntelliJ IDEA.
* User: Zhong Gang
* Date: 11-8-2
* Time: 下午9:39
*/
public class CommentServiceImpl implements CommentService {

private CommentDao commentDao;

@Inject
public CommentServiceImpl(CommentDao commentDao) {
this.commentDao = commentDao;
}

@Override
public void comment() {
commentDao.comment("This is a comment message!");
}

public void setCommentDao(CommentDao commentDao) {
this.commentDao = commentDao;
}
}

[/java]

五。CommentModule.java

[java]

package com.template.guice;

import com.google.inject.AbstractModule;

/**
* Created by IntelliJ IDEA.
* User: Zhong Gang
* Date: 11-8-2
* Time: 下午9:46
*/
public class CommentModule extends AbstractModule {

@Override
protected void configure() {
bind(CommentDao.class).to(CommentDaoImpl.class);
}
}

[/java]

六。Main.java

[java]

package com.template.guice;

import com.google.inject.Guice;
import com.google.inject.Injector;

/**
* Created by IntelliJ IDEA.
* User: Zhong Gang
* Date: 11-8-2
* Time: 下午9:55
*/
public class Main {

public static void main(String[] args) {
Injector injector = Guice.createInjector(new CommentModule());
CommentService commentService = injector.getInstance(CommentServiceImpl.class);
commentService.comment();
}
}

[/java]

@Inject表示Guice会隐式调用CommentServiceImpl的构造方法,而CommentModule则表示需要将CommentDao以CommentDaoImpl来注入对象中。

微博看到的一句话很好

一个真正强大的人,不会把太多心思花在取悦,迎合和亲附别人上面。所谓的圈子,资源,都只是衍生品,只有提高自己的内功,修炼好自己,才会有别人来亲附。自己是大海,百川才会来归。一个人只有到了那个层次,才会有相应的圈子,地位的巩固,而不是倒过来的。

你是”穷忙族”吗

【你是”穷忙族”吗?】1、一周工作超过54小时,但是看不到前途;2、一年内未曾加薪;3、三年内未曾升职;4、薪水很低,到月底总是很艰难;5、积蓄少,无力置产;6、工资不低,但花钱很大手笔;7、收入不低,但内心没有安全感。据说以上七项,如果你有两项或以上,那么恭喜,你属于”穷忙族”了!