GVKun编程网logo

营销卖货不得不知的3个赠品促销手法!(营销卖货不得不知的3个赠品促销手法是什么)

13

本文将分享营销卖货不得不知的3个赠品促销手法!的详细内容,并且还将对营销卖货不得不知的3个赠品促销手法是什么进行详尽解释,此外,我们还将为大家带来关于Android开发不得不知的使用技巧、IT人不得不

本文将分享营销卖货不得不知的3个赠品促销手法!的详细内容,并且还将对营销卖货不得不知的3个赠品促销手法是什么进行详尽解释,此外,我们还将为大家带来关于Android 开发 不得不知的使用技巧、IT 人不得不知的 linux 服务器搭建方法、Java Thread 你不得不知的那些事、Java:不得不知的Object类的相关知识,希望对你有所帮助。

本文目录一览:

营销卖货不得不知的3个赠品促销手法!(营销卖货不得不知的3个赠品促销手法是什么)

营销卖货不得不知的3个赠品促销手法!(营销卖货不得不知的3个赠品促销手法是什么)

适用性是指连结“礼品性”的根源上尽量保障其适用性,正在“适用性”的根源上又尽能够的保有其情趣性/情调。

① 要有操纵场景,才力创造“送礼人-礼品-收礼人”之间的干系。操纵场景除了本人真的派上用场以外,还囊括对他人的展现场景。

人与人之间咀嚼审美不同能够比人和狗都大,但总归有极少差异不大的需求,正在这上面你就可能找到极少“起码不会惹起反感的礼品”,譬喻9999元的红包、iphone xs max(此处仅举例,杠精绕途)。

试念一下,收到礼品总得发个伴侣圈展现下本人的魅力吧?总得发给闺蜜伴侣分享(炫耀)一下吧?送的东西拿不拿得开始,间接决计了你能不行正在对方的伴侣圈C位出道。

卑鄙一点说即是,你得有行动一个男生的自发,“我即是以为你美观,念要和你正在一道,除了你别人毛线都不算”!

举个例子,普通再男人的女生正在收到口红的功夫也会涂上,而且不由得设念本人涂完今后美美的、被人留心的花式。

从性别感的角度来看,性别颜色强的礼品本来创作了一个两性相处的场景,一忽儿就把两私人从平常的往来边界,拉到了“我和你”“男和女”的二人天下中。

而正在对的所在,才越有能够成为对的人。正在不适当的场景,对方压根没有没往“正在一道”这个宗旨上念,怎样会有“正在一道”这个结果?

借使你懂得了礼品的“代价感”该怎样操作,那么你就越有能够正在有限的预算内,买到一份让对方如愿以偿的礼品。

既然送女生礼品有迹可循,那么常把“要和消费者道爱情”的商家该怎样行使赠品(礼品)来感动消费者呢?

商家通过赠品擢升商品/办事的代价感,让消费者具有“划算”、“我占了省钱”的感触,缩短消费者的计划时期,低落消费者的计划本钱,从而实现交往。

譬喻,现正在商家正在外卖套餐中时时送极少又low又难喝的低价饮料。这些低价饮料不单不会让消费者以为订单划算,反而会有一种这家店很low的印象,连带着低落对其他单品的采办欲。

己所不欲,勿施于人。既然一经决断要送赠品了,那么就站正在消费者的角度就送点适用的、真正有代价的,而不是一堆本钱低廉的物件的打包组合。

用膳的流程中弄脏是不免的,哪怕再小心谨慎,擦嘴擦手也是要的。而许众商家都只配套餐筷中的一张纸巾,远远不行满意一次用膳所必要的纸巾量的。

除了小菜和汤品这一类的东西,还可能连合商家的极少新品小样,通过免费赠品的方法来实行用户市集考核。既能胀吹新品,又能取得用户反应,两全其美。

送出去的赠品必定倘使本人拿出去不跌面儿的产物。质地然而合,给人感触即是low,回过头来还容易带累首要产物的发售,影响商家的举座地步评分。

赠品德地首要是显露商家的根本由衷,这个质地不单仅囊括物质产物的根本质地,还囊括了虚拟商品的由衷。

对,这里说的即是百般必要动用终身数学天禀才力满意前提的优惠券!要送就送点有由衷的!舍不得孩子套不着狼!

总而言之,站正在消费者的角度去斟酌,对他而言真正有代价的东西是什么,而正在你有限的资源内,你能众大水平满意消费者“占省钱”的需求。

借使本钱cover不住,可能先放弃满意一切消费者的念法,转而体贴极少老顾客(少数人)的需求。

看到这里,借使你照旧抱着“就一个赠品云尔,搞这么大,商家不是亏大发了”的主张,那只可说你真的没有体会赠人品动促销伎俩的道理。

Android 开发 不得不知的使用技巧

Android 开发 不得不知的使用技巧

转载至:https://github.com/jiang111/awesome-android-tips

 

值得收藏的 AS 插件 ->_->:https://github.com/jiang111/awesome-androidstudio-plugins

这里收集了大家常用的一些 Android 代码,持续更新中,内容来自自己的平时积累和网络上看到的文章,部分原文地址在最下方。如有错误欢迎指正,如有侵权,请联系我删除。里面可能会有重复内容,请忽略或者提醒我删除。
 

