這是自己在項(xiàng)目當(dāng)中踩過的坑而總結(jié)整理的筆記,僅供自己。署名android developer
基礎(chǔ)知識(shí)
此部分包含android相關(guān)的基礎(chǔ)部分,而且是容易忽視的基礎(chǔ),平時(shí)很少用到但用時(shí)卻不知道的。
1 Android launchMode
Task是一個(gè)具有棧結(jié)構(gòu)的對(duì)象,一個(gè)Task可以管理多個(gè)Activity,啟動(dòng)一個(gè)應(yīng)用,也就創(chuàng)建一個(gè)與之對(duì)應(yīng)的task。Activity一共有以下四種launchMode:
- standard
- singleTop
- singleTask
- singleInstance
[圖片上傳失敗...(image-a1184e-1556638375279)]
[圖片上傳失敗...(image-398128-1556638375279)]
[圖片上傳失敗...(image-eea1a2-1556638375279)]
每次跳轉(zhuǎn)系統(tǒng)都會(huì)在task中生成一個(gè)新的FirstActivity實(shí)例,并且放于棧結(jié)構(gòu)的頂部,當(dāng)我們按下后退鍵時(shí),才能看到原來的FirstActivity實(shí)例。standard啟動(dòng)模式,不管有沒有已存在的實(shí)例,都生成新的實(shí)例。
[圖片上傳失敗...(image-d1ace4-1556638375279)]
[圖片上傳失敗...(image-3ddbdc-1556638375279)]
[圖片上傳失敗...(image-9b51ba-1556638375279)]
三個(gè)序列號(hào)是相同的,也就是說使用的都是同一個(gè)FirstActivity實(shí)例;如果按一下后退鍵,程序立即退出,說明當(dāng)前棧結(jié)構(gòu)中只有一個(gè)Activity實(shí)例。跳轉(zhuǎn)時(shí)系統(tǒng)會(huì)先在棧結(jié)構(gòu)中尋找是否有一個(gè)FirstActivity實(shí)例正位于棧頂,如果有則不再生成新的,而是直接使用。
singleTop啟動(dòng)模式,如果發(fā)現(xiàn)有對(duì)應(yīng)的Activity實(shí)例正位于棧頂,則重復(fù)利用,不再生成新的實(shí)例。
singleTask模式,如果發(fā)現(xiàn)有對(duì)應(yīng)的Activity實(shí)例,則使此Activity實(shí)例之上的其他Activity實(shí)例統(tǒng)統(tǒng)出棧,使此Activity實(shí)例成為棧頂對(duì)象,顯示到幕前。
singleInstance啟動(dòng)模式可能是最復(fù)雜的一種模式,為了幫助大家理解,我舉一個(gè)例子,假如我們有一個(gè)share應(yīng)用,其中的ShareActivity是入口Activity,也是可供其他應(yīng)用調(diào)用的Activity,我們把這個(gè)Activity的啟動(dòng)模式設(shè)置為singleInstance,然后在其他應(yīng)用中調(diào)用。我們編輯ShareActivity的配置:
<activity android:name=".ShareActivity" android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SINGLE_INSTANCE_SHARE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
然后我們?cè)谄渌麘?yīng)用中這樣啟動(dòng)該Activity:
Intent intent = new Intent("android.intent.action.SINGLE_INSTANCE_SHARE");
startActivity(intent);
一般的登陸界面LoginActivity設(shè)計(jì)為
android:launchMode="singleTask"
Flags模式:
//如果activity在task存在,拿到最頂端,不會(huì)啟動(dòng)新的Activity
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
//如果activity在task存在,將Activity之上的所有Activity結(jié)束掉
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//默認(rèn)的跳轉(zhuǎn)類型,將Activity放到一個(gè)新的Task中
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//如果Activity已經(jīng)運(yùn)行到了Task,再次跳轉(zhuǎn)不會(huì)在運(yùn)行這個(gè)Activity
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
2 Spinner
- 獲取默認(rèn)值:Spinner.setSelection(int positon, boole anitme);
在做二級(jí)聯(lián)動(dòng)時(shí)此方法屬于同步方法,需要開啟異步任務(wù)。
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
Spinner.setSelection(0, true);
}
});
或者做一個(gè)延時(shí)操作。
3 ScrollView
- 滾動(dòng):
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
// ScrollView.FOCUS_DOWN 底部
// ScrollView.FOCUS_UP 頂部
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
4 Android Studio支持java8
第一步 配置JAVA8
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
第二步 使用jack鏈
defaultConfig {
jackOptions {
enabled true
}
}
5 軟盤
保持狀態(tài)欄不被擠掉
android:windowSoftInputMode="stateVisible|adjustResize|stateHidden"
適合頂部有輸入框
android:windowSoftInputMode="adjustPan|stateHidden"
6 屏幕旋轉(zhuǎn)
在需要旋轉(zhuǎn)的界面Activity注冊(cè)清單中配置
android:configChanges="keyboardHidden|orientation|screenSize"
再在Activity中重寫
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// do something
} else {
// do something
}
}
7 ScrollView嵌套列表解決焦點(diǎn)停留在列表上的問題
在Scrollview第一層子View上設(shè)置屬性
android:descendantFocusability="blocksDescendants"
8 View超出容器
第一步設(shè)置View的margin值為負(fù)數(shù)
android:layout_margint="-10dp"
第二步給父容器設(shè)置屬性 false不裁剪
android:clipChildren="false"
9 防止頁面打開時(shí)彈出軟盤
添加屬性
android:focusable="true"
android:focusableInTouchMode="true"
10 Formatter.formatFileSize(Context context, long sizeBytes)
格式化文件大小,將字節(jié)數(shù)據(jù)格式化為 B、KB、M 等單位的相應(yīng)數(shù)據(jù)。context
參數(shù)用于判斷返回結(jié)果的字符串順序,right-to-left
還是 left-to-right
形式的。這個(gè)工具類免去我們自己轉(zhuǎn)化計(jì)算的過程,非常方便,特別適用于應(yīng)用內(nèi)文件下載的類似場(chǎng)景。
11 TypedValue.applyDimension(int unit, float value, DisplayMetrics metrics)
- COMPLEX_UNIT_PX
- COMPLEX_UNIT_DIP
- COMPLEX_UNIT_PT
- COMPLEX_UNIT_SP
將指定單位的尺寸數(shù)據(jù)按照當(dāng)前設(shè)備屏幕信息轉(zhuǎn)化為相應(yīng)的像素值。其中,TypedValue
為第一個(gè)參數(shù)提供了常用的單位值
12 Space
Space 是一個(gè)用于創(chuàng)建視圖之間空隙的輕量級(jí) View。在onDraw()
方法中不執(zhí)行任何繪制,所以android:background
屬性對(duì)他來說不起作用。通常我們使用 View 創(chuàng)建視圖間的空隙,在不考慮背景色的情況下,Space
其實(shí)效率更高。注意,由于是 API 14 引入的控件,如果需要向前兼容的話,需要使用到 support v4 包。
13 view.performClick()
自動(dòng)調(diào)用View
點(diǎn)擊事件。通常按鈕等控件只有在用戶點(diǎn)擊時(shí)才能觸發(fā)其點(diǎn)擊事件,該方法可以由某些特殊條件觸發(fā)模擬用戶點(diǎn)擊行為。類似的還有performLongClick()
方法。
14 Log.getStackTraceString(Throwable tr)
Log
類提供的一個(gè)公共靜態(tài)方法,與常見的Log.i()
等方法打印日志到logcat
控制臺(tái)不同的是,該方法從Throwable
對(duì)象中獲取錯(cuò)誤信息,并以字符串的形式返回。當(dāng)你需要做錯(cuò)誤信息的數(shù)據(jù)持久化,比如保存至本地存儲(chǔ)卡中或者上傳至服務(wù)器時(shí),利用這個(gè)方法就非常方便。
15 Linkify.addLinks()
我們知道對(duì)于TextView
文本控件中的內(nèi)容,通過android:autoLink
屬性可以為其添加諸如web
、phone
等固定模版的超鏈接點(diǎn)擊事件。但畢竟系統(tǒng)模版有限,而利用Linkify.addLinks()
方法可以添加一些應(yīng)用內(nèi)自定義模版,比如新浪微博中的 "@XXX"
格式的超鏈接跳轉(zhuǎn)等,都可以通過自定義正則表達(dá)式來匹配處理。
16 禁止截屏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE)
設(shè)置安全窗口,禁用系統(tǒng)截屏。防止App中的一些界面被截屏,并顯示在其他設(shè)備中造成信息泄漏。
比如支付寶App的“向商家付款”的包含付款二維碼的界面。(補(bǔ)充說明一點(diǎn),微信付款界面不是這么做的,采用的是在onResume()
生命周期方法中實(shí)時(shí)刷新付款二維碼,與支付寶在安全方法采取的手段不同。)
17 攔截Bac 鍵,使App進(jìn)入后臺(tái)而不是關(guān)閉
@Override
public void onBackPressed() {
Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
launcherIntent.addCategory(Intent.CATEGORY_HOME);
startActivity(launcherIntent);
}
使用Back鍵返回桌面,但不關(guān)閉當(dāng)前應(yīng)用,而是使之進(jìn)入后臺(tái),就像按下Home鍵一樣。
這個(gè)技巧厲害了。通常為了防止出現(xiàn)用戶誤按Back鍵退出App的情況,我們會(huì)在應(yīng)用首頁的Activity
中監(jiān)聽返回鍵操作,使用Toast
弱提示甚至Dialog
強(qiáng)提示的方式給到用戶一個(gè)再次確認(rèn)的操作,但無法阻止用戶通過返回鍵逐步關(guān)閉應(yīng)用。
然而,如果用這個(gè)方法攔截 App 最后一個(gè) Activity
(常見為首頁界面),既沒有阻礙用戶操作(回到桌面),又沒有關(guān)閉掉我們的應(yīng)用(后臺(tái)運(yùn)行中),間接提高 App 的存活時(shí)間,真乃暗度陳倉。并且據(jù)我實(shí)驗(yàn),微信、支付寶、微博等 App 都是這么做的,大家不妨一試。
另外其他幾種方法
- finish(); 正常退出遵循android系統(tǒng)規(guī)則
- android.os.Process.killProcess(android.os.Process.myPid());
- System.exit(0); java退出機(jī)制
- moveTaskToBack(true); 退出程序后臺(tái)?;?/em>
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
if ((System.currentTimeMillis() - exitTime) > 2000) {
ToastUtil.getInstace(getActivity()).show("再按一次退出程序");
exitTime = System.currentTimeMillis();
} else {
finish();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
moveTaskToBack(true);
}
}
return true;
}
return super.onKeyDown(keyCode, event);
}
18 縮略圖工具類ThumbnailUtils
縮略圖工具類,可以根據(jù)本地視頻文件源、Bitmap 對(duì)象生成縮略圖,常用的公共靜態(tài)方法為:
- createVideoThumbnail(String filePath, int kind)
- extractThumbnail(Bitmap source, int width, int height)
19 bitmap.extractAlpha()
從源bitmap
中根據(jù)alpha
獲取一個(gè)新的bitmap
對(duì)象。比較繞口,通常App中的Icon多數(shù)是純色透明像素背景組成,利用這個(gè)方法可以對(duì)該圖的非透明區(qū)域著色,有多種使用場(chǎng)景,常見如Button的pressed
狀態(tài),View 的陰影狀態(tài)等。舉個(gè)例子:
private static Bitmap getDropShadow(ImageView iv, Bitmap src, float radius, int color) {
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
final int width = src.getWidth(), height = src.getHeight();
final Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(dest);
final Bitmap alpha = src.extractAlpha();
canvas.drawBitmap(alpha, 0, 0, paint);
final BlurMaskFilter filter = new BlurMaskFilter(radius, BlurMaskFilter.Blur.OUTER);
paint.setMaskFilter(filter);
canvas.drawBitmap(alpha, 0, 0, paint);
iv.setImageBitmap(dest);
return dest;
}
20 ArgbEvaluator
系統(tǒng)提供的一個(gè)TypeEvaluator
,我們只需要提供兩個(gè)起始顏色值和一個(gè)分值,系統(tǒng)會(huì)通過特定的算法計(jì)算得出一個(gè)新的顏色中間值。利用這個(gè)類,我們至少可以做兩件事情。
第一,用于屬性動(dòng)畫中。由于其實(shí)現(xiàn)了TypeEvaluator
接口,可以用來做自定義屬性動(dòng)畫的求值器,改變 View 的顯示狀態(tài)。比如:
int colorStart = ContextCompat.getColor(this, R.color.black);
int colorEnd = ContextCompat.getColor(this, R.color.green);
ValueAnimator valueAnimator = ValueAnimator
.ofObject(new ArgbEvaluator(), colorStart, colorEnd)
.setDuration(3000);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
textView.setTextColor((Integer) animation.getAnimatedValue());
}
});
valueAnimator.start();
第二,利用該類提供的顏色求值算法,配合ViewPager
提供的滑動(dòng)偏離值使用。這種場(chǎng)景常見于使用ViewPager
實(shí)現(xiàn)的引導(dǎo)頁,其背景色隨著滑動(dòng)距離動(dòng)態(tài)改變;使用ViewPager
實(shí)現(xiàn)的Tab樣式菜單頁面,Tab 中文本內(nèi)容隨著滑動(dòng)距離動(dòng)態(tài)改變字體顏色(可以參考安卓版微信)。這兩種使用都使得ViewPager
頁面切換過渡得很自然,體驗(yàn)極佳。如:
viewPager.addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
new ArgbEvaluator().evaluate(positionOffset, startColor, endColor);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
另外,關(guān)于顏色差值的計(jì)算,Google Sample里有另一種算法,可參考SlidingTabStrip.java
文件源碼,核心方法內(nèi)容如下:
/**
* Blend {@code color1} and {@code color2} using the given ratio.
*
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
* 0.0 will return {@code color2}.
*/
private static int blendColors(int color1, int color2, float ratio) {
final float inverseRation = 1f - ratio;
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
return Color.rgb((int) r, (int) g, (int) b);
}
21 android:weightSum
用于LinearLayout
中,用于設(shè)置Childrenweight
的總比重。在LinearLayout
的children中,我們經(jīng)常會(huì)使用android:layout_weight
按比例分配容器布局的空間,但有時(shí)候不一定會(huì)分完。以往,有些朋友可能會(huì)使用一個(gè)空 放在最后來達(dá)到末尾占位效果。如果你知道這個(gè)屬性的話,就能少寫一些代碼。
22 android:descendantFocusability
用于ViewGroup
中,解決作為Parent
的ViewGroup
與Children View
之間的焦點(diǎn)占用問題。最最常見的使用場(chǎng)景就是 list item 中含有一些點(diǎn)擊效果的控件,比如 Button
、CheckBox
等,相信大家都遇到過。取值有三種,含義就不用再多說了:
- afterDescendants viewgroup只有當(dāng)其子類控件不需要獲取焦點(diǎn)時(shí)才獲取焦點(diǎn)
- beforeDescendants viewgroup會(huì)優(yōu)先其子類控件而獲取到焦點(diǎn)
- blocksDescendants viewgroup會(huì)覆蓋子類控件而直接獲得焦點(diǎn)
23 android:duplicateParentState
是否將View
自身的drawable state
交給直接parent ViewGroup
控制,值為boolean
類型。比如有一個(gè)item
布局, item
中有一個(gè)button
,如果點(diǎn)擊item layout
時(shí),需要button
呈現(xiàn)對(duì)應(yīng)的點(diǎn)擊效果,就可以在button
中用到這個(gè)屬性。不過,從設(shè)計(jì)的角度來講,這種場(chǎng)景還是比較少見的。知道有這個(gè)屬性就好,不推薦這種交互設(shè)計(jì)。
24 android:fillViewport
ScrollView
的一個(gè)屬性,用于設(shè)置內(nèi)容部分是否填滿屏幕,主要針對(duì)內(nèi)容不足以填滿屏幕的情況。
25 android:adjustViewBounds
使用ImageView
時(shí),你可能會(huì)用android:scaleType
屬性設(shè)置圖片縮放方式。殊不知,android:adjustViewBounds
屬性也能起到類似的效果。但要注意的是,后者需要至少指定ImageView
寬高中的一個(gè)屬性,或者maxHeight
之類的,然后另一個(gè)屬性隨之適配。這個(gè)屬性用在列表中較為合適,比如App中的活動(dòng)列表頁面,圖片寬度設(shè)置為match_parent
,然后高度設(shè)為wrap_content
使其自適應(yīng),這樣便能保證從服務(wù)獲取的高分辨率圖片在不同的屏幕中不被拉伸變形。(備注:最好在項(xiàng)目資源文件中放置一個(gè)與網(wǎng)絡(luò)圖片相同尺寸的默認(rèn)圖,起到placeholder
作用,避免圖片顯示前高度為 0 的較差體驗(yàn)。)
26 Android動(dòng)畫——布局動(dòng)畫、轉(zhuǎn)場(chǎng)動(dòng)畫
布局動(dòng)畫的作用于ViewGroup
,執(zhí)行動(dòng)畫效果的是內(nèi)部的子View
。布局動(dòng)畫在android中可以通過LayoutAnimation
或LayoutTransition
來實(shí)現(xiàn)。
- LayoutAnimation
LayoutAnimation
實(shí)際上是一個(gè)View
動(dòng)畫,用來控制子View顯示時(shí)的動(dòng)畫效果。可以通過Java代碼或者Xml文件來定義LayoutAnimation
動(dòng)畫。
使用java代碼:
private void setLayoutAnimation() {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.layout_item_anim_set);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
mListView.setLayoutAnimation(controller);
}
使用xml:
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/layout_item_anim_set"
android:animationOrder="normal"
android:delay="0.5">
</layoutAnimation>
在ViewGroup
所在布局中調(diào)用:
android:layoutAnimation="@anim/layout_anim"
- LayoutTransition
LayoutTransition
用于在ViewGroup
中有子View添加、刪除、隱藏、顯示時(shí)所有子View動(dòng)畫效果。LayoutTransition
有5中動(dòng)畫變化形式
- LayoutTransition.APPEARING:子View添加到容器中時(shí)的動(dòng)畫效果
- LayoutTransition.CHANGE_APPEARING:子View添加到容器中時(shí),其他子View位置改變的動(dòng)畫效果
- LayoutTransition.DISAPPEARING:子View被移除時(shí)的動(dòng)畫效果
- LayoutTransition.CHANGE_DISAPPEARING:子View被移除時(shí),其他子View的動(dòng)畫效果
- LayoutTransition.CHANGING:子View在容器中位置變化時(shí)其他子View的動(dòng)畫效果
使用默認(rèn)的動(dòng)畫樣式:
只需要在使用的LinearLayout
、FrameLayout
、RelativeLayout
等ViewGroup容器的布局文件中添加android:animateLayoutChanges="true"
即可,系統(tǒng)會(huì)使用默認(rèn)的LayoutTransition
來實(shí)現(xiàn)子View添加、刪除或變化是的動(dòng)畫效果。
27 Handler
- 定義
Handler
允許您發(fā)送和處理Message
和Runnable
與線程的MessageQueue
關(guān)聯(lián)的對(duì)象。每個(gè)Handler
實(shí)例與單個(gè)線程和該線程的MessageQueue
相關(guān)聯(lián)。當(dāng)你創(chuàng)建一個(gè)新的Handler
時(shí),它綁定到線程/正在創(chuàng)建它的線程的消息隊(duì)列 - 從那時(shí)起,它會(huì)將消息和可運(yùn)行文件傳遞到該消息隊(duì)列并執(zhí)行他們從消息隊(duì)列中出來。 - 處理程序有兩個(gè)主要用途
(1)調(diào)度Message
和Runnable
將在未來的某個(gè)時(shí)候執(zhí)行;
(2)入隊(duì)在不同于你自己的線程上執(zhí)行的動(dòng)作。
private static class MessageHandler extends Handler {
WeakReference<ImageLoadActivity> reference;
MessageHandler(ImageLoadActivity activity) {
reference = new WeakReference<>(activity);
}
// 處理消息
@Override
public void handleMessage(android.os.Message msg) {
final ImageLoadActivity activity = reference.get();
switch (msg.what) {
case 0:
Toast.makeText(activity, R.string.clear, Toast.LENGTH_LONG).show();
break;
}
super.handleMessage(msg);
}
}
// 創(chuàng)建
MessageHandler handler = new MessageHandler(this);
// 發(fā)送消息
new Thread(() -> {
Glide.get(this).clearDiskCache(); // 清理磁盤緩存
handler.sendEmptyMessage(0);
}).start();
28 Looper
- 定義
用于為線程運(yùn)行消息循環(huán)的類。 線程默認(rèn)情況下沒有與他們相關(guān)聯(lián)的消息循環(huán); 創(chuàng)建一個(gè),調(diào)用Looper.prepare()
在線程中運(yùn)行循環(huán),然后調(diào)用Looper.loop()
讓它處理消息,直到循環(huán)停止。大多數(shù)與消息循環(huán)的交互是通過的Handler
類。
class LooperThread extends Thread {
Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
- Looper主要作用:
1、與當(dāng)前線程綁定,保證一個(gè)線程只會(huì)有一個(gè)Looper實(shí)例,同時(shí)一個(gè)Looper實(shí)例也只有一個(gè)MessageQueue。
2、loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。
好了,我們的異步消息處理線程已經(jīng)有了消息隊(duì)列(MessageQueue),也有了在無限循環(huán)體中取出消息的哥們,現(xiàn)在缺的就是發(fā)送消息的對(duì)象了,于是乎:Handler登場(chǎng)了。
29 Message
定義
定義一個(gè)包含可以是描述和任意數(shù)據(jù)對(duì)象的消息發(fā)送到Handler
。 這個(gè)對(duì)象包含兩個(gè)額外的int
字段和一個(gè)
額外的object
字段允許您在許多情況下不進(jìn)行分配。雖然Message
的構(gòu)造函數(shù)是公開的,但最好的方式是獲取其中之一是調(diào)用Message.obtain()
或其中一個(gè)Handler.obtainMessage()
方法,這將拉他們來自一個(gè)循環(huán)再造的物體池。問題
2.1 一個(gè)線程是否只有一個(gè)Looper?
2.2 如何保證一個(gè)線程只有一個(gè)Looper?
30 android子線程更新UI
android提供以下幾種方法在主線程更新ui
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
- Handler
31 關(guān)于Handler,Looper,Message總結(jié)
- 首先Looper.prepare()在本線程中保存一個(gè)Looper實(shí)例,然后該實(shí)例中保存一個(gè)MessageQueue對(duì)象;因?yàn)長(zhǎng)ooper.prepare()在一個(gè)線程中只能調(diào)用一次,所以MessageQueue在一個(gè)線程中只會(huì)存在一個(gè)。
- Looper.loop()會(huì)讓當(dāng)前線程進(jìn)入一個(gè)無限循環(huán),不端從MessageQueue的實(shí)例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法。
- Handler的構(gòu)造方法,會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例,進(jìn)而與Looper實(shí)例中的MessageQueue想關(guān)聯(lián)。
- Handler的sendMessage方法,會(huì)給msg的target賦值為handler自身,然后加入MessageQueue中。
- 在構(gòu)造Handler實(shí)例時(shí),我們會(huì)重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法。
- 好了,總結(jié)完成,大家可能還會(huì)問,那么在Activity中,我們并沒有顯示的調(diào)用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功創(chuàng)建呢,這是因?yàn)樵贏ctivity的啟動(dòng)代碼中,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()和Looper.loop()方法