Chapter1 Android體系與系統架構
Android 高富帥 Google Linux Open Source
四層:Applications/Application Framework/Libraries+Android Runtime/Linux Kernel
Android 5.X起ART取代Dalvik Dalvik運作時編譯 ART安裝時編譯
PackageManager/ResourceManager/ActivityManager/WindowManager/ViewSystem/NotificationManager/LocationManager/TelphonyManager/Content Provider/XMPP Service
Android NDK APP AndroidManifest/Dalvik Classes/Resource Bundle/Libraries & JNI
Android SDK APP AndroidManifest/Dalvik Classes/Resource Bundle
四大元件:Activity/BroadcastReceiver/ContentProvider/Service
Intent信使
上下文Context:Application/Activity/Service都繼承自Context ApplicationContext是全局的上下文 getApplicationContext()
Android系統源碼:http://androidxref.com/ Makefile機制
/system /data目錄
/system/app/ 系統APP /system/bin/ Linux自帶元件 /system/build.prop 系統屬性檔案 /system/fonts/ 字型庫 /system/framework/ 核心檔案架構層 /system/lib/ 節享庫.so /system/media/ 提示音系統鈴聲 /system/usr 使用者配置
/data/app/ 使用者安裝的app /data/data/ App的資料資訊 /data/system/ 手機的各項系統資訊 /data/misc Wi-Fi/VPN資訊
Project/Module
app
--manifests
----AndroidManifest.xml
--java
----com.test.HelloWorld
----com.test.HelloWorld(test)
----com.test.HelloWorld(Android Test)
--res
Gradle Scripts
Chapter2 Android開發工具新接觸
工欲善其事,必先利其器
IDE Integrated Development Environment 內建開發環境
Eclipse vs Android Studio(2013年I/O大會)
http://www.androiddevtools.cn
Case sensitive completion
ADB指令
adb version
adb install -r F:\test.apk
adb push D:\test.apk /system/app/
adb push <local> <remote>
adb pull <remote> <local>
adb shell
adb remount
adb shell
rm *.apk
adb shell input keyevent 3
adb shell input touchscreen
adb shell input touchscreen swipe
pm/am
adb指令來源于\system\core\toolbox 和 \frameworks\base\cmds
Chapter3 Android控件架構與自定義控件詳解
Android中每個控件都會在界面中占得一塊矩形區域 ViewParent-ViewGroup/View 樹形結構
findViewById()深度優先周遊 setContentView()
Activity(PhoneWindow(DecorView(TitleView+ContentView))) 其中DecorView為根View WindowManagerService接收所有View的監聽事件
ContentView是一個id為content的FrameLayout TitleView一般為ActionBar Container requestWindowFeature(Window.FEATURE_NO_TITLE)要在setContentView()之前
ActivityManagerService進行Activity的生命周期回調
View的測量:onMeasure()
MeasureSpec是一個32位的int值 高2位為測量模式 低30位為測量的大小
測量模式:
EXACTLY 适用于 android:layout_width="100"或者match_parent
AT_MOST 适用于wrap_content
UNSPECIFIED 适用于自定義View時 想多大就多大
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
private int measureWidth(int measureSpec){
int result = 100;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else if(specMode == MeasureSpec.AT_MOST){
result = Math.min(result, specSize);
}
return result;
}
View的繪制:onDraw()
Canvas對象 其它地方Canvas canvas = new Canvas(bitmap); canvas.drawXXX();
ViewGroup的測量:周遊子View
ViewGroup的繪制:dispatchDraw()
自定義View:擴充現有控件/組合實作控件/重寫View實作
onFinishInflate()/onSizeChanged()/onMeasure()/onLayout()/onDraw()/onTouchEvent()
定義屬性:res/values/attrs.xml <resources><declare-styleable name="TopBar"><attr name="title" format="string"/></declare-styleable></resources>
string|dimension|reference|color|boolean|enum
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
ta.recycle();
xmlns:custom="http://schemas.android.com/apk/res-auto"
重繪:invalidate();/postInvalidateDelayed(300);
自定義ViewGroup:int count = getChildCount(); View childView = getChildAt(i);
事件攔截處理機制
MotionEvent ACTION_DOWN ACTION_MOVE ACTION_UP
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev);//View沒有此方法
public boolean onTouchEvent(MotionEvent ev);
流程:Adispatch->Aintercept->Bdispatch->Bintercept->Viewdispatch->ViewonTouch->BonTouch->AouTouch
誰攔截誰處理,處理完就回調
Chapter4 ListView使用技巧
public class DataAdapter extends BaseAdapter{
private ArrayList<String> mDatas;
private LayoutInflater mInflater;
public DataAdapter(Context context){
mDatas = new ArrayList<>();
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount(){return mDatas.size();}
@Override
public Object getItem(int position){return mData.get(position);}
@Override
public long getItemId(int position){return position;}
@Override
public View getView(int position, View convertView, ViewGroup ){
ViewHolder holder = null;
if(convertView == null){
convertView = mInflater.inflate(R.layout.item_content, null);
holder = new ViewHolder();
holder.content = (TextView)convertView.findViewById(R.id.content);
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}
holder.content.setText("use ViewHolder cache");
return convertView;
}
public final class ViewHolder{
public TextView content;
}
}
設定分隔線:android:divider="@android:color/darker_grey" android:dividerHeight="10dp"
隐藏分隔線:android:divider="@null"
隐藏滾動條:android:scrollbars="none"
取消Item的點選效果:android:listSelector="#00000000" 或 android:listSelector="@android:color/transparent"
顯示在第幾項:listView.setSelection(N);listView.smoothScrollXX();
notifyDataSetChanged();
空ListView可以設定setEmptyView()來顯示内容為空時的提示
ListView的滑動監聽:GestureDetector/VelocityTracker onTouchListener/onScrollListener
firstVisibleItem/visibleItemCount/totalItemCount
overScrollBy()中maxOverScrollY改為一個數字就可以彈性十足了
自動顯示隐藏的ListView
//添加頭防止ActionBar引藏了第一個Item
View header = new View(this);
header.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, (int)getResources().getDimension(R.dimen_abc_action_bar_default_height_material)));
mListView.setHeader(header);
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();//擷取最低滑動距離
View.OnTouchListener mOnTouchListener = new View.OnTouchListener(){……};//動畫用屬性動畫
不同布局的ListView
getItemViewType()/getViewTypeCount()
Chapter5 Android Scroll分析
滑動一個View本質上就是移動一個View改變其目前所處位置
Android坐标系:螢幕左上角(0, 0) 右 X正 下 Y正 getLocationOnScreen(int[] location); getRawX()/getRawY()
視圖坐标系:相對于父視圖 getX()/getY()
MotionEvent ACTION_DOWN/ACTION_UP/ACTION_MOVE/ACTION_CANCEL/ACTION_OUTSIDE/ACTION_POINTER_DOWN/ACTION_POINTER_UP
View提供的擷取坐标的方法:getTop()/getLeft()/getRight()/getBottom()
MotionEvent提供的方法:getX()/getY()/getRawX()/getRawY()
實作滑動的八種方法:
layout()/offsetLeftAndRight()和offsetTopAndBottom()/LayoutParams(getLayoutParams()/setLayoutParams)/scrollTo與scrollBy
Scroller/屬性動畫/ViewDragHelper(說好八種隻有七種還有一種,你想想)
Chapter6 Android繪圖機制與處理技巧
螢幕大小:螢幕對角線的長度,機關寸,如5.36寸
分辨率:螢幕的像素點數,720*1280指寬有720個像素點高有1280個像素點
PPI:Pixels Per Inch或DPI,Dots Per Inch 對角線像素點數除以螢幕大小
hdpi:240 / 480 X 800
dp/sp
public class DisplayUtil{
public static int px2dip(Context context, float pxValue){
final float scale = context.getResources().getDisplayMetrics().density;
return (int)(pxValue / scale + 0.5f);
}
public static int dip2px(Context context, float dipValue){
final float scale = context.getResources().getDisplayMetrics().density;
return (int)(dipValue * scale + 0.5f);
}
public static int px2sp(Context context, float pxValue){
final float fontScale = context.getResources().getDisplayMetrics().density;
return (int)(pxValue / fontScale + 0.5f);
}
public static int sp2px(Context context, float spValue){
final float fontScale = context.getResources().getDisplayMetrics().density;
return (int)(spValue * fontScale + 0.5f);
}
}
//TypedValue類也可以進行轉換
protected dp2px(int dp){
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
protected sp2px(int sp){
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
2D繪圖:Canvas/Paint
Android XML繪圖:<bitmap/> <shape/> <layer-list/> <selector/>
Canvas.save()//類似儲存圖層
Canvas.restore()//類似合并圖層
Canvas.translate()//平移坐标原點
Canvas.rotate()//翻轉坐标原點
saveLayer()/saveLayerAlpha()/restore()/restoreToCount()
Bitmap位圖ARGB
色彩矩陣:ColorMatrix 4X5 一維數組存儲 矩陣乘法 RGBA 改變系數 改變偏移量
色調:setRotate()/飽和度:setSaturation()/亮度:setScale()
mImageView.setImageBitmap(ImageHelper.handleImageEffect());
paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
像素點分析:
bitmap.getPixels(pixels, offset, stride, x, y, width, height);
bitmap.setPixels(newPx, 0, width, 0, 0, width, height);
Android圖像處理之圖形特效處理Matrix 3x3
Translate/Rotate/Scale/Skew
像素塊分析:drawBitmapMesh()
畫筆特效處理:PorterDuffXfermode
Shader着色器:BitmapShader/LinearGradient/RadialGradient/SweepGradient/ComposeShader
PathEffect
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable
surfaceCreated()/surfaceChanged()/surfaceDestroyed()/run()
private SurfaceHolder mHolder;
private Canvas mCanvas;
private boolean isDrawing;
mHolder = this.getHolder();
mHolder.addCallback(this);
mCanvas = mHolder.lockCanvas();
mHolder.unlockCanvasAndPost(mCanvas);
GenymotionChapter7 Android動畫機制與使用技巧
Frame Animation(drawable目錄裡frame.xml)/Tween Animation(anim目錄裡tween.xml)
Animation架構定義了AlphaAnimation/RotateAnimation/TranslateAnimation/ScaleAnimation四種動畫方式和AnimationSet動畫集合
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
//aa.setAnimationListener(new Animation.AnimationListener(){……});
view.startAnimation(aa);
Animator架構AnimatorSet/ObjectAnimator
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 300);
animator.setDuration(300);
animator.start();
//操作對象必須有getter/setter
//translationX/translationY/rotation/rotationX/rotationY/scaleX/scaleY/pivotX/pivotY/x/y/alpha
private static class WrapperView{
private View mTarget;
public WrapperView(View target){
mTarget = target;
}
public int getWidth(){
return mTarget.getLayoutParams().width;
}
public void setWidth(int width){
mTarget.setLayoutParams().width = width;
mTarget.requestLayout();
}
}
WrapperView wrapper = new WrapperView(mButton);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("translationY", 300);
ObjectAnimator.ofPropertyValuesHolder(view, pvh1, pvh2).setDuration(3000).start();
ObjectAnimator繼承自ValueAnimator
ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
animator.setTarget(view);
animator.setDuration(1000).start();
animator.addUpdateListener(new AnimatorUpdateListener(){……});
一個完整的動畫具有start repeat end cancel四個過程
animator.addListener(new AnimatorListener(){……});
animator.addListener(new AnimatorListenerAdapter(){……});
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1, animator2, animator3);
set.start();
//playTogether()/playSequentially()/before()/after()
animator目錄下animator.xml
<objectAnimator></objectAnimator>……
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scalex);
animator.setTarget(mView);
animator.start();
view.animate().alpha(0).y(300).setDuration(1000).widthStartAction(*).withEndAction(*).start();
Android布局動畫android:animateLayoutChanges="true"或者LayoutAnimationController
Interpolators插值器 定義動畫的變化速率 線性 加速度
自定義動畫:實作applyTransformation的邏輯并Override initialize() 主要是Matrix m = t.getMatrix();
SVG矢量動畫機制(5.X)Scalable Vector Graphics可伸縮矢量圖形 使用XML定義圖形 W3C标準
<path>标簽 指令:M/L/H/V/C/S/Q/T/A/Z 大寫絕對定位參照全局坐标系 小寫相對定位參照父容器會标系
http://editor.medhod.ac/
VectorDrawable/AnimatedVectorDrawable
group-path
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="200dp"
android:width="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group
android:name="test"
android:rotation="0">
<path
android:fillColor="@android:color/holo_blue_light"
android:pathData="M 25 0 a 25 25 0 1, 0 50, 0" />
</group>
</vector>
AnimatedVectorDrawable如同膠水一樣連接配接VectorDrawable和ObjectAnimator <animated-vector/>
(imageView.getDrawable()).start();開始動畫
線圖動畫 模拟三球儀 軌迹動畫
Chapter8 Activity與Activity調用棧分析
setContentView(View);
Running/Paused/Stopped/Killed
onCreate/onStart/onResume/onPause/onStop/onDestroy/onRestart
onSaveInstanceState()/onRestoreInstanceState
A task is a stack of activities.
android:laaunchMode/standard singleTop singleTask singleTop
Intent.FLAG_ACTIVITY_NEW_TASK
Intent.FLAG_ACTIVITY_SINGLE_TOP
Intent.FLAG_ACTIVITY_CLEAR_TOP
Intent.FLAG_ACTIVITY_NO_HISTORY
clearTaskOnLaunch/finishOnTaskLaunch/alwaysRetainTaskState
Chapter9 Android系統資訊與安全機制
系統的配置資訊:android.os.Build 和 SystemProperty
String board = Build.BOARD;
String os_version = System.getProperty("os.version");
system/build.prop /proc
PM/AM PackageManager/ActivityManager
ActivityInfo封裝了<activity/>和<receiver/>
ServiceInfo封裝了<service/>
ApplicationInfo封裝了<application/>
PackageInfo封裝了<manifest/>
ResolveInfo封裝了<intent/>
packages.xml檔案 /data/system/packages.xml
安全機制五道防線:代碼混淆proguard/AndroidManifest檔案權限聲明檢查/數字證書/UID通路權限控制/沙箱隔離
反編譯:
apktool/dex2jar/jd-gui/AndroidKiller
Chapter10 Android性能優化
UI優化
VSYNC信号觸發重新整理16ms一次 60fps
避免Overdraw
View測量、布局、繪制都是通過對View的周遊進行的
<include />重用Layout
<ViewStub />延遲加載Layout mViewStub = (ViewStub)findViewById(R.id.not_often_use);mViewStub.setVisibility(View.VISIBLE);或View inflateView = mViewStub.inflate();
Hierarchy Viewer
記憶體優化
Android應用沙箱機制 LMK
RAM
寄存器:速度最快,程式無法控制
棧:基本類型的資料和對象的引用
堆:new建立的對象和數組 由GC管理
靜态存儲區:一直存在的資料 如靜态資料變量
常量池:JVM為每個裝載的類型維護一個常量池
ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int heapSize = manager.getLargeMemoryClass();
調用System.gc()隻是建議系統進行GC但不一定會被采納
Bitmap優化(常見于OOM):及時回收記憶體(bitmap.recycle())、圖檔緩存
任何Java類都将占用大約500位元組的記憶體空間 建立一個類的執行個體會消耗大約15位元組的記憶體
Lint/Memory Monitor/TraceView/Android Device Monitor/MAT/Dumpsys/
Debug.startMethodTracing("test");
Debug.stopMethodTracing();
Chapter11 搭建雲端伺服器
BAAS Backend As A Service
StackMob/Parse/Amazon/Kinvey/Bmob/原子雲/AVOS Cloud/百度Frontia/華為PowerApp
資料存儲 消息推送 檔案服務 API分析 應用統計 移動官網等
Chapter12 Android 5.X新特性
Material Design:紙墨光影紙紙、過渡動畫、大色塊
http://www.google.com/design/#resources
三種預設主題:
@android:style/Theme.Material
@android:style/Theme.Material.Light
@android:style/Theme.Material.Light.DarkActionBar
Color Palette
colorPrimary/colorPrimaryDark/textColorPrimary/navigationBarColor/windowBackground
色調各類:Vibrant/Vibrant dark/Vibrant light/Muted/Muted dark/Muted light
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.test);
Palette.generateAsync(bmp, new Palette.PaletteAsyncListener(){
@Override
public void onGenerated(Palette palette){
Palette.Swatch vibrant = palette.getDarkVibrantSwatch();
getActionBar().setBackgroundDrawable(new ColorDrawable(vibrant.getRgb()));
getWindow().setStatusBarColor(vibrant.getRgb());
}
});
空間坐标系OXYZ
Z = elevation + translationZ
布局中:android:elevation="Xdp"
代碼中:view.setTranslation(X);view.animate().translationZ(X);
Tinting
android:tint="@android:color/holo_blue_bright"
android:tintMode="multiply"
Clipping
ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider(){
@Override
public void getOutline(View view, Outline outline){
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
};
v.setOutlineProvider(viewOutlineProvider);
RecyclerView
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{
private ArrayList<String> mData;
public RecyclerAdapter(){
mData = new ArrayList<>();
}
private OnItemClickListener itemClickListener;
public void setOnItemClickListener(OnItemClickListener itemClickListener){
this.itemClickListener = itemClickListener;
}
public interface OnItemClickListener{
void onItemClick(View view, int position);
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
pulic TextView textView;
public ViewHolder(View itemView){
super(itemView);
textView = (TextView)itemView;
textView.setOnClickListener(this);
}
@Override
public void onClick(View v){
if(itemClickListener != null){
itemClickListener.onItemClick(v, getPosition());
}
}
}
@Override
public ViewHodler onCreateViewHolder(ViewGroup viewGroup, int i){
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int i){
viewHolder.textView.setText(mData.get(i));
}
@Override
public int getItemCount(){
return mData.size();
}
}
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setHasFixedSize(true);
CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
card_view:cardBackgroundColor="@color/cardview_initial_background"
card_view:cardCornerRadius="10dp"
三種Transition類型:進入、退出、共享元素
Activity進入退出:explode/slide/fade
Activities共享元素:changeBounds/changeClipBounds/changeTransform/changeImageTransform
進入退出使用方法:
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());//ActivityA中
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);//B
或者:<item name="android:windowContentTransitions">true</item>
getWindow().setEnterTransition(new Explode());//B
getWindow().setExitTransition(new Slide());
共享元素使用方法:
android:transitionName="XXX"
android:transitionName="XXX"
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(view. "share"), Pair.create(fab, "fab")).toBundle());//A
Ripple效果
android:background="?android:attr/selectableItemBackground"
android:background="?android:attr/selectableItemBackgroundBorderless"
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@android:color/holo_blue_bright">
<item>
<shape>……</shape>
</item>
</ripple>
Animator animator = ViewAnimationUtils.createCircularReveal(oval, oval.getWidth()/2, oval.getHeight()/2, oval.getWidth(), 0);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(2000);
animator.start();
狀态切換動畫
StateListAnimator
android:stateListAnimator="@drawable/anim_change"
selector的item中
<set>
<objectAnimator android:propertyName="rotationX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="360"
android:valueType="floatType"/>
</set>
或者:AnimationInflater.loadStateListAnimator();View.setStateListAnimator();
Animated-selector
<animated-selector>
<item
android:id="@+id/state_on"
android:state_checked="true">
<bitmap android:src="@drawable/ic_done_anim_000"/>
<item>
<item
android:id="@+id/state_off">
<bitmap android:src="@drawable/ic_plus_anim_30"/>
<item>
<transition
android:fromId="@+id/state_on"
android:toId="@+id/state_off">
<animation-list>
<item android:duration="10"><bitmap android:src=""/></item>
</animation-list>
</transition>
……
</animated-selector>
ToolBar
ToolBar mToolBar = (ToolBar)findViewById(R.id.toolbar);
mToolBar.setLogo(R.drawable.ic_launcher);
mToolBar.setTitle("主标題");
mToolBar.setSubtitle("副标題");
setSupportActionBar(mToolBar);
DrawerLayout
配合使用
getSupportActionBar().setDisplayHomeAsUpEnable(true);
mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.abc_action_bar_home_description, R.string.abc_action_bar_home_description_format);
mDrawerToogle.syncState();
mDrawerLayout.setDrawerListener(mDrawerToggle);
基本Notification
NotificationBuilder builder = new NotificationBuilder(this);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
builder.setContentIntent(pi);
builder.setContentTitle("test");
NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(520, builder.build());
折疊Notification
RemoteViews contentView = new RemoteViews(getPackageame(), R.layout.notification);
contentView.setTextViewText(R.id.textView, "show me when collapsed");
notification.contentView = contentView;
RemoteViews expandedView = new RemoteViews(getPackageName(), R.layout.notification_expanded);
notification.bigContentView = expandedView;
懸挂Notification
builder.setContentText("test").setFullScreenIntent(pendingIntent, true);
不同等級
VISIBILITY_PRIVATE//沒有鎖屏時
VISIBILITY_PUBLIC//任何時候
VISIBILITY_SECRET//沒有PIN和PASSWORD等的情況下
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
builder.setColor(Color.CYAN);
builder.setCategory(Notification.CATEGORY_MESSAGE);