Table of Contents

  • setBackgroundResource (0) 可以移除 View 的背景色

  • Resources.getSystem ().getDisplayMetrics ().density 可以不用 Context 也能获取屏幕密度哦

  • 通过重载 ViewGroup 的 dispatchDraw 可以实现一个简单的蒙版效果。 例如下拉刷新时,可以在 contentView 上加一层遮罩。 canvas.drawRect (0, mContentView.getTranslationY (), getWidth (), getHeight (), mMaskPaint);

  • new 出来的 View 可以用 View.generateViewId () (API 17 以上可用) 生成 id,系统保证唯一

  • 使用 GridView 时 android:padding 和 android:clipToPadding="false" 配合使用效果更好哦。

  • 在布局文件中,如果只是为了占位,可以用 Space 来取代 View。 最棒的一点是 Space 可以跳过 Draw 这个过程。

  • TypedValue.applyDimension (int unit, float value, DisplayMetrics metrics) 方便 dp, px, sp 之间的转换。

  • Activity.startActivities () 这个方法最直接的理解就是使用 intent 开启多个 Activity

  • TextUtils.isEmpty () 如果传入的 String 为 NULL 或者 Length 为 0 的话就返回 true。

  • Html.fromHtml () 如果你对 Html 熟悉的话,可以很迅速通过这个方法处理一些富文本操作。比如超链接和图文排版等处理。

  • TextView.setError () 设置文本框错误提醒

  • Build.VERSION_CODES 有些时候我们的 app 需要根据不同的 SDK 版本进行执行不同的操作

  • PhoneNumberUtils.convertKeypadLettersToDigits 这个方法简单粗暴,会将输入的字母根据键盘上的映射转换为数字。

  • ArgbEvaluator ArgbEvaluator.evaluate (float fraction, Object startValue, Object endValue); 根据一个起始颜色值和一个结束颜色值以及一个偏移量生成一个新的颜色,分分钟实现类似于微信底部栏滑动颜色渐变。

  • ValueAnimator.reverse () 顺畅的取消动画效果

  • DateUtils.formatDateTime ()) 这个方法可以输出相应格式化的时间或者日期

  • Pair 这个类 可以用来存储存储一” 组” 数据。但不是 key 和 value 的关系。

  • SparseArray 目前有很多地方从性能优化方说使用 SparseArray 来替换 hashMap,来节省内存,提高性能。

  • Linkify.addLinks () 这个类可以更方便的为文本添加超链接。

  • android.media.ThumbnailUtils 这个类主要是用来处理缩略图相关的工作,比如:用来获取媒体(图片、视频)的缩略图;

  • Bitmap.extractAlpha (); 返回一个新的 Bitmap,capture 原始图片的 alpha 值。有的时候我们需要动态的修改一个元素的背景图片又不希望使用多张图片的时候,通过这个方法,结合 Canvas 和 Paint 可以动态的修改一个纯色 Bitmap 的颜色。

  • 模块间有消息需要传递时,使用 LocalBroadcastManager 替代 Listener 进行模块解耦。除了解耦,这样发送消息和执行消息差一个线程循环,可以减小方法的调用链,我这就碰到一次方法调用链太长导致 StackOverflow 的问题。

  • 静态变量不要直接或者间接引用 Activity、Service 等。这会使用 Activity 以及它所引用的所有对象无法释放,然后,用户操作时间一长,内存就会狂升。

  • Handler 机制有一个特点是不会随着 Activity、Service 的生命周期结束而结束。也就是说,如果你 Post 了一个 Delay 的 Runnable,然后在 Runnable 执行之前退出了 Activity,Runnable 到时间之后还是要执行的。如果 Runnable 里面包含更新 View 的操作,程序崩溃了。

  • 不少人在子线程中更新 View 时喜欢使用 Context.runOnUiThread,这个方法有个缺点,就是一但 Context 生命周期结束,比如 Activity 已经销毁时,一调用就会崩溃。

  • SharedPreferences.Editor.commit 这个方法是同步的,一直到把数据同步到 Flash 上面之后才会返回,由 IO 操作的不可控,尽量使用 apply 方法代替。apply 只在 API Level>=9 才会支持,需要做兼容。不过,最新的 support v4 包已经为我们做好了处理,使用 SharedPreferencesCompat.EditorCompat.getInstance().apply(editor) 即可。

  • PackageManager.getInstalledPackages 这个方法经常使用,你可能不知道,当获取的结果数量比较多的时候,在某些机型上面调用它花费的时间可能秒级的,所以尽量在子线程中使用。另外,如果结果太多,超过系统设置的 Binder 数据最大传输量的上限,则会发生 TransactionException,如果你使用这个方法获取机器上的己安装应用列表,最好做一下预防。

  • 如果使用 Context.startActivity 启动外部应用,最好做一下异常预防,因为寻找不到对应的应用时,会抛出异常。如果你要打开的是应用内的 Activity,不防使用显式 Intent,这样能提高系统搜索目标 Activity 的效率。

  • Application 的生命周期就是进程的生命周期。只有进程被干掉时,Application 才会销毁。哪怕是没有 Activity、Service 在运行,Application 也会存在。所以,为了减少内存压力,尽量不要在 Application 里面引用大对象、Context 等。

  • getWindow ().setLayout (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 设置全屏方法一定要在 setContentView 之后

  • viewpager 的 setCurrentItem 一定要在 setAdapter 方法之后调用才会有效果.

  • 判断手机是不是飞行模式 boolean isEnabled = Settings.System.getInt (context.getContentResolver (), Settings.System.AIRPLANE_MODE_ON, 0) == 1;

  • TabLayout 修改字体的方法 官方的 TabLayout 没有提供修改 TextView size 的方法,可以新建一个 style CustomTabLayoutTextAppearance 继承 TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse ,然后增加 item ,设置 android:textAllCaps 为 true ,再设置 android:textSize 为你想设置的大小。 再在 TabLayout 的布局文件里设置 app:tabTextAppearance="@style/CustomTabLayoutTextAppearance" 即可。

  • 遍历 HashMap 的最佳方法

public static void printMap(Map mp) {
    for (Map.Entry m : mp.entrySet()) {
            System.out.println(m.getKey() + ":" + m.getValue());
        }
}
  • 使用 Java 在一个区间内产生随机整数数
public static int randInt(int min, int max) {
    Random rand = new Random();
    int randomNum = rand.nextInt((max - min) + 1) + min;
    return randomNum;
}
  • 如果子类实现 Serializable 接口而父类未实现时,父类不会被序列化,但此时父类必须有个无参构造方法,否则会抛 InvalidClassException 异常。

  • transient 关键字修饰变量可以限制序列化。

  • 当使用 JakeWharton 的 TabPageIndicator 时,如果需要先做一些耗时的操作,然后再展示 TabPageIndicator 的话,需要先设置 mIndirector.setVisibility (View.GONE); 然后耗时任务结束以后再 mIndirector.setVisibility (View.VISIBLE); 否则会报错

  • 类继承之间的调用顺序 父类 static 成员 -> 子类 static 成员 -> 父类普通成员初始化和初始化块 -> 父类构造方法 -> 子类普通成员初始化和初始化块 -> 子类构造方法

  • 华为手机无法显示 log 解决方案,. 拨号界面输入 (*#*#2846579#*#*) Service menu will appear.Go to "ProjectMenu" -> "Background Setting" -> "Log Setting"Open "Log switch" and set it to ON.Open "Log level setting" and set the log level you wish.

  • 后台 service 经常因为重启之类的出现 onStartCommand () 中的 Intent 传递的参数为 null, 通过在 onStartCommand () 中的返回值改成 return super.onStartCommand (intent, Service.START_REDELIVER_INTENT, startId); 可以解决问题。下面介绍几个 flag 的意思

  • flag 解释
    START_STICKY 如果 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的 intent 对象。随后系统会尝试重新创建 service,由于服务状态为开始状态,所以创建服务后一定会调用 onStartCommand (Intent,int,int) 方法。如果在此期间没有任何启动命令被传递到 service,那么参数 Intent 将为 null。
    START_NOT_STICKY “非粘性的”。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。
    START_REDELIVER_INTENT 重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。
    START_STICKY_COMPATIBILITY START_STICKY 的兼容版本,但不保证服务被 kill 后一定能重启。
  • 不能在 Activity 没有完全显示时显示 PopupWindow 和 Dialog

  • 在多进程之间不要用 SharedPreferences 共享数据,虽然可以(MODE_MULTI_PROCESS),但极不稳定

  • 有些时候不能使用 Application 的 Context,不然会报错(比如启动 Activity,显示 Dialog 等)

* 备注:大家注意看到有一些 NO 上添加了一些数字,其实这些从能力上来说是 YES,但是为什么说是 NO 呢?下面一个一个解释: 1. 数字 1:启动 Activity 在这些类中是可以的,但是需要创建一个新的 task,一般情况不推荐; 2. 数字 2:在这些类中去 layout inflate 是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用; 3. 数字 3:在 Receiver 为 null 时允许,在 4.2 或以上的版本中,用于获取黏性广播的当前值。(可以无视); 4. ContentProvider、BroadcastReceiver 之所以在上述表格中,是因为在其内部方法中都有一个 context 用于使用。

  • 谨慎使用 Android 的透明主题,透明主题会导致很多问题,比如:如果新的 Activity 采用了透明主题,那么当前 Activity 的 onStop 方法不会被调用;在设置为透明主题的 Activity 界面按 Home 键时,可能会导致刷屏不干净的问题;进入主题为透明主题的界面会有明显的延时感
  • 不要在非 UI 线程中初始化 ViewStub,否则会返回 null

  • 尽量不要通过 Application 缓存数据,这不稳定

  • 华为手机无法打开 USB 调试的问题,

  • 插好数据线,拨号界面 输入 ##2846579## 进入工程模式
  • projectmenu→3 后台设置→4USB 端口配置→Balong 调试模式,点确定
  • 不要拔线,退出工程模式,直接重启手机,电脑中显示可移动磁盘 (若仍未出现,重复步骤 1、2)
  • 这个是关闭 USB 调试的情况下电脑中使用手机的可移动磁盘的方法,使用后下拉菜单中 usb 选项也回来了。

  • android listview 中的消息被软键盘遮挡了,在设置 listview 的时候加上 android:transcriptMode="normal" 就好了

  • TextUtils 是一个非常好用的工具类,把 List 转成字符串,逗号分隔,逗号分隔的 String 字符串,切割成 List ,分别可以用 TextUtils 的 join 和 split 方法。如果要对 List 去重,则可以用 Collection 的 frequency 方法。

  • 在 activity 中调用 moveTaskToBack (boolean nonRoot) 方法即可将 activity 退到后台,注意不是 finish () 退出。

  • activity 中的 runOnUiThrea (Runnable action) 方法可以直接回到主线程

  • listview 有个 footerDividersEnabled 和 headerDividersEnabled 方法可以设置 listview 的顶部和底部 divide,但是必须保证你设置了 headview 和 footview 才会有效果

  • Throwable 类中的 getStackTrace () 方法,根据这个方法可以得到函数的逐层调用地址,其返回值为 StackTraceElement [];

  • StackTraceElement 类,其中四个方法 getClassName (),getFileName (),getLineNumber (),getMethodName () 在调试程序打印 Log 时非常有用;

  • UncaughtExceptionHandler 接口,再好的代码异常难免,利用此接口可以对未捕获的异常善后

  • Resources 类中的 getIdentifier (name, defType, defPackage) 方法,根据资源名称获取其 ID,做 UI 时经常用到;

  • view 的 isShown 方法,只有当 view 本身以及它的所有祖先们都是 visible 时,isShown()才返回 TRUE。而平常我们调用 if (view.getVisibility () == View.VISIBLE) 只是对 view 本身而不对祖先的可见性进行判断。

  • Arrays 类中的一系列关于数组操作的工具方法:binarySearch (),asList (),equals (),sort (),toString (),copyOfRange () 等;Collections 类中的一系列关于集合操作的工具方法:sort (),reverse () 等;

  • TextView 类中的 append (CharSequence) 方法,添加文本。一些特殊文本直接用 + 连接会变成 String;

  • System 类中的 arraycopy (src, srcPos, dest, destPos, length) 方法,用来 copy 数组;

  • Fragment 类中的 onHiddenChanged (boolean) 方法,使用 FragmentTransaction 中的 hide (),show () 时只会调用 Fragment 中的 show 和 hidden 状态,其他生命周期不会调用。

  • Activity 类中的 onWindowFocusChanged (boolean),onNewIntent (intent) 等回调方法;

  • TextView 类中的 setTransformationMethod (TransformationMethod) 方法,可用来实现 “显示密码” 功能

  • PageTransformer 接口,用来自定义 ViewPager 页面切换动画,用 setPageTransformer (boolean, PageTransformer) 方法来进行设置;

  • apache 提供的一系列 jar 包:commons-lang.jar,commons-collections.jar,commons-beanutils.jar 等,里面很多方法可能是你曾经用几十几百行代码实现过的,但是执行效率或许要差很多,比如:ArrayUtils,StringUtils……;

  • ActivityLifecycleCallbacks 接口,用于在 Application 类中监听各 Activity 的状态变化 阅读地址点我

  • ActionBar.hide ()/.show () 顾名思义,隐藏和显示 ActionBar,可以优雅地在全屏和带 Actionbar 之间转换。

  • SystemClock.sleep () 这个方法在保证一定时间的 sleep 时很方便,通常我用来进行 debug 和模拟网络延时。

  • UrlQuerySanitizer—— 使用这个工具可以方便对 URL 进行检查。

  • ActivityOptions —— 方便的定义两个 Activity 切换的动画。 使用 ActivityOptionsCompat 可以很好解决旧版本的兼容问题。

  • getParent ().requestDisallowInterceptTouchEvent (true); 剥夺父 view 对 touch 事件的处理权,谁用谁知道。

  • HandlerThread,代替不停 new Thread 开子线程的重复体力写法。

  • IntentService, 一个可以干完活后自己去死且不需要我们去管理子线程的 Service

  • Executors. newSingleThreadExecutor (); 这个是 java 的,之前不知道它,自己花很大功夫去研究了单线程顺序执行的任务队列

  • android:animateLayoutChanges="true",LinearLayout 中添加 View 的动画的办法,支持通过 setLayoutTransition () 自定义动画。

  • AsyncQueryHandler,如果做系统工具类的开发,比如联系人短信辅助工具等,肯定免不了和 ContentProvider 打交道,如果数据量不是很大的情况下,随便搞,如果数据量大的情况下,了解下这个类是很有必要的,需要注意的是,这玩意儿吃异常..

  • ViewFlipper,实现多个 view 的切换 (循环),可自定义动画效果,且可针对单个切换指定动画。

  • android util 包中的 Pair 类,可以方便的用来存储一 "组" 数据。注意不是 key value

  • android:descendantFocusability,ListView 的 item 中 CheckBox 等元素抢焦点导致 item 点击事件无法响应时,除了给对应的元素设置 focusable, 更简单的是在 item 根布局加上 android:descendantFocusability=”blocksDescendants”

  • includeFontPadding="false",TextView 默认上下是有一定的 padding 的,有时候我们可能不需要上下这部分留白,加上它即可。

  • Messenger,面试的时候通常都会被问到进程间通信,一般情况下大家都是开始背书,AIDL 巴拉巴拉。。有一天在鸿神的博客看到这个,嗯,如他所说,又可以装一下了。

  • EditTxt.setImeOptions, 使用 EditText 弹出软键盘时,修改回车键的显示内容 (一直很讨厌用回车键来交互,所以之前一直不知道这玩意儿)

  • java8 中新增的 LocalDate 和 LocalTime 接口,Date 虽然是个万能接口,但是它真的不好用,有了这俩,终于可以愉快的处理日期时间了。

  • WeakHashMap,直接使用 HashMap 有时候会带来内存溢出的风险,使用 WaekHashMap 实例化 Map。当使用者不再有对象引用的时候,WeakHashMap 将自动被移除对应 Key 值的对象。

  • 使用 SnackBar 的时候,不要使用 view.getRootView () 作为 snackbar 的 view, 华为荣耀 7 会出问题。

  • 设置 TextView 单行显示的时候不要用 Lines=1, 而要用 singleLine="true" , 因为魅族部分手机在设置 Lines=1 的时候,然后 TextView 的值全为数字的时候, 你就会懵逼了.

  • TouchDelegate 可用于更改 View 的触摸区域。场景:比如在 RecyclerView 的 ItemView 里包含了 CheckBox 组件,然后想实现点击 ItemView 的时候,也可以触发 CheckBox,就可以使用此类

  • ArgbEvaluator 可用于计算不同颜色值之间的插值,配合 ValueAnimator.ofObject 或者 ViewPager.PageTransformer 使用,可以实现不同颜色之间的平滑过渡。

  • Palette 可用于提取一张图片的颜色。

  • ViewDragHelper, 做过自定义 ViewGroup 的童鞋都应该知道这个东西吧,用来处理触摸事件的神器,妈妈再也不用担心我自定义控件了。

  • PageTransformer 用于定义 ViewPager 页面切换时的动画效果(淡入淡出,放大缩小神马的…)官方有例子,直接看吧。

  • Formatter.formatFileSize () 这个方法会格式化数据的大小,根据输入的字节大小,返回 B KB MB GB 等等(最大支持到 PB)。当然要注意的是输入的最大值是 Long.MAX_VALUE.

  • Activity.recreate 重新创建 Activity。有什么用呢?可以在程序更换主题后,立马刷新当前 Activity,而不会有明显的重启 Activity 的动画。

  • View.getContext 顾名思义,就不用解释了吧… 以前在写 RecyclerView 的 Adapter 的时候,为了使用 LayoutInflater,经常傻乎乎地在构造函数中传入一个外部的 context…. 是不是只有我不知道而已(笑 cry 脸)

  • View.post 方便在非 UI 线程对界面进行修改,与 Handler 的作用类似。并且由于 post 的 Runnable 会保证在该 View 绘制完成的前提下才调用,所以一般也可以用于获取 View 的宽高。

  • Activity.runOnUiThread 与 View.post 类似,方便在非 UI 线程中对界面进行修改。

  • Fragment 在配合 PagerAdapter 使用的时候可以重写 setUserVisibleHintFragment () 方法,然后根据参数的布尔值(true 的话表示当前 Fragment 对用户可见), 来执行一些逻辑。

  • android:animateLayoutChanges 这是一个非常酷炫的属性。在父布局加上 android:animateLayoutChanges="true" 后,如果触发了 layout 方法(比如它的子 View 设置为 GONE),系统就会自动帮你加上布局改变时的动画特效!!

  • android:clipToPadding 设置父 view 是否允许其子 view 在它的 padding(这里指的是父 View 的 padding)中绘制。是不是有点绕?举个实际场景吧:假如有个 ListView,我们想要在初始位置时,第一项 Item 离顶部有 10dp 的距离,就可以在 ListView 的布局中加入 android:clipToPadding="false" android:paddingTop="10dp" 即可。是不是很方便呢?

  • rv 的 Layoutmanager 可以直接申明在 xml 中,具体代码可查看 RecyclerView.createLayoutManager 方法.

  • RecyclerView 在 23.2.+ 的版本中新增了自动测量的功能,由于新增了自动测量,那么它的 item 的根布局在需要测量的方向上就不能写 match_parent 了,需要改成 wrap_content

  • getParent ().requestDisallowInterceptTouchEvent (true); 剥夺父 view 对 touch 事件的处理权,谁用谁知道。

  • Canvas 中 clipRect、clipPath 和 clipRegion 剪切区域的 API。

  • GradientDrawable 有个阴影效果还不错,以为是切的图片,一看代码,什么鬼 = =!

  • 有朋友提到了在自定义 View 时有些方法在开启硬件加速的时候没有效果的问题,在 API16 之后确实有很多方法不支持硬件加速,通常我们关闭硬件加速都是在清单文件中通过,其实 android 也提供了针对特定 View 关闭硬件加速的方法,调用 View.setLayerType (View.LAYER_TYPE_SOFTWARE, null); 即可。

  • PointF,graphics 包中的一个类,我们经常见到在处理 Touch 事件的时候分别定义一个 downX,一个 downY 用来存储一个坐标,如果坐标少还好,如果要记录的坐标过多那代码就不好看了。用 PointF (float x, float y); 来描述一个坐标点会清楚很多。

  • StateListDrawable,定义 Selector 通常的办法都是 xml 文件,但是有的时候我们的图片资源可能是从服务器动态获取的,比如很多 app 所谓的皮肤,这种时候就只能通 StateListDrawable 来完成了,各种 addState 即可。

  • android:duplicateParentState="true",让子 View 跟随其 Parent 的状态,如 pressed 等。常见的使用场景是某些时候一个按钮很小,我们想要扩大其点击区域的时候通常会再给其包裹一层布局,将点击事件写到 Parent 上,这时候如果希望被包裹按钮的点击效果对应的 Selector 继续生效的话,这时候 duplicateParentState 就派上用场了。

  • ViewConfiguration.getScaledTouchSlop (); 触发移动事件的最小距离,自定义 View 处理 touch 事件的时候,有的时候需要判断用户是否真的存在 movie,系统提供了这样的方法。

  • ViewStub,有的时候一块区域需要根据情况显示不同的布局,通常我们都会通过 setVisibility 的方法来显示和隐藏不同的布局,但是这样默认是全部加载的,用 ViewStub 可以更好的提升性能。

  • onTrimMemory,在 Activity 中重写此方法,会在内存紧张的时候回调(支持多个级别),便于我们主动的进行资源释放,避免 OOM。

  • TextView.setCompoundDrawablePadding,代码设置 TextView 的 drawable padding。

  • ImageSwitcher,可以用来做图片切换的一个类,类似于幻灯片。

  • 在自定义控件的时候,能用 drawable 来绘制圆,或者其他样式的时候,尽量用 drawable, 因为 drawable 的效果要远胜于 canvas.drawXXX ().

  • 如果想要自定义 View 支持 SwipeRefreshLayout,只需要声明并实现 ScrollingView 接口即可,RecyclerView 和 NestedScrollView 已经实现此接口。

  • AtomicFile—— 通过使用备份文件进行文件的原子化操作。这个知识点之前我也写过,不过最好还是有出一个官方的版本比较好。

  • DatabaseUtils—— 一个包含各种数据库操作的使用工具。

  • Activity.isChangingConfigurations ()—— 如果在 Activity 中 configuration 会经常改变的话,使用这个方法就可以不用手动做保存状态的工作了。

  • SearchRecentSuggestionsProvider—— 可以创建最近提示效果的 provider,是一个简单快速的方法。

  • android:clipChildren (ViewGroup)—— 如果此属性设置为不可用,那么 ViewGroup 的子 View 在绘制的时候会超出它的范围,在做动画的时候需要用到。

  • android:fillViewport (ScrollView)—— 在这片文章中有详细介绍文章链接,可以解决在 ScrollView 中当内容不足的时候填不满屏幕的问题。

  • android:tileMode (BitmapDrawable)—— 可以指定图片使用重复填充的模式。

  • android:enterFadeDuration/android:exitFadeDuration (Drawables)—— 此属性在 Drawable 具有多种状态的时候,可以定义它展示前的淡入淡出效果。

  • Log.wtf () 的意思是 What a Terrible Failure, 而不是 What The Fuck!

  • 使用 RenderScript 虚化图片效果。如果你的 app 的 minSDK 为 16 或者更低,你需要使用 support 模式,因为很多方法都是在 API 17 之后添加的。renderscriptTargetApi 最高到 23,但是你应该把它设置到能保持脚本中使用到的功能完整的最低 API。如果你想在 support 模式下 target API 21 + 你必须使用 gradle-plugin 2.1.0 和 buildToolsVersion “23.0.3” 或者以上。需要在 gradle 中添加 renderscriptTargetApi 18,renderscriptSupportModeEnabled true 这两句话

public static Bitmap blurBitmap(Context context, Bitmap src, int radius) {
        Bitmap dest = src.copy(src.getConfig(), true);
        RenderScript rs = RenderScript.create(context);
        Allocation allocation = Allocation.createFromBitmap(rs, src);
        Type t = allocation.getType();
        Allocation blurredAllocation = Allocation.createTyped(rs, t);
        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        blurScript.setRadius(radius);
        blurScript.setInput(allocation);
        blurScript.forEach(blurredAllocation);
        blurredAllocation.copyTo(dest);
        allocation.destroy();
        blurredAllocation.destroy();
        blurScript.destroy();
        t.destroy();
        rs.destroy();
        return dest;
    }
  • 如果想把一个 view 保存为 Bitmap,正常情况下用第一种方法就可以了,但是如果是 ScrollView,则必须采用第二种方法。

  • 当 Activity LauncherMode 为 singleTask singleInstance 时,使用 startActivityForResult 会立马返回,不能正常调用。具体请看 http://www.360doc.com/content/15/0123/14/12928831_443085580.shtml

  • 当 PopupWindow 中有 EditText 控件时,因为 Popupwindow 默认没有获取到焦点,需要手动设置焦点,这样子 view 才能获取到事件的监听。所以你需要在创建完 popwindow 后设置他的焦点,popupWindow.setFocusable (true); 就可以让 EditText 获取焦点。

  • PopupWindow 默认点击外部的时候不消失,需要对 PopupWindow 设置一个背景图 popWindow.setBackgroundDrawable (new BitmapDrawable ()); 要创建一个空对象,设置为 null 是不行的,或者就创建一个全透明的背景图。

  • android 中的序列化官方推荐 Parceble, 其实 Parceble 最好用于内存之间数据的交换,如果要把数据写入硬盘的话,推荐实现 Serializable

  • tools 标签可以很好的帮助开发者实时预览 xml 的效果,并且运行以后 tools 标签的内容不会展示出来。例如:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:text="这段话只在预览时能看到,运行以后就看不到了" />
  • android studio 2.1 起已经支持 jdk8 了,使用的时候要在 gradle 中加上,需要把 buildToolsVersion 更新到 24 以上的版本
android {
    defaultConfig {
    ...
            jackOptions {
                enabled true
            }
        }
   ...
    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }
}
  • 6.0 之后 getResources ().getColor () 方法被废弃了,大家可以用 ContextCompat.getColor (context, R.color.color_name) 替换,ContextCompat 是 v4 包里的,请放心使用,另外还有 getDrawable () 等方法

  • 图片的资源文件官方推荐只把 launcher 放在 mipmap 文件夹下面,而 app 用到的资源文件建议放在 drawable 下面。

  • SharedPreference.Editor 的 apply 是异步操作,不会返回成功的状态,而 commit 是同步操作,因此,在多个并发的提交 commit 的时候,他们会等待正在处理的 commit 保存到磁盘后再操作下一个数据,从而降低了效率。

  • 如果你在 manifest 中把一个 activity 设置成 android:windowSoftInputMode="adjustResize",那么 ScrollView(或者其它可伸缩的 ViewGroups)会缩小,从而为软键盘腾出空间。但是,如果你在 activity 的主题中设置了 android:windowFullscreen="true",那么 ScrollView 不会缩小。这是因为该属性强制 ScrollView 全屏显示。然而在主题中设置 android:fitsSystemWindows="false" 也会导致 adjustResize 不起作用

  • 在 Android 4.0 以后,在 Manifest.xml 中静态注册的广播,程序安装后必须启动一次才能接收到广播,比如你的应用监听开机启动的广播,必须要你的程序被运行过才能监听到

  • Activity 的 onDestory 方法调用时机是不确定的(有时候离开界面很久之后才会调用 onDestory 方法),应该避免指望通过 onDestory 方法去释放与 Activity 相关的资源,否则会导致一些随机 bug

  • 2.X 时代 Bitmap 对象虽然存储在堆内存中,但是用了一个 byte 数组存储其像素信息。通过计数器来记录该像素信息被引用的个数。有人认为这个 byte 数组在 native 堆中,但事实上它也在堆中。只有在使用者调用 recycle () 后,Bitmap 对象才会释放像素信息,才会在失去引用后被垃圾回收机制销毁。再加上 DVM 的 heap size 有严格的阀值,所以在使用大量图片资源的时候,及其容易发生 OOM。解决办法一般都是,用一个哈希表存储 Bitmap 对象的软引用,作为内存缓存,并在适当时机掉用其 recycle ()。3.0 以上版本 Bitmap 对象可以通过垃圾回收机制完全销毁,理论上不用再调用 recycle ()。

  • .gitignore 只能忽略那些原来没有被 track 的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore 是无效的。那么解决方法就是先把本地缓存删除(改变成未 track 状态),然后再提交:

git rm -r --cached .
git add .
git commit -m ''update .gitignore''
  • 时间戳请使用 long 或者 String 类型接收,遇到的坑,由于项目中的 model 好多都是通过 GsonFormat 生成的,服务器给的 json 中的时间戳都是 10 位的,导致了 GsonFormat 自动解析成了 int, 当测试人员选择时间为 2100 年的时候时间戳是 4 开头的十位 用 int 类型接收越界了,导致报错

  • 为你的 app 添加默认布局样式,比如:每一个控件都需要写 width 和 height 属性,然而很多的控件的宽高属性都是 wrap_content, 那么我们可以通过在 style 文件添加如下样式:

<style name="Theme.YourApp" parent="android:style/Theme.Light">
    <item name="android:layout_width">wrap_content</item>
    <item name="android:layout_height">wrap_content</item>
</style>

这样,控件的宽高默认都是 wrap_content 样式啦。

  • 在 style 中写的样式通过视同 parent 标签来扩展你的样式,这样更高效,这里官方的建议是,只有 Android 自带的 style 才用 parent 标签,如果是自定义的 style,直接用。符号来连接就行。如 Fill.Height。
<style name="Fill">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">fill_parent</item>
</style>
<style name="Fill.Height" >
    <item name="android:orientation">vertical</item>
</style>
  • Android 上的应用切换按钮列出的其实不是应用而是 Task,所以你会看到有的应用在切换视图里有多个任务。如果你的应用中有逻辑上相互独立的部分,或者想在多窗口环境下并排显示应用的两个不同部分,这种情况就适合多任务了。使用 manifest 属性 (静态) 或者 intent flags (动态) 可以实现这一点,详见视频: http://v.youku.com/v_show/id_XMTU2ODk4NDg2NA==.html?f=26587294

  • 当 app 的 theme 用的是 NoActionBar, 但是在 layout 中仍然用到 toolbar 的时候,不要在 style 文件中加 fitsSystemWindows 属性,而是在用到 toolbar 的 layout 最外层加 fitsSystemWindows, 否则当你使用 EditText, 在小米手机上长按 EditText 调出系统粘贴功能的时候,粘贴的 layout 的布局会错位.

  • 当 WebView 与 ScrollView 嵌套使用,并且 WebView 有字体放大缩小的功能时,当切换 webview 的字体后,webview 的高度并不能很好的计算出来,这时候可以通过注入的方式,让 js 算出高度,经测试,这样是最可靠的,代码地址:http://blog.csdn.net/jys1115/article/details/43525979

  • Context 类中的 createPackageContext (packageName, flags) 方法,可用来获取指定包名应用程序的 Context 对象。

  • TextView 类的 setKeyListener (KeyListener) 方法; 其中 DigitsKeyListener 类,使用 getInstance (String accepted) 方法即可指定 EditText 可输入字符集;

  • View 类中的 getLocationInWindow (int []) 方法和 getLocationOnScreen (int []) 方法,获取 View 在窗口 / 屏幕中的位置;

  • Context.getCacheDir () - 可以获取到 app 默认的缓存路径。

  • StaticLayout 在自定义控件绘制文本的时候很有用。

  • Android 中的四大组件千万不要通过 new 的方式创建出来。

  • 测试 app 的时候,我们大都想要将 debug 和 release 版本同时安装到手机里,可以通过在 gradle 中修改 applicationid 来实现:

android {
    buildTypes {
        debug {
            applicationIdSuffix ''.debug''
            versionNameSuffix ''-DEBUG''
        }
        release {
            //...
        }
    }
}
  • 在大多数的登陆界面中,都提供了用户是否让密码可见的选项,Support Library 24.2.0 提供了官方的实现,TextInputLayout 中添加了 passwordToggleEnabled 属性来开启此功能,并且可以通过 passwordToggleDrawable 设置图标。(摘自:Android 笔记的微博)

  • 同样,在 Support Library 24.2.0 中增加 RecyclerView 在快速滚动时的回调接口,SnapHelper 是官方的一个实现 OnFlingListener 的 一个抽象类,LinearSnapHelper 则是一个完整的实现.LinearSnapHelper 默认实现的功能是类似 ViewPager, 在滚动结束后,会选择列表某一条居中展示 (这里有开始位置展示,或者结束位置显示点我). 例如:

    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
    recyclerView.setLayoutManager(linearLayoutManager);
    LinearSnapHelper snapHelper = new LinearSnapHelper();
    snapHelper.attachToRecyclerView(recyclerView);
  • Android 中推荐使用的数据结构 :
ArrayMap<K,V> in place of HashMap<K,V> 
ArraySet<K,V> in place of HashSet<K,V> 
SparseArray<V> in place of HashMap<Integer,V> 
SparseBooleanArray in place of HashMap<Integer,Boolean> 
SparseIntArray in place of HashMap<Integer,Integer>  
SparseLongArray in place of HashMap<Integer,Long>  
LongSparseArray<V> in place of HashMap<Long,V>
  • 生成 GUID (由于 java 中只能生成 UUID, 所以这里要转换一下):
    return UUID.randomUUID().toString().toUpperCase().replaceAll("-", "");
  • 业务场景:需要定时后台扫描数据库,上传本地照片至云端,定时任务采用何种模式: 1.Handler 或者 Timer 定时一般为秒级别的任务,Timer 会启动额外线程,而 Handler 可以不用。 2. 无论是 Handler 还是 Timer 都需要依赖于进程存活 3. 利用 Handler 实现定时任务的类:HandlerTimer 4. 如果时间较长,则需要使用 AlarmManager 5. 另外,我们对于这种业务应该优先考虑是否可以基于事件通知。 6. 如果是加入媒体库的文件,我们可以使用 registerContentObserver 监听媒体库文件变化。

  • 把 Activity 作为参数传给一个静态方法,会影响这个 Activity 的正常销毁吗?1. 内存泄露与方法是否是静态与否无关,与内部的方法体实现有关系。 2. 内存泄露可以简单理解成:生命周期长的对象不正确持有了持有了生命周期短的对象,导致生命周期短的对象无法回收。 3. 比如 Activity 实例被 Application 对象持有,Activity 实例被静态变量持有。

IT 人不得不知的 linux 服务器搭建方法

IT 人不得不知的 linux 服务器搭建方法

OSC 请你来轰趴啦!1028 苏州源创会,一起寻宝 AI 时代

  根据主营服务器租用服务器托管的天下数据了解到的:使用 Linux 作为开发产品的操作系统的情况在目前来说越来越多,使用也越来越广泛。为了交叉编译,为了最接近开发目标,一般都会自己搭建一台 Linux 开发服务器。

在开发过程中,特别是嵌入式开发项目中,必然会出现多人同时工作、协调的情况,这样可以加快项目周期,为产品上市占得时间先机。现今,使用 Linux 作为开发产品的操作系统情况越来越多,使用越来越广泛。为了交叉编译,为了最接近开发目标,我们一般都会自己搭建一台 Linux 开发服务器。Linux 开发服务器一些常用的功能必须支持,比如 Sambanfstftphttpd 等。

那么如何搭建一台 linux 开发服务器呢?

  首先,我们需要选择合适的 Linux 操作系统作为服务器的系统,推荐使用 Fedora8Fedora10 等,笔者在长期使用的过程中觉得比较稳定吧 ! 如何安装该操作系统就不多说了。

  装好系统后,需要增加 Linux OS 用户,可以使用命令#adduser XXX; 增加 XXX 用户,#passwd XXX 给其设置登录密码。当然使用图形化创建用户也很直观、方便。在这个环节值得注意的一定要设置好该用户的 home 目录,一般都会设置到该用户的工作目录,各个用户的工作目录需要私有化、独立开来,这样方便些、安全些。

  多用户的账户有了,我们应该来打通网络功能了,Fedora bug,在图形化配置 GATEWAY 什么的会不成功,请安装如下方法修改:

  # cd cd /etc/sysconfig/network-scripts/

  # vi ifcfg-eth0

  修改配置文件如下:

  # Marvell Technology Group Ltd. 88E8001 Gigabit Ethernet Controller

  DEVICE=eth0

  HWADDR=00:16:e6:db:c2:96

  ONBOOT=yes

  BOOTPROTO=static // 这个应该是 “static”,而不是 “dhcp” “none”;

  USERCTL=yes

  PEERDNS=yes

  IPV6INIT=no

以上便是今天笔者谈论的关于搭建 linux 开发服务器的全部内容了。

Java Thread 你不得不知的那些事

Java Thread 你不得不知的那些事

        本文只聚焦JVM层面的线程模型,不考虑和真实的操作系统Thread模型挂钩(由于篇幅有限,本文不会介绍Thread dump结构,也不会介绍调优过程中对工具的综合使用,如ps,perf.top,iostat,jstack,TDA plugin,Thread inspector.如果有问题,欢迎大家留言交流)。后面会考虑对xUnixWindows平台的线程/进程模型进行深入分析,也希望大家能够喜欢。

         ok,言归正传。上图:

       
                                            

       Java的线程状态一共有NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED 6种状态。这里重点关注一下BLOCKED和TIMED_WAITING状态。

      BLOCKED状态:线程进入此状态的前提一般有两个:waiting for monitor(intrinsic or external) entry 或者 reenter 同步代码块。讲到这我们先了解一下Java线程模型中的两个队列。如图所示:


       每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。如果你不恰当的使用了ReentrantLock或者ReentrantReadWriteLock类,就有可能陷入BLOCKED状态,这个也是我们调优中经常会遇到的情况,解决方案也很简单,找到等待上锁的地址,分析是否发生了Thread starvation。
       至于TIME_WAITING状态,官方文档也讲解的比较好,即你在调用下面方法时,线程会进入该状态。


Thread.sleep 
	  Object.wait with timeout 
	  Thread.join with timeout 
	  LockSupport.parkNanos 
	  LockSupport.parkUntil


        这里重点关注一下LockSupport,该类是用来创建锁和其他同步类的基本线程阻塞原语,是一个针对Thread.suspend和Thread.resume()的优化,也是针对忙等,防止过度自旋的一种优化(关于这一点,感兴趣的同学可以参阅一下文献5)。

       Ok,在简单介绍完几个重点的线程状态后,我们通过几个具体的case来了解下Thread stack

         Case 1:NIO 中的Acceptor 

"qtp589745448-36 Acceptor0 SelectChannelConnector@0.0.0.0:8161" prio=10 tid=0x00007f02f8eea800 nid=0x18ee runnable [0x00007f02e70b3000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:241)
	- locked <0x00000000ec8ffde8> (a java.lang.Object)
	at org.eclipse.jetty.server.nio.SelectChannelConnector.accept(SelectChannelConnector.java:109)
	at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:938)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
	at java.lang.Thread.run(Thread.java:724)

   Locked ownable synchronizers:
	- None

        源代码中是这么实现的,如下:

