先来说说Static的用法之后介绍线程安全的相关知识。
类(static)变量
在所有类的实例中共享
可以被标记为public或private
如果被标记为public而没有该类的实例,可以从该类的外部访问。
有时想有一个可以在类的所有实例中共享的变量。比如,这可以用作实例之间交流的基础或追踪已经创建的实例的数 量。
可以用关键字static来标记变量的办法获得这个效果。这样的变量有时被叫做class variable,以便与不共享的成员或实例变量区分开来。
Static变量在某种程度上与其它语言中的全局变量相似。Java编程语言没有这样的全局语言,但static变量是可以从类的任何实例访问的单个变量。
如果static变量没有被标记成private,它可能会被从该类的外部进行访问。要这样做,不需要类的实例,可以通过类名指向它。
类(static)方法
因为static方法不需它所属的类的任何实例就会被调用,因此没有this值。结果是,static方法不能访问与它本身的参数以及static变量分离的任何变量。访问非静态变量的尝试会引起编译错误。
也就是说static 的方法只能访问static的类。
public class Wrong {
int x;
public static void main(String args[]) {
x = 9; // COMPILER ERROR!
}
}
below is right
public class Wrong {
static int x;
public static void main(String args[]) {
x = 9; // COMPILER ERROR!
}
}
注意:
Main()是静态的,因为它必须在任何实例化发生前被顺序地访问,以便应用程序的运行。
静态方法不能被覆盖成非静态。
静态初始化程序
方法程序体中不存在的代码在static block中类可以包含该代码,这是完全有效的。当类被装载时,静态块代码只执行一次。类中不同的静态块按它们在类中出现的顺序被执行。
public class StaticInitDemo {
static int i = 5;
static {
System.out.println("Static code i= "+ i++ );
}
}
public class Test {
public static void main(String args[]) {
System.out.println("Main code: i="
+ StaticInitDemo.i);
}
}
Static code: i=5
Main code: i=6
注意:
1. Static方法和数据的单个(共享)副本是因为类和该类的所有实例而存在。通过一个实例或通过类本身可以访问static成员。
2. 非静态数据只限于实例,只能通过该实例的非静态方法对它进行访问。非静态数据定义对象之间互不相同的特点,非静态方法在它们所作用的非静态数据的基础上对每个对象的行为互不相同。
举一个简单例子说明使用:
一款鞋子的尺码是固定的,但这个型号的颜色可以有不同的,那么定义的时候,我们可以吧这款鞋子尺码定义成static的,颜色值定义成非static的,。颜色根据对象的不同而不同,其行为也根据对象的不同而不同,在它所作用的非静态数据的基础上对不同对象返回不同的颜色。
线程安全:
对于线程安全,我们先看两个例子:
class CheckoutLane
{
public static float GetTotal(Cart cart)
{
float total = 0;
for (int i = 0; i < cart.GroceryItems.Length; i++)
{
total += cart.GroceryItems[i].Price;
Thread.Sleep(100);
}
return total;
}
}
对于上面的代码,你使用多少的线程来控制,都是安全的。因为线程之间,不会共享什么资源,唯一相同的是使用了同一个逻辑。其实这是在破坏线程的前题方面下功夫,出现线程不安全的条件都没有了,那肯定就是线程安全的。
class CheckoutLane
{
static float total;
public static float GetTotal(Cart cart)
{
total = 0;
for (int i = 0; i < cart.GroceryItems.Length; i++)
{
total += cart.GroceryItems[i].Price;
Thread.Sleep(100);
}
return total;
}
}
对于上面的这个例子,不是线程安全的,因为共享了static float total;这个资源,而各个线程都随机都被调用,可以任意修改total这个数据。这个,就正如多个收银员共享柜台,任意执行收银操作一样。
class CheckoutLane
{
static float total;
static object synchLock = new object();
public static float GetTotal(Cart cart)
{
lock(synchLock)
{
total = 0;
for (int i = 0; i < cart.GroceryItems.Length; i++)
{
total += cart.GroceryItems[i].Price;
Thread.Sleep(100);
}
return total;
}
}
}
上面的代码是线程安全的,使用了lock关键字,可以达到一个线程强占资源的效果。这时,synchLock就作为了共享的资源,被某一个线程锁住了。
(第二届 Google 暑期大学生博客分享大赛 - 2011 Android 成长篇)
做过web开发的人应该都知道,在HTML里支持<a>标签在文本里插入一个链接,点击后跳转;并且有<img>标签可以插入图片。Android开发是否也支持呢?带着这个疑问,我们去APIDemos探索一下。OK,在com.example.android.apis.text.link这个类里,官方演示了TextView支持的一些链接,上个图:
看来TextView是支持链接跳转的,不过做Android开发的应该都知道,android的View载体是Activity,能不能支持activity跳转呢,很遗憾,不支持。
不过无所谓,Android很有爱,开源的,理解了原理后我们自己去做,这也是我写本篇文章的主要目的,"授之以鱼,不如授之以渔",希望大家在遇到相似问题时能像我这样去分析源码,然后找出解决办法(或者大家可以提出更好的方法),另外,文中如有不妥的地方,也欢迎大家批评指正。先上效果图:点击左边的链接后跳转到右边。
现在我们开始开发吧!第一步,研究相关的源代码吧。通过跟踪TextView的源码,我们发现TextView支持的链接是由android.text.style.URLSpan这个类实现的,它重写了一个onClick方法:
public void onClick(View widget) { Uri uri = Uri.parse(getURL()); Context context = widget.getContext(); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); context.startActivity(intent); }
大家看到了吧startActivity,多么熟悉的方法。既然它能实现,为什么我们不能呢,答案是可以的。我们接着跟踪代码,可以看到URLSpan其实继承的是android.text.style.ClickableSpan,我们来看一下他的源码:
public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance { /** * Performs the click action associated with this span. */ public abstract void onClick(View widget); /** * Makes the text underlined and in the link color. */ @Override public void updateDrawState(TextPaint ds) { ds.setColor(ds.linkColor); ds.setUnderlineText(true); } }
是不是有点眉目了,我们直接继承这个类,重写他的方法不就可以了吗?大胆假设,小心求证,我们新建一个类:
import android.content.Context; import android.content.Intent; import android.text.TextPaint; import android.text.style.ClickableSpan; import android.view.View; /** * If an object of this type is attached to the text of a TextView with a * movement method of LinkMovementMethod, the affected spans of text can be * selected. If clicked, the {@link #onClick} method will be called. * * @author 张宁 */ public class MyClickableSpan extends ClickableSpan { int color = -1; private Context context; private Intent intent; public MyClickableSpan(Context context, Intent intent) { this(-1, context, intent); } /** * constructor * @param color the link color * @param context * @param intent */ public MyClickableSpan(int color, Context context, Intent intent) { if (color!=-1) { this.color = color; } this.context = context; this.intent = intent; } /** * Performs the click action associated with this span. */ public void onClick(View widget){ context.startActivity(intent); }; /** * Makes the text without underline. */ @Override public void updateDrawState(TextPaint ds) { if (color == -1) { ds.setColor(ds.linkColor); } else { ds.setColor(color); } ds.setUnderlineText(false); } }
在这个类里,我们重写了onClick事件,实现了Activity的跳转,并且去掉了下划线。Ok,第一个目的就达到了,下面我们来看一下如何在TextView里加入表情。
这个就比较复杂了,因为TextView只能在其上下左右方向加入图片,是由Drawables这个类实现的,而我们想要的效果是在中间也可以插入,看来这次TextView插入图片源码帮不了我们了。不过我们可以去android.text这个包里去找别的类,大家可以看到在这个包里有一个Html类,做过web开发的应该可以想到什么吧?在文章开头已经提到了Html的<img>标签可以插入图片,那这个类是否提供这个功能呢?带着这个疑问我们可以进去看看,其中有个接口:
/** * Retrieves images for HTML <img> tags. */ public static interface ImageGetter { /** * This methos is called when the HTML parser encounters an * <img> tag. The <code>source</code> argument is the * string from the "src" attribute; the return value should be * a Drawable representation of the image or <code>null</code> * for a generic replacement image. Make sure you call * setBounds() on your Drawable if it doesn't already have * its bounds set. */ public Drawable getDrawable(String source); }
看到<code>source</code>这个没,熟悉吧,结合URLSpan的用法,我们是否可以配合Spanned实现一个
ImageSpan呢?OK,上代码:
import java.util.Map; import java.util.Set; import android.content.Context; import android.graphics.drawable.Drawable; import android.text.Html; import android.text.Spanned; import android.text.Html.ImageGetter; /** * this is a class which defining a spanned with image * @author 张宁 * */ public class ImageSpan { /** * the map of face. */ private Map<String, String> faceMap; private Context context; public ImageSpan(Context context, Map<String, String> faceMap){ this.context = context; this.faceMap = faceMap; } /** * get the image by the given key */ private ImageGetter imageGetter = new Html.ImageGetter() { @Override public Drawable getDrawable(String source) { Drawable drawable = null; String sourceName = context.getPackageName() + ":drawable/" + source; int id = context.getResources().getIdentifier(sourceName, null, null); if (id != 0) { drawable = context.getResources().getDrawable(id); if (drawable != null) { drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); } } return drawable; } }; /** * return a {@link Spanned} with image * @param text * @return */ public Spanned getImageSpan(CharSequence text){ String cs = text.toString(); if (faceMap != null) { Set<String> keys = faceMap.keySet(); for (String key : keys) { if (cs.contains(key)) { cs = cs.replace(key, "<img src='" + faceMap.get(key) + "'>"); } } } return Html.fromHtml(cs, imageGetter, null); } }
到目前为止可以说关键代码都已经实现了,但是会有人问,我该如何使用这两个类呢?下面,我们在实现一个工具类来封装这两个类的方法,以方便调用:
import java.util.HashMap; import java.util.List; import java.util.Map; import android.content.Context; import android.content.Intent; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.widget.EditText; import android.widget.TextView; /** * TextView with intent that can redirect to a new activity * * @author 张宁 * */ public class CustomTextView { private static Map<String, String> faceMap; static { faceMap = new HashMap<String, String>(); faceMap.put("[哭]", "face_1"); faceMap.put("[怒]", "face_2"); } /** * make textview a clickable textview<br> * Note: make true the order of textList and intentList are mapped * * @param context * @param textView * @param textList * the text should be set to this textview,not null * @param intentList * the intent map to the text, if the text have no intent mapped * to, please set a null value.Or it will happen some unknown * error.<br> * not null */ public static void setClickableTextView(Context context, TextView textView, List<String> textList, List<Intent> intentList) { if (textList == null || intentList == null) { return; } SpannableStringBuilder builder = new SpannableStringBuilder(); int end = -1, length = -1; int size = textList.size(); Intent intent; for (int i = 0; i < size; i++) { String text = textList.get(i); if (TextUtils.isEmpty(text)) { continue; } builder.append(textList.get(i)); if ((intent = intentList.get(i)) != null) { end = builder.length(); length = textList.get(i).length(); builder.setSpan(getClickableSpan(context, intent), end - length, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } builder.append(" "); } textView.setText(builder); textView.setFocusable(true); textView.setMovementMethod(LinkMovementMethod.getInstance()); } /** * make textview a clickable textview<br> * Note: make true the order of textList and intentList are mapped * @param context * @param textView * @param text * @param intent */ public static void setClickableTextView(Context context, TextView textView, String text, Intent intent) { SpannableStringBuilder builder = new SpannableStringBuilder(text); builder.setSpan(getClickableSpan(context, intent), 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setText(builder); textView.setMovementMethod(LinkMovementMethod.getInstance()); } /** * make TextView a View with image at any index * @param context * @param textView * @param textList */ public static void setImgTextView(Context context, TextView textView, List<String> textList) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < textList.size(); i++) { builder.append(textList.get(i)).append(" "); } setImgTextView(context, textView, builder.toString()); } /** * make TextView a View with image at any index * @param context * @param textView * @param text */ public static void setImgTextView(Context context, TextView textView, String text) { ImageSpan imageSpan = new ImageSpan(context, faceMap); Spanned spanned = imageSpan.getImageSpan(text); textView.setText(spanned); } /** * make EditText a View with image at any index * @param context * @param EditText * @param text */ public static void setImgTextView(Context context, EditText editText, String text) { ImageSpan imageSpan = new ImageSpan(context, faceMap); Spanned spanned = imageSpan.getImageSpan(text); editText.setText(spanned); } /** * return a custom ClickableSpan * * @param context * @param intent * @return */ public static MyClickableSpan getClickableSpan(Context context, Intent intent) { return new MyClickableSpan(context, intent); } /** * make textview a clickable textview with image<br> * Note: make true the order of textList and intentList are mapped * * @param context * not null * @param haveImg * whether this is image in the text,not null * @param textView * not null * @param textList * the text should be set to this textview,not null * @param intentList * the intent map to the text, if the text have no intent mapped * to, please set a null value.Or it will happen some unknown * error.<br> * allow null */ public static void setCustomText(Context context, Boolean haveImg, TextView textView, List<String> textList, List<Intent> intentList) { SpannableStringBuilder builder = new SpannableStringBuilder(); int end = -1, length = -1; if (intentList != null) { int size = textList.size(); Intent intent; for (int i = 0; i < size; i++) { String text = textList.get(i); if (TextUtils.isEmpty(text)) { continue; } builder.append(textList.get(i)); if ((intent = intentList.get(i)) != null) { end = builder.length(); length = textList.get(i).length(); builder.setSpan(getClickableSpan(context, intent), end - length, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } builder.append(" "); } } else { for (String text : textList) { builder.append(text).append(" "); } } if (haveImg) { ImageSpan imageSpan = new ImageSpan(context, faceMap); Spanned spanned = imageSpan.getImageSpan(builder); textView.setText(spanned); } else { textView.setText(builder); } textView.setMovementMethod(LinkMovementMethod.getInstance()); } }
有了这个类,我们就可以方便的实现在TextView中插入Intent和表情了,甚至不用管底层是怎样实现的,也降低了代码的耦合度。但是又回到我写这篇文章的目的:希望大家能得到“渔”而不仅仅是“鱼”。
Ok,任务完成。源码奉上。
注:原创作品,转载请标明出处:
http://zhangning290.iteye.com/blog/1134286
1 for + Alt+/
/** * 根据appPageList包装page展示列表 * * @param appPageList */ protected void buildAppPageList(int userId, AppPageResult appPageResult) { int size = appPageResult.getCount(); if (appPageResult != null && size > 0) { List<Integer> pageIdList = new ArrayList<Integer>(); for (AppPage appPage : appPageResult.getAppPageList()) { pageIdList.add(appPage.getId()); } List<UserPlayFlowResult> friendIdList = null; if(!CollectionUtils.isEmpty(pageIdList)){ friendIdList = userPlayService.getFriendPlayByPageIds(userId, pageIdList, EXPIRED_DAYS); } if(!CollectionUtils.isEmpty(friendIdList)){ for (AppPage appPage : appPageResult.getAppPageList()) { int pageId = appPage.getId(); for (Iterator<UserPlayFlowResult> iterator = friendIdList.iterator(); iterator.hasNext();) { UserPlayFlowResult userPlayFlowResult = (UserPlayFlowResult) iterator .next(); if(pageId == userPlayFlowResult.getPageId()){ int friendCount = userPlayFlowResult.getFriends().size(); if (friendCount > 0) { String friends = getFriendsNameAndCount(userPlayFlowResult.getFriends().subList(0, 2), friendCount); appPage.setFriends(friends); } iterator.remove(); } } } } } }
2、
sort(appPageList, new ComparatorAppPage() { @Override public int compare(AppPage page0, AppPage page1) { int compareOrder = 0; //如展示顺序相同则比较更新时间 if (page0.getAddTime() != null && page1.getAddTime() != null) { compareOrder = page0.getAddTime().compareTo(page1.getAddTime()); if (compareOrder == 0) { return compare(page0.getDisplayOrder(), page1.getDisplayOrder()); } else { return compareOrder; } } else { return -1; } } });
3、
List<Integer> pageIdList = new ArrayList<Integer>(); for (AppPage appPage : appPageResult.getAppPageList()) { pageIdList.add(appPage.getId()); } List<UserPlayFlowResult> friendIdList = null; if(!CollectionUtils.isEmpty(pageIdList)){ friendIdList = userPlayService.getFriendPlayByPageIds(userId, pageIdList, EXPIRED_DAYS); }