public void accept(int acceptorID) throws IOException
100    {
101        ServerSocketChannel server;
102        synchronized(this)
103        {
104            server = _acceptChannel;
105        }
106
107        if (server!=null && server.isOpen() && _manager.isStarted())
108        {
109            SocketChannel channel = server.accept();
110            channel.configureBlocking(false);
111            Socket socket = channel.socket();
112            configure(socket);
113            _manager.register(channel);
114        }
115    }

        关于Thread stack,这里强调一点:nid,native lwp id,即本地轻量级进程(即线程)ID。

        Case 2: NIO中的Selector

"qtp589745448-35 Selector0" prio=10 tid=0x00007f02f8ee9800 nid=0x18ed runnable [0x00007f02e71b4000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
	at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:228)
	at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:81)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
	- locked <0x00000000ec9006f0> (a sun.nio.ch.Util$2)
	- locked <0x00000000ec9006e0> (a java.util.Collections$UnmodifiableSet)
	- locked <0x00000000ec9004c0> (a sun.nio.ch.EPollSelectorImpl)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
	at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:569)
	at org.eclipse.jetty.io.nio.SelectorManager$1.run(SelectorManager.java:290)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
	at java.lang.Thread.run(Thread.java:724)

   Locked ownable synchronizers:
	- None

      代码片段如下:

// If we should wait with a select
566                     if (wait>0)
567                     {
568                         long before=now;
569                         selector.select(wait);
570                         now = System.currentTimeMillis();
571                         _timeout.setNow(now);
572 
573                         // If we are monitoring for busy selector
574                         // and this select did not wait more than 1ms
575                         if (__MONITOR_PERIOD>0 && now-before <=1)
576                         {
577                             // count this as a busy select and if there have been too many this monitor cycle
578                             if (++_busySelects>__MAX_SELECTS)
579                             {
580                                 // Start injecting pauses
581                                 _pausing=true;
582 
583                                 // if this is the first pause
584                                 if (!_paused)
585                                 {
586                                     // Log and dump some status
587                                     _paused=true;
588                                     LOG.warn("Selector {} is too busy, pausing!",this);
589                                 }
590                             }
591                         }
592                     }

       Case 3: ActveMQ中针对MQTT协议的Handler

"ActiveMQ Transport Server Thread Handler: mqtt://0.0.0.0:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600" daemon prio=10 tid=0x00007f02f8ba6000 nid=0x18dc waiting on condition [0x00007f02ec824000]
   java.lang.Thread.State: TIMED_WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000faad0458> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
	at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
	at org.apache.activemq.transport.tcp.TcpTransportServer$1.run(TcpTransportServer.java:373)
	at java.lang.Thread.run(Thread.java:724)

   Locked ownable synchronizers:
	- None

       代码片段:

@Override
    protected void doStart() throws Exception {
        if (useQueueForAccept) {
            Runnable run = new Runnable() {
                @Override
                public void run() {
                    try {
                        while (!isStopped() && !isStopping()) {
                            Socket sock = socketQueue.poll(1, TimeUnit.SECONDS);
                            if (sock != null) {
                                handleSocket(sock);
                            }
                        }

                    } catch (InterruptedException e) {
                        LOG.info("socketQueue interuppted - stopping");
                        if (!isStopping()) {
                            onAcceptError(e);
                        }
                    }
                }
            };
            socketHandlerThread = new Thread(null, run, "ActiveMQ Transport Server Thread Handler: " + toString(), getStackSize());
            socketHandlerThread.setDaemon(true);
            socketHandlerThread.setPriority(ThreadPriorities.BROKER_MANAGEMENT - 1);
            socketHandlerThread.start();
        }
        super.doStart();
    }

      Case 5: 模拟银行转帐存款

"withdraw" prio=10 tid=0x00007f3428110800 nid=0x2b6b waiting for monitor entry [0x00007f34155bb000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.von.thread.research.DeadThread.depositMoney(DeadThread.java:13)
	- waiting to lock <0x00000000d7fae540> (a java.lang.Object)
	- locked <0x00000000d7fae530> (a java.lang.Object)
	at com.von.thread.research.DeadThread.run(DeadThread.java:28)
	at java.lang.Thread.run(Thread.java:724)

   Locked ownable synchronizers:
	- None

"deposit" prio=10 tid=0x00007f342810f000 nid=0x2b6a waiting for monitor entry [0x00007f34156bc000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.von.thread.research.DeadThread.withdrawMoney(DeadThread.java:21)
	- waiting to lock <0x00000000d7fae530> (a java.lang.Object)
	- locked <0x00000000d7fae540> (a java.lang.Object)
	at com.von.thread.research.DeadThread.run(DeadThread.java:29)
	at java.lang.Thread.run(Thread.java:724)

   Locked ownable synchronizers:
	- None
Found one Java-level deadlock:
=============================
"withdraw":
  waiting to lock monitor 0x00007f3400003620 (object 0x00000000d7fae540, a java.lang.Object),
  which is held by "deposit"
"deposit":
  waiting to lock monitor 0x00007f3400004b20 (object 0x00000000d7fae530, a java.lang.Object),
  which is held by "withdraw"

Java stack information for the threads listed above:
===================================================
"withdraw":
	at com.von.thread.research.DeadThread.depositMoney(DeadThread.java:13)
	- waiting to lock <0x00000000d7fae540> (a java.lang.Object)
	- locked <0x00000000d7fae530> (a java.lang.Object)
	at com.von.thread.research.DeadThread.run(DeadThread.java:28)
	at java.lang.Thread.run(Thread.java:724)
"deposit":
	at com.von.thread.research.DeadThread.withdrawMoney(DeadThread.java:21)
	- waiting to lock <0x00000000d7fae530> (a java.lang.Object)
	- locked <0x00000000d7fae540> (a java.lang.Object)
	at com.von.thread.research.DeadThread.run(DeadThread.java:29)
	at java.lang.Thread.run(Thread.java:724)

Found 1 deadlock.

      这里是一个非顺序加锁诱发的一个死锁场景。

       好了,差不多了,总结一下在调优过程中需要重点关注的三类情况(grep java.lang.Thread.State dump.bin | awk ''{print $2$3$4$5}'' | sort | uniq -c):

       1. waiting for monitor entry – thread state blocked。可能发生的问题: deadlock(sequential deadlock,starvation deadlock...)
       2. waiting on condition – sleeping or timed_waiting。可能发生的问题: IO bottleneck
       3. Object.wait – TIMED_WAITING。wait  & notifyAll使用上需要明确其性能及其局限性问题,JCIP上也推荐尽可能使用JUC提供的高级并发原语AQS

       参考文献:

  1. http://architects.dzone.com/articles/how-analyze-java-thread-dumps

  2. http://stackoverflow.com/questions/7698861/simple-java-example-runs-with-14-threads-why

  3. http://www.longene.org/forum/viewtopic.php?f=5&t=94&p=399#p399

  4. http://www.slideshare.net/Byungwook/analysis-bottleneck-in-j2ee-application

  5. http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

  6. http://www.artima.com/insidejvm/ed2/threadsynch.html

  7. JavaConcurrency in practice

  8. http://stackoverflow.com/questions/37026/java-notify-vs-notifyall-all-over-again

If  you prefer this article and want to learn more about distributed systems. please add the following public number.



本文分享自微信公众号 - RocketMQ官微(ApacheRocketMQ)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

Java:不得不知的Object类

Java:不得不知的Object类

目录
  • 一、equals
    • 1、equals与==有啥区别?
    • 2、equals方法的规范
    • 3、instanceof 和getClass()
    • 4、其他总结
  • 二、hashCode
    • 1、hashCode的规范
    • 2、String类的hashCode实现
  • 三、toString
    • 1、打印对象信息
    • 2、论优雅打印数组
    • 3、自定义toString方法
  • 五、其他重要方法

Class Object is the root of the class hierarchy.

Object类是所有类的顶级父类,任何一个对象(除了基本类型)都实现了Object类的方法,包括数组。

一、equals

public boolean equals(Object obj)

1、equals与==有啥区别?

我们通常会见到一类问题:==equals有啥区别?每次见到这种问题,都有一种模棱两可的感觉,这次完完整整地总结一波:

从Object类中的equals方法作为切入点,我们看看它的源码:

    public boolean equals(Object obj) {
        return (this == obj);
    }

事实上,在Object类中,a.equals(b)等价于a==b,即判断两个对象是否具有相同的引用。

==对于基本数据类型,判断数值是否相等,对于引用数据类型,判断地址值是否相等,也就是是否具有相同的引用。

我们发现,如果两个对象具有相同引用,则equals结果相等。但大多数情况下,这样的判断形式没啥意义,在实际情况下,我们往往需要用equals检测对象状态、属性的相等性,往往会在类中重写equals方法。

我们以String来举例,看看String类中的源码:

    //重写注意形参类型必须是Object
    public boolean equals(Object anObject) {
        //引用相同,必然返回true
        if (this == anObject) {
            return true;
        }
        //判断anObject类型是否和String相同
        if (anObject instanceof String) {
            //anObject向下转型
            String anotherString = (String)anObject;
            int n = value.length;
            //每个位置上字符逐一比较
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

我们可以发现,如果是两个字符串对象,除了判断是否同一引用,还比较字符串值是否相等(至于字符串的地址相关问题,我们之后再做总结)。同样的例子还有Integer等包装类,如果感兴趣,可以查看源码。

2、equals方法的规范

equals方法实现了非空对象引用的等价关系

  1. 自反性(reflexive):对于任何非空引用x,x.equals(x)为true。
  2. 对称性(symmetric):对于任何非空的引用x和y,当且仅当y.equals(x)返回true时,x.equals(y)返回true。
  3. 传递性(transitive):对于任何非空引用x、y和z,如果x.equals(y)返回true,而y.equals(z)返回true,那么x.equals(z)也应该返回true。
  4. 一致性(consistent):如果非空引用x和y的对象没有变化,反复调用x.equals(y)返回相同的结果。
  5. 对于任何非空引用x,x.equals(null)应该返回false。

3、instanceof 和getClass()

如果equals方法判断双方属于同一类,按照上面的规则编写代码其实比较轻松。但是,如果双方不同类,则关于对称性,就需要考虑用哪种方式判定。

我们通过小测试看看instanceofgetClass()的区别:

public class EqualsDemo {
    public static void main(String[] args) {
        Super aSuper = new super();
        Sub sub = new Sub();
        System.out.println(sub.getClass() == aSuper.getClass());//false
        System.out.println(sub instanceof Super);//true
    }
}
class Super{}
class Sub extends Super{}

可以发现,getClass判断时,子类与父类类型严格不同;而instanceof意味着父类的概念适用于所有子类,相同类。

对于一个要编写equals的类而言:

  • 如果equals的语义在子类中有所改变,则应使用getClass检测。

  • 如果所有的子类都拥有同一的语义,就使用instanceof检测。

4、其他总结

  • 重写equals方法时,注意形参类型一定是Object
  • 数组类型的域可以使用Arrays工具类的静态方法static boolean equals(type[] a,type[] b)判断是否相等。
    int[] a = {1,2,3};
    int[] b = {1,3};
    System.out.println(Arrays.equals(a,b));//true

二、hashCode

public native int hashCode();

Object底层的hashCode方法是native修饰的,不是Java语言编写的,我们要知道:

这个方法将会返回对象的哈希码值,哈希码是整型且没有规律的,也叫做散列码。

我们之前进行过基于JDK1.8的HashMap源码分析,了解到通过哈希函数将键值转化为整型的哈希值,然后通过巧妙的操作,将其映射到数组的各个索引上,利用数组查询快的优势,大大提升了性能。

1、hashCode的规范

  1. 当equals方法被重写时,应该重写hashCode方法,从而保证两个相等的对象拥有相同的哈希码
  2. 程序执行过程中,如果对象的数据没有被修改,则多次调用hashCode方法将返回相同的整数。
  3. 两个不相等的对象可能具有相同的哈希码,但在实现hashCode方法时应避免太多这样的情况出现。

2、String类的hashCode实现

hash函数非常多样,我们以常见的String类的hashCode实现举例:

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
  • 字符串底层是由字符数组组成,当我们传入"abc"时,用val[]这个字符数组接收['a','b','c']。
  • 然后将字符转化为int,也就是[97,98,99]。
  • ((97x31)+98)x31+99 = 96354

三、toString

public String toString()

该方法返回对象的字符串表现形式,结果应该是简洁但信息丰富的表示,便于阅读。建议所有子类都重写此方法。

如果不重写的话,Object类中定义的形式会让人很不爽,以下是toString()源码:

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

返回字符串 = 对象对应运行时类的名称+“@”+对象哈希码的无符号十六进制表示形式

1、打印对象信息

我们可以试一试:

    public static void main(String[] args) {
        Super s = new super();
        System.out.println(s);
    }
    //输出结果
    com.my.objectClass.equals.Super@677327b6

System.out.println(s);意思是标准输出s到打印台上,我们看看具体执行的步骤:

    public void println(Object x) {
        //首先调用String类的valueOf方法,获得s的字符串表现形式
        String s = String.valueOf(x);
        synchronized (this) {
            //打印
            print(s);
            //换行
            newLine();
        }
    }

我们继续看看这个valueOf是怎么一回事:

    public static String valueOf(Object obj) {
    //如果为null,则输出"null",非空则调用toString()
        return (obj == null) ? "null" : obj.toString();
    }

至此,我们可以知道,我们在试图打印对象的时候,都会调用对象的toString方法,我们可以试着重写该方法,则测试的时候能够快速清晰地定位。

2、论优雅打印数组

数组也是一种特殊的类型,我们通过打印可以发现,他的结果比较离谱,看上去比较奇怪,具体规则可以查看官方文档Class类的getName()方法。

    int[] arr = {1,3};
    System.out.println(arr);//[I@14ae5a5

总之,我们总是希望我们打印出来的是,数组中的数整整齐齐排列着的,对吧。

我们可以利用Arrays工具类的静态方法toString方法,优雅地打印数组:

    public static String toString(int[] a) {
        if (a == null)
            return "null";
        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {
            b.append(a[i]);
            if (i == iMax)
                return b.append(']').toString();
            b.append(",");
        }
    }

3、自定义toString方法

既然是自定义,那么就没啥严格规定,为了测试数据的时候更加清晰,可以想想适合自己的打印信息的方法。

现在的IDE一般都是支持根据字段,自动生成toString方法的,比如我是用的IDEA,按住AltIns键就可以快速生成。

    
public class EqualsDemo {
    public static void main(String[] args) {
        System.out.println(new Person());
    }
}
class Person{

    String name = "天乔巴夏丶";
    int age = 18;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ",age=" + age +
                '}';
    }
}

//测试结果
Person{name='天乔巴夏丶',age=18}

五、其他重要方法

至此,已经总结了Object中三个比较重要的方法,其他的诸如

  • 并发编程相关的wait()、notify()、notifyAll()等方法
  • 用于垃圾回收终结的finalize()方法
  • 用于创建对象副本的clone()方法
  • 用于获取对象运行时类信息getClass()的方法

这些方法,我们之后再做总结。

参考资料:《Java核心技术卷Ⅰ》

我们今天的关于营销卖货不得不知的3个赠品促销手法!营销卖货不得不知的3个赠品促销手法是什么的分享已经告一段落,感谢您的关注,如果您想了解更多关于Android 开发 不得不知的使用技巧、IT 人不得不知的 linux 服务器搭建方法、Java Thread 你不得不知的那些事、Java:不得不知的Object类的相关信息,请在本站查询。

本文标签:

上一篇CMM:2016年彩电市场618促销总结(附下载)(2020年彩电市场)

下一篇营销案例经营计划 推论引流举动促销贸易形式超旧例实体店教程(促销引流方式)