View
自定義View中在onDraw()方法中可以設(shè)置padding嗎?
答案是不能,設(shè)置padding后饲握,View的布局改變宾添,會(huì)重新進(jìn)行measure,layout流程糖赔,然后draw,從而陷入死循環(huán),導(dǎo)致內(nèi)存溢出或泄漏穗慕;
- 面試題
- 接上面
- 圖解 Android 事件分發(fā)機(jī)制(推薦,講解詳細(xì))
- Android View 事件分發(fā)機(jī)制 源碼解析 (上)
- Android ViewGroup事件分發(fā)機(jī)制
自定義View
- Android 之 自定義View全解
- Android View的繪制流程
- 自定義View之總結(jié)
- [Android技術(shù)專題]自定義View從入門到上天
- 教你步步為營掌握自定義View
Android緩存策略
Android 之 Bitmap
一逛绵、加載Bitmap
BitmapFactory類提供了四類方法用來加載Bitmap:
1、
decodeFile(...)
通過圖片路徑加載倔韭,同時(shí)可以選擇是否設(shè)置options术浪,不設(shè)置則采用默認(rèn)options。
例子:
Bitmap bm = BitmapFactory.decodeFile(sd_path)
采用默認(rèn)options
Bitmap bm = BitmapFactory.decodeFile(sd_path,options)
2寿酌、
decodeResource(...)
通過傳入Resource對(duì)象和R.drawable.xxx
形式加載添吗。
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.aaa);
默認(rèn)options
3、
decodeStream(...)
通過輸入流加載
Bitmap bm = BitmapFactory.decodeStream(stream)
,這是一個(gè)耗時(shí)操作份名,要在子線程中執(zhí)行
4碟联、
decodeByteArray(...)
從字節(jié)數(shù)組中加載。通過講輸入流inputstream轉(zhuǎn)換成byte[]字節(jié)數(shù)組加載僵腺。
Bitmap bm = BitmapFactory.decodeByteArray(myByte,0,myByte.length);
** 注:**decodeFile和decodeResource間接調(diào)用decodeStream方法鲤孵。
二、高效加載bitmap
如果圖片過大辰如,直接通過BitmapFactory
加載普监,容易出現(xiàn)內(nèi)存溢出。這樣就需要采取一定策略來加載所需的圖片琉兜。主要就是通過BitmapFactory
內(nèi)部的一個(gè)內(nèi)部類Options
來實(shí)現(xiàn)凯正。
尺寸壓縮 是壓縮圖片的像素,一張圖片所占內(nèi)存的大小 圖片類型*寬*高豌蟋,通過改變?nèi)齻€(gè)值減小圖片所占的內(nèi)存廊散,防止OOM,當(dāng)然這種方式可能會(huì)使圖片失真 梧疲。這是必然的取舍允睹。
設(shè)置Options运准,主要是設(shè)置圖片色彩模式,采樣率來實(shí)現(xiàn)缭受。
** Android圖片色彩模式分類:**
**Bitmap.Config.ALPHA_8*
:**每個(gè)像素占用1byte內(nèi)存胁澳。顏色信息只由透明度組成,占8位米者。
Bitmap.Config.ARGB_4444
:每個(gè)像素占用2byte內(nèi)存韭畸。顏色信息由透明度與R(Red),G(Green)蔓搞,B(Blue)四部分組成胰丁,每個(gè)部分都占4位,總共占16位败明。
Bitmap.Config.ARGB_8888
:每個(gè)像素占用4byte內(nèi)存隘马。顏色信息由透明度與R(Red),G(Green)妻顶,B(Blue)四部分組成酸员,每個(gè)部分都占8位,總共占32位讳嘱。是Bitmap默認(rèn)的顏色配置信息幔嗦,也是最占空間的一種配置。
Bitmap.Config.RGB_565
:每個(gè)像素占用2byte內(nèi)存沥潭。顏色信息由R(Red)邀泉,G(Green),B(Blue)三部分組成钝鸽,R占5位汇恤,G占6位,B占5位拔恰,總共占16位因谎。Android默認(rèn)的色彩模式為ARGB_8888,這個(gè)色彩模式色彩最細(xì)膩颜懊,顯示質(zhì)量最高财岔。但同樣的,占用的內(nèi)存也最大河爹。
BitmapFactory.Options
的inPreferredConfig
參數(shù)可以 指定decode到內(nèi)存中匠璧,手機(jī)中所采用的編碼,可選值定義在Bitmap.Config
中咸这。缺省值是ARGB_8888夷恍。
采樣率:inSampleSize的值必須大于1時(shí)才會(huì)有效果,且采樣率同時(shí)作用于寬和高炊苫;當(dāng)inSampleSize=1時(shí)裁厅,采樣后的圖片為圖片的原始大小冰沙。當(dāng)inSampleSize=2時(shí)侨艾,采樣后的圖片的寬高均為原始圖片寬高的1/2执虹,這時(shí)像素為原始圖片的1/(2x2),占用內(nèi)存也為原始圖片的1/(2x2);inSampleSize的取值應(yīng)該總為2的整數(shù)倍,否則會(huì)向下取整唠梨,取一個(gè)最接近2的整數(shù)倍袋励,比如inSampleSize=3時(shí),系統(tǒng)會(huì)取inSampleSize=2当叭。
假設(shè)一張1024x1024茬故,模式為ARGB_8888的圖片,inSampleSize=2,原始占用內(nèi)存大小是4MB蚁鳖,采樣后的圖片占用內(nèi)存大小就是(1024/2) x (1024/2 )x4 = 1MB磺芭。具體的采樣步驟可以參考Android 之 Bitmap
** 優(yōu)點(diǎn):** 效率較高,解析速度快
缺點(diǎn):采樣率inSampleSize的取值只能是2的次方數(shù)(例如:inSampleSize=15,實(shí)際取值為8;inSampleSize=17,實(shí)際取值為16;實(shí)際取值會(huì)往2的次方結(jié)算),因此該方法不能精確的指定圖片的大小
三、Bitmap 注意事項(xiàng)
1醉箕、不用的bitmap即使釋放
if (!bmp.isRecycled()) {
bmp.recycle(); //回收?qǐng)D片所占的內(nèi)存
bitmap = null;
system.gc(); //提醒系統(tǒng)及時(shí)回收
}
2钾腺、捕獲OutOfMemoryError
bitmap在實(shí)例化的過程中是很耗內(nèi)存的。很容易出現(xiàn)OutOfMemery內(nèi)存溢出的情況讥裤。而且一出現(xiàn)程序就會(huì)crash放棒。所以,需要對(duì)bitmap的實(shí)例化的時(shí)做OutOfMemoryError捕獲己英,需要注意的是OutOfMemoryError并不是異常而是錯(cuò)誤间螟。一般情況下java中異常是可以捕獲的。而錯(cuò)誤是不可以的损肛,因?yàn)镋rror的出現(xiàn)一般情況下程序就會(huì)終止厢破。OutOfMemoryError比較特殊。
Bitmap bitmap = null;
try {
// 實(shí)例化Bitmap
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
// 如果實(shí)例化失敗 返回默認(rèn)的Bitmap對(duì)象
return defaultBitmapMap;
}
**3治拿、緩存通用的bitmap對(duì)象** 在加載用戶頭像的時(shí)候摩泪,如果用戶沒有上傳的頭像,一般會(huì)加載一個(gè)默認(rèn)頭像忍啤。而用戶的默認(rèn)頭像又是一樣的加勤,所以,對(duì)于相同頭像的bitmap應(yīng)該做緩存處理同波。`如果不進(jìn)行緩存鳄梅,盡管看到的是同一張圖片文件,但是使用BitmapFactory類的方法來實(shí)例化出來的Bitmap未檩,是不同的Bitmap對(duì)象戴尸。緩存可以避免新建多個(gè)Bitmap對(duì)象,避免內(nèi)存的浪費(fèi)冤狡。`
4孙蒙、圖片質(zhì)量壓縮
上述用inSampleSize壓縮是尺寸壓縮项棠,Android中還有一種壓縮方式叫質(zhì)量壓縮。質(zhì)量壓縮是在保持像素的前提下改變圖片的位深及透明度等挎峦,來達(dá)到壓縮圖片的目的香追,經(jīng)過它壓縮的圖片文件大小(kb)會(huì)有改變,但是導(dǎo)入成bitmap后占得內(nèi)存是不變的坦胶,寬高也不會(huì)改變透典。因?yàn)橐3窒袼夭蛔儯运蜔o法無限壓縮顿苇,到達(dá)一個(gè)值之后就不會(huì)繼續(xù)變小了峭咒。顯然這個(gè)方法并不適用與縮略圖,其實(shí)也不適用于想通過壓縮圖片減少內(nèi)存的適用纪岁,僅僅適用于想在保證圖片質(zhì)量的同時(shí)減少文件大小的情況而已
凑队。
private void compressImage(Bitmap image, int reqSize) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 質(zhì)量壓縮方法,這里100表示不壓縮幔翰,
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
>
// 循環(huán)判斷壓縮后的圖片是否大于reqSize漩氨,大于則繼續(xù)壓縮
while (baos.toByteArray().length / 1024 > reqSize) {
baos.reset();//清空baos
// 這里壓縮options,把壓縮后的數(shù)據(jù)放到baos中
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
// 把壓縮后的baos放到ByteArrayInputStream中
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
//decode圖片
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); }
**?四导匣、Android加載大量圖片內(nèi)存溢出解決方案 **
①才菠、在加載圖片的時(shí)候,盡量不要直接使用
setImageBitmap
或setImageResource
或BitmapFactory.decodeResource
來設(shè)置一張大圖贡定,因?yàn)檫@些函數(shù)在完成decode后赋访,最終都是通過java層的createBitmap來完成的,需要消耗更多內(nèi)存缓待,可以通過BitmapFactory.decodeStream
方法蚓耽,創(chuàng)建出一個(gè)bitmap,再將其設(shè)為ImageView的 source
②旋炒、使用BitmapFactory.Options
對(duì)圖片進(jìn)行壓縮
③步悠、運(yùn)用Java軟引用,進(jìn)行圖片緩存瘫镇,將需要經(jīng)常加載的圖片放進(jìn)緩存里鼎兽,避免反復(fù)加載
**五、其他 **
圖片的旋轉(zhuǎn)铣除,合成谚咬,圓角,縮放尚粘,裁剪择卦,bitmap與drawable互相轉(zhuǎn)換,以及保存到sd卡的相關(guān)操作參考Android 之 Bitmap
WebView
WebView的實(shí)現(xiàn)主要依靠WebView和WebSettings這兩個(gè)類來實(shí)現(xiàn)。WebView提供容器秉继,WebSetting設(shè)置WebView支持的屬性祈噪。
WebView使用過程中需要注意的地方:
1、在實(shí)例化WebView的時(shí)候盡量不要使用當(dāng)前Activity的引用尚辑。用代碼New一個(gè)WebView而不是在XML中靜態(tài)寫入辑鲤。有人利用LeakCanary檢測(cè)過傳入當(dāng)前Activity引用時(shí)是否會(huì)出現(xiàn)內(nèi)存泄露,結(jié)果是沒有腌巾。接著換成Application傳入遂填,與之前傳入的Activity引用進(jìn)行對(duì)比發(fā)現(xiàn)铲觉,雖然兩者都不會(huì)造成內(nèi)存泄露澈蝙,但是使用Application要使用Activity時(shí)所消耗的內(nèi)存少20~30MB。所以撵幽,建議直接使用Application灯荧。
即WebView實(shí)例化的時(shí)候不要采用這種方式WebView webView = new WebView(?this);
應(yīng)該采用這種方式WebView webView = new WebView(App.getContext());
而是采用動(dòng)態(tài)加載的方式:
private WebView webView;
webView = new WebView(App.getContext());
//一定要設(shè)置WebView的LayoutParams,并且值MATCH_PARENT盐杂。否則的話就會(huì)出現(xiàn)有的網(wǎng)頁無法加載的情況
webView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
binding.webviewContainer.addView(webView);
**2逗载、WebView資源的釋放**。頁面銷毀之前勿忘釋放WebView資源链烈。具體釋放規(guī)則可以看下面這段代碼
private void clearWebViewResource(){
if (webView != null){
webView.removeAllViews();
//在5.1上如果不加上這句話就會(huì)出現(xiàn)內(nèi)存泄露厉斟。這是5.1的bug
// mComponentCallbacks導(dǎo)致的內(nèi)存泄漏
((ViewGroup)webView.getParent()).removeView(webView);
webView.setTag(null);
webView.clearHistory();
webView.destroy();
webView = null;
}
}
基于上述寫的Demo示例: [Demo](http://blog.csdn.net/loveyaozu/article/details/52933897)
Android res目錄下的raw和assets
**assets **資源目錄或者叫資產(chǎn)目錄,里面存放的是無法直接訪問的原生資源强衡。與res屬于同級(jí)目錄擦秽。應(yīng)用程序需要通過AssetsManager以二進(jìn)制流的形式讀取文件。應(yīng)用程序編譯的時(shí)候不會(huì)在R類中為assets目錄下的文件創(chuàng)建索引漩勤。
raw在res目錄下(res/raw)也是用于存放一些資源文件的感挥。應(yīng)用程序編譯的時(shí)候raw目錄下的資源文件會(huì)在R類中生成索引
res/raw與assets的比較
相同點(diǎn):
都是用于存放資源文件的。兩者目錄下的文件在打包后會(huì)原封不動(dòng)的保存在apk包中越败,不會(huì)被編譯成二進(jìn)制触幼。
不同點(diǎn):
1、編譯的時(shí)候res/raw目錄下的文件會(huì)在R類中生成索引究飞,訪問的時(shí)候直接使用資源ID即R.id.filename置谦;assets目錄下的文件則不會(huì),訪問的時(shí)候需要通過AssetManager亿傅。
2媒峡、res/raw下不能再有下級(jí)目錄;而assets則可以有袱蜡。res/raw與assets目錄下文件的讀取
1丝蹭、res/raw目錄下文件訪問方式:
InputStream is = getResources().openRawResource(R.raw.filename);
2、assets目錄下文件的讀取方式:
AssetManager am = getResources().getAssets();
InputStream is = am.open("filename.xxx");
或者直接這樣
InputStream is = getResources().getAssets().open("filename.xxx");
Android對(duì)assets和raw目錄下文件大小的限制
android 2.3之前在讀取這兩個(gè)資源文件夾中的文件時(shí)會(huì)有一定的限制,即單個(gè)文件大小不能超過1M 奔穿,如果讀取超過1M的文件會(huì)報(bào) "Data exceeds UNCOMPRESS_DATA_MAX (1314625 vs 1048576)" 的IOException镜沽。
Android文件路徑
SD卡讀寫需要權(quán)限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
// SD卡是否可用
boolean isSDCardAvailable = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
if (isSDCardAvailable){
//目錄 /storage/emulated/0
String path = Environment.getExternalStorageDirectory().getPath();
//SD卡上面的緩存目錄 /cache
String cachePath = Environment.getDownloadCacheDirectory().getPath();
}
//目錄 /data/data/packageName/filesString
fileDir = getFilesDir().getPath();
//目錄 /data/data/packageName/cacheString
cachePath = getCacheDir().getPath();
MVC,MVP 和 MVVM 設(shè)計(jì)模式
Sqlite數(shù)據(jù)庫
1.面試題
** 注意**:數(shù)據(jù)庫的增刪改操作最好加上事務(wù)缅茉,根據(jù)測(cè)試發(fā)現(xiàn)開啟事務(wù)比不開啟事務(wù)的時(shí)候數(shù)據(jù)插入的效率會(huì)高很多,特別是批量插入的時(shí)候男摧。
關(guān)于Sqlite的一些細(xì)節(jié):
1蔬墩、對(duì)于數(shù)據(jù)的批量插入,數(shù)據(jù)的修改與刪除建議加上事務(wù)耗拓。有人做過測(cè)試拇颅,批量插入1000條數(shù)據(jù)的時(shí)候,使用事務(wù)和不使用事務(wù)的差別很大乔询。使用事務(wù)的批量插入效率比不使用事務(wù)的批量插入要高的多樟插。使用事務(wù)還有一點(diǎn)好處就是保證每次事務(wù)操作的原子性。出現(xiàn)錯(cuò)誤自動(dòng)回滾竿刁。
2黄锤、索引的使用。如果應(yīng)用中查詢操作量級(jí)較大食拜,業(yè)務(wù)對(duì)要求查詢要求較高的可以使用索引鸵熟。
其他情況不要輕易使用。因?yàn)槭挛锒加袃擅妗?/p>
- ①负甸、對(duì)于數(shù)據(jù)的增刪流强,索引會(huì)?降低增刪的效率,使用了索引會(huì)變慢惑惶,比如你想要?jiǎng)h除字典中的一個(gè)字煮盼,那么你同時(shí)也需要?jiǎng)h除這個(gè)字在拼音索引和部首索引中的信息。
- ②带污、建立索引會(huì)增加數(shù)據(jù)庫的大小僵控,比如字典中的拼音索引和部首索引實(shí)際上是會(huì)增加字典的頁數(shù),讓字典變厚的鱼冀。
3报破、大批量數(shù)據(jù)的插入或者刪除的時(shí)候盡量開啟新的線程。數(shù)據(jù)量過大千绪,在插入或者查詢的時(shí)候耗時(shí)就會(huì)比較大充易。如果在UI線程中執(zhí)行容易出現(xiàn)ANR。
4荸型、SQLiteDatabase的引用盹靴。在多線程中只使用一個(gè)SQLiteDatabase引用,在用SQLiteDataBase.close()的時(shí)需要注意調(diào)是否還有別的線程在使用這個(gè)實(shí)例。如果一個(gè)線程操作完成后就直接close了稿静,別一個(gè)正在使用這個(gè)數(shù)據(jù)庫的線程就會(huì)異常梭冠。
解決辦法:
- ①、將SQLiteDatabase實(shí)例放在Application中改备,使其生命周期和App一致控漠。
- ②、采用計(jì)數(shù)器的方式悬钳,在Application中添加一個(gè)線程安全的計(jì)數(shù)器盐捷,每次數(shù)據(jù)庫操作完成后檢查一下計(jì)數(shù)是否為0。為0就關(guān)閉SQLiteDatabase默勾,不為0就不關(guān)閉碉渡。
5、數(shù)據(jù)查詢時(shí)對(duì)cursor的遍歷灾测。
?遍歷cursor時(shí)爆价,我們通常的做法是這樣:
private void badQueryWithLoop(SQLiteDatabase db) {
Cursor cursor = db.query(TableDefine.TABLE_RECORD, new String[]{TableDefine.COLUMN_INSERT_TIME}, null, null, null, null, null) ;
while (cursor.moveToNext()) {
long insertTime = cursor.getLong(cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME));
}
}
如果我們將獲取ColumnIndex的操作提到循環(huán)之外,效果會(huì)更好一些
private void goodQueryWithLoop(SQLiteDatabase db) {
Cursor cursor = db.query(TableDefine.TABLE_RECORD, new String[]{TableDefine.COLUMN_INSERT_TIME}, null, null, null, null, null) ;
int insertTimeColumnIndex = cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME);
while (cursor.moveToNext()) {
long insertTime = cursor.getLong(insertTimeColumnIndex);
}
cursor.close();
}
**6媳搪、ContentValues容量調(diào)整** ContentValues內(nèi)部采用了HashMap來存儲(chǔ)Key-Value數(shù)據(jù),ContentValues的初始容量是8骤宣,如果當(dāng)添加的數(shù)據(jù)超過8之前秦爆,則會(huì)進(jìn)行雙倍擴(kuò)容操作,因此建議對(duì)ContentValues填入的內(nèi)容進(jìn)行估量憔披,設(shè)置合理的初始化容量等限,減少不必要的內(nèi)部擴(kuò)容操作。
事務(wù)事例:
public void insertStudent(Student student){
if (student == null){
return;
}
SQLiteDatabase database = null;
try {
database = dbHelper.getReadableDatabase();
database.beginTransaction();//開啟事務(wù)
ContentValues cv = new ContentValues();
cv.put(StudentColumn.NAME,student.getName());
cv.put(StudentColumn.SEX,student.getSex());
cv.put(StudentColumn.STUDENT_NUM,student.getStudentNum());
database.insertOrThrow(TableHelpter.TB_STUDENT,null,cv);
database.setTransactionSuccessful(); //操作執(zhí)行完成將事務(wù)設(shè)置成功芬膝,這一句必須要有望门。否則的話,在關(guān)閉事務(wù)的時(shí)候會(huì)回滾結(jié)果不提交锰霜。
}catch (Exception e){
e.printStackTrace();
} finally {
database.endTransaction();//數(shù)據(jù)庫關(guān)閉前關(guān)閉事務(wù)
if (database != null){
database.close();
}
}
}
補(bǔ)充:數(shù)據(jù)庫中事務(wù)的四大特性
1筹误、原子性
原子性是指事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾癣缅。因此事務(wù)的操作如果成功就必須要完全應(yīng)用到數(shù)據(jù)庫厨剪,如果操作失敗則不能對(duì)數(shù)據(jù)庫有任何影響。
2友存、一致性
一致性是指事務(wù)必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài)祷膳,也就是說一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。
拿轉(zhuǎn)賬來說屡立,假設(shè)用戶A和用戶B兩者的錢加起來一共是5000直晨,那么不管A和B之間如何轉(zhuǎn)賬,轉(zhuǎn)幾次賬,事務(wù)結(jié)束后兩個(gè)用戶的錢相加起來應(yīng)該還得是5000勇皇,這就是事務(wù)的一致性奕巍。
3、隔離性
隔離性是當(dāng)多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫時(shí)儒士,比如操作同一張表時(shí)的止,數(shù)據(jù)庫為每一個(gè)用戶開啟的事務(wù),不能被其他事務(wù)的操作所干擾着撩,多個(gè)并發(fā)事務(wù)之間要相互隔離诅福。
即要達(dá)到這么一種效果:對(duì)于任意兩個(gè)并發(fā)的事務(wù)T1和T2,在事務(wù)T1看來拖叙,T2要么在T1開始之前就已經(jīng)結(jié)束氓润,要么在T1結(jié)束之后才開始,這樣每個(gè)事務(wù)都感覺不到有其他事務(wù)在并發(fā)地執(zhí)行薯鳍。
4咖气、持久性
持久性是指一個(gè)事務(wù)一旦被提交了,那么對(duì)數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的挖滤,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會(huì)丟失提交事務(wù)的操作崩溪。
數(shù)據(jù)庫并發(fā)帶來的問題
由于事務(wù)具有隔離性,即事務(wù)之間互不影響斩松。當(dāng)數(shù)據(jù)庫出現(xiàn)并發(fā)訪問的時(shí)候就會(huì)帶來一些列的問題伶唯。
1、臟讀
臟讀是指在一個(gè)事務(wù)處理過程里讀取了另一個(gè)未提交的事務(wù)中的數(shù)據(jù)惧盹。
2乳幸、不可重復(fù)讀
不可重復(fù)讀是指在對(duì)于數(shù)據(jù)庫中的某個(gè)數(shù)據(jù),一個(gè)事務(wù)范圍內(nèi)多次查詢卻返回了不同的數(shù)據(jù)值钧椰,這是由于在查詢間隔粹断,被另一個(gè)事務(wù)修改并提交了。
例如事務(wù)T1在讀取某一數(shù)據(jù)嫡霞,而事務(wù)T2立馬修改了這個(gè)數(shù)據(jù)并且提交事務(wù)給數(shù)據(jù)庫瓶埋,事務(wù)T1再次讀取該數(shù)據(jù)就得到了不同的結(jié)果,發(fā)送了不可重復(fù)讀秒际。
不可重復(fù)讀和臟讀的區(qū)別是悬赏,臟讀是某一事務(wù)讀取了另一個(gè)事務(wù)未提交的臟數(shù)據(jù),而不可重復(fù)讀則是讀取了前一事務(wù)提交的數(shù)據(jù)娄徊。
3闽颇、幻讀
幻讀是事務(wù)非獨(dú)立執(zhí)行時(shí)發(fā)生的一種現(xiàn)象。例如事務(wù)T1對(duì)一個(gè)表中所有的行的某個(gè)數(shù)據(jù)項(xiàng)做了從“1”修改為“2”的操作寄锐,這時(shí)事務(wù)T2又對(duì)這個(gè)表中插入了一行數(shù)據(jù)項(xiàng)兵多,而這個(gè)數(shù)據(jù)項(xiàng)的數(shù)值還是為“1”并且提交給數(shù)據(jù)庫尖啡。而操作事務(wù)T1的用戶如果再查看剛剛修改的數(shù)據(jù),會(huì)發(fā)現(xiàn)還有一行沒有修改剩膘,其實(shí)這行是從事務(wù)T2中添加的衅斩,就好像產(chǎn)生幻覺一樣,這就是發(fā)生了幻讀怠褐。
幻讀和不可重復(fù)讀都是讀取了另一條已經(jīng)提交的事務(wù)(這點(diǎn)就臟讀不同)畏梆,所不同的是不可重復(fù)讀查詢的都是同一個(gè)數(shù)據(jù)項(xiàng),而幻讀針對(duì)的是一批數(shù)據(jù)整體(比如數(shù)據(jù)的個(gè)數(shù))奈懒。
事務(wù)的隔離級(jí)別
現(xiàn)在來看看MySQL數(shù)據(jù)庫為我們提供的四種隔離級(jí)別:
① Serializable (串行化):可避免臟讀奠涌、不可重復(fù)讀、幻讀的發(fā)生磷杏。
② Repeatable read (可重復(fù)讀):可避免臟讀溜畅、不可重復(fù)讀的發(fā)生。
③ Read committed (讀已提交):可避免臟讀的發(fā)生极祸。
④ Read uncommitted (讀未提交):最低級(jí)別慈格,任何情況都無法保證。
以上四種隔離級(jí)別最高的是Serializable級(jí)別遥金,最低的是Read uncommitted級(jí)別浴捆,當(dāng)然級(jí)別越高,執(zhí)行效率就越低汰规。像Serializable這樣的級(jí)別汤功,就是以鎖表的方式(類似于Java多線程中的鎖)使得其他的線程只能在鎖外等待,所以平時(shí)選用何種隔離級(jí)別應(yīng)該根據(jù)實(shí)際情況溜哮。在MySQL數(shù)據(jù)庫中默認(rèn)的隔離級(jí)別為Repeatable read (可重復(fù)讀)。
在MySQL數(shù)據(jù)庫中色解,支持上面四種隔離級(jí)別茂嗓,默認(rèn)的為Repeatable read (可重復(fù)讀);而在Oracle數(shù)據(jù)庫中科阎,只支持Serializable (串行化)級(jí)別和Read committed (讀已提交)這兩種級(jí)別,其中默認(rèn)的為Read committed級(jí)別。
RecyclerView往枷,ListView菲驴,GridView
**1.ListView **
ListView 面試相關(guān):
1.如何加載不同類別的item?
BaseAdapter中有兩個(gè)方法错英,一個(gè)是
public int getItemViewType(int position)
返回當(dāng)前ItemView的類型入撒,默認(rèn)返回0。另一個(gè)方法是public int getViewTypeCount()
返回item view的類型個(gè)數(shù)椭岩,默認(rèn)返回1茅逮×模可以通過復(fù)寫這兩個(gè)方法,達(dá)到加載不同itemview的需求献雅。假設(shè)碉考,當(dāng)前有兩個(gè)類型的viewitem,一個(gè)是文本類型挺身,一個(gè)是圖片類型侯谁。我們就可以復(fù)寫getViewTypeCount
的時(shí)候返回2,然后通過復(fù)寫getItemViewType
章钾,根據(jù)position通過getItem得到當(dāng)前的object對(duì)象墙贱,然后根據(jù)這個(gè)object所對(duì)應(yīng)的類型(文本,圖片)返回對(duì)應(yīng)類型的int值伍玖。
2.listview的優(yōu)化
- 重用ConvertView(?getView在首次調(diào)用的時(shí)候嫩痰,convertView是null,所以這個(gè)時(shí)候就需要?jiǎng)?chuàng)建ItemView的layout窍箍。后續(xù)調(diào)用的時(shí)候就不需要了串纺,因?yàn)榇藭r(shí)的convertView已經(jīng)不為null了。所以椰棘,convertView可以復(fù)用纺棺。?不用每次都用inflate一下ItemView的layout布局,如果每次inflate邪狞,這樣會(huì)非常消耗性能祷蝌,尤其是item有成百上千個(gè)的時(shí)候。歸根結(jié)底就是為了
避免重復(fù)inflate ItemView的layout布局
) - 采用View Holder模式(目的是緩存ItemView的layout中子控件帆卓,不用每次都通過findViewById的方式去加載,而是從ViewHolder的緩存中獲取剑令,速度就會(huì)快很多糊啡。)
關(guān)于上面兩個(gè)原理,可以查看ListView常用知識(shí)點(diǎn)歸納
- 使用異步線程加載圖片(一般都是直接使用圖片庫加載吁津,如Glide, Picasso)棚蓄;
- 在adapter的getView方法中盡可能的減少邏輯判斷,特別是耗時(shí)的判斷碍脏;
- 避免GC(可以從LOGCAT查看有無GC的LOG)梭依;
- 在快速滑動(dòng)時(shí)不要加載圖片;ListView 快速滑動(dòng)不加載圖片
- 將ListView的scrollingCache和animateCache這兩個(gè)屬性設(shè)置為false(默認(rèn)是true);
- 盡可能減少List Item的Layout層次(如可以使用RelativeLayout替換LinearLayout典尾,或使用自定的View代替組合嵌套使用的Layout)役拴;
3.其他
ListView的item點(diǎn)擊事件和item中button點(diǎn)擊事件發(fā)送沖突
解決辦法:方法一,在ItemView配置的xml文件中的根節(jié)點(diǎn)添加屬性android:descendantFocusability="blocksDescendants"
?方法二急黎,在要添加事件的控件上添加android:focusable="false"
**2.GridView **
** 3.RecyclerView**
** RecyclerView與ListView對(duì)比:**
1扎狱、ListView只支持線性顯示規(guī)則侧到;RecyclerView支持多種顯示規(guī)則LinearLayoutManager
、GridLayoutManager
淤击、StaggeredGridLayoutManager
匠抗。分別是線性規(guī)則,網(wǎng)格和瀑布流污抬」常可以通過setLayoutManager設(shè)置。所以RecyclerView的更加靈活多變印机。
2矢腻、ListView可以添加頭和腳;RecyclerView不可以射赛。
3多柑、ListView的adapter需要繼承重寫 BaseAdapter 類,自定義 ViewHolder 和 convertView 一起完成復(fù)用優(yōu)化工作楣责。RecyclerView則繼承重寫 RecyclerView.Adapter 和 RecyclerView.ViewHolder竣灌,設(shè)置布局管理器,控制布局效果秆麸。
4初嘹、ListView只支持縱向滾動(dòng),RecyclerView支持橫向和縱向滾動(dòng)
5沮趣、ListView不支持局部刷新屯烦,
notifyDataSetChanged()
是整體刷新。RecyclerView可以通過notifyItemChanged(...)
刷新單個(gè)item房铭。
6驻龟、RecyclerView不支持
OnItemClickListener
和OnItemLongClickListener
事件。需要自己實(shí)現(xiàn)缸匪。
7迅脐、RecyclerView的動(dòng)畫效果比ListView更加豐富。
RecyclerView與ListView對(duì)比分析
RecyclerView 和 ListView 使用對(duì)比分析
ListView/RecyclerView通用adapter(實(shí)際開發(fā)中可以借鑒)
這兩篇博客是對(duì)上面通用adapter的實(shí)現(xiàn)介紹
CoordinatorLayout布局
- CoordinatorLayout的使用
- CoordinatorLayout——小試牛刀
- CoordinatorLayout——源碼分析
- CoordinatorLayout自定義Bahavior特效及其源碼分析
- Android 一步一步分析CoordinatorLayout.Behavior
Android內(nèi)存泄露與性能優(yōu)化
內(nèi)存泄露檢測(cè)工具
Android性能優(yōu)化方案
1.布局優(yōu)化
盡量減少layout層級(jí)豪嗽,減少界面繪制的工作量。
采用<include>,<merge>標(biāo)簽
2.自定義View的繪制
onDraw中不要?jiǎng)?chuàng)建大量的局部對(duì)象豌骏。因?yàn)閛nDraw方法會(huì)被頻繁調(diào)用龟梦,這樣就會(huì)在一瞬間產(chǎn)生大量的臨時(shí)對(duì)象,不僅會(huì)占用過多內(nèi)存還會(huì)導(dǎo)致系統(tǒng)頻繁GC窃躲,降低程序執(zhí)行效率计贰。
onDraw中不要做太多耗時(shí)才操作。
3.內(nèi)存優(yōu)化
靜態(tài)變量導(dǎo)致的內(nèi)存泄露
示例:一個(gè)外部的靜態(tài)Context變量引用了當(dāng)前的Activity實(shí)例蒂窒,當(dāng)Activity銷毀的時(shí)候無法被銷毀躁倒≤衽或者一個(gè)Activity的一個(gè)靜態(tài)Context變量,引用了當(dāng)前Activity秧秉,銷毀之前沒有釋放褐桌,導(dǎo)致Activity無法被銷毀。
解決方法:不要將Activty作為Context傳給外部Context變量象迎。如果外部需要傳入Context荧嵌,可以使用Application Context,因?yàn)锳pplication Context的生命周期與APP的生命周期一致砾淌。如果Activity內(nèi)部有靜態(tài)Context變量持有當(dāng)前Activity實(shí)例啦撮,在onDestroy的時(shí)候要將該變量持有Activity釋放。單例模式導(dǎo)致的內(nèi)存泄露
示例:?jiǎn)卫J絻?nèi)部的Context變量持有外部存入的Activity實(shí)例汪厨,在Activity銷毀之前沒有釋放操作赃春,導(dǎo)致Activity無法被銷毀,內(nèi)存無法回收劫乱。
解決辦法:如果單例模式需要Context的時(shí)候织中,可以通過調(diào)用Context.getApplicationContext()方法或者Activity.getApplication()方法來傳入Application對(duì)象∫鳎或者直接在Application 創(chuàng)建的時(shí)候抠璃,即onCreate的時(shí)候傳入Application Context。屬性動(dòng)畫導(dǎo)致的內(nèi)存泄露
示例:屬性動(dòng)畫中有一類無限循環(huán)的動(dòng)畫脱惰,如果在Activity播放了此類動(dòng)畫并且沒有在onDestroy中去停止動(dòng)畫搏嗡,那么動(dòng)畫會(huì)一直播放下去,并且這個(gè)時(shí)候Activity的View會(huì)被動(dòng)畫持有拉一,而View又持有了Activity采盒,最終導(dǎo)致Activity無法釋放。
解決辦法:在Activity的onDrstroy中調(diào)用animator.cancel()來停止動(dòng)畫蔚润。自定義Handler導(dǎo)致的內(nèi)存泄露
示例:如果我們?cè)贏ctivity中定義一個(gè)非靜態(tài)的Handler內(nèi)部類磅氨,這樣這個(gè)內(nèi)部類就默認(rèn)持有了當(dāng)前Activity實(shí)例的引用。Handler常常伴隨著一個(gè)執(zhí)行耗時(shí)操作的異步線程(如下載多張圖片)嫡纠,如果在完成耗時(shí)操作之前烦租,Activity退出,異步線程持有handler的引用除盏,handler持有Activity的引用叉橱,Activity的實(shí)例無法被銷毀,從而導(dǎo)致內(nèi)存泄漏
解決辦法:用一個(gè)靜態(tài)的內(nèi)部類的來代替者蠕,同時(shí)Weakference的方式傳入外部Activity的引用窃祝。同時(shí)在Activity的onDestroy方法里面調(diào)用mHandler.removeCallbacksAndMessages(null);移除該Handler相關(guān)的消息隊(duì)列中所有消息和所有的Runnable。AsyncTask導(dǎo)致的內(nèi)存泄露
原因和Handler內(nèi)存泄露原因相似踱侣,解決辦法也一樣粪小,采用靜態(tài)內(nèi)部類的方式大磺。
4.其他
Bitmap對(duì)象使用完后,忘記了調(diào)用recycle()方法銷毀探膊;
Android內(nèi)存泄露與性能優(yōu)化相關(guān)博客
- Android開發(fā)藝術(shù)探索 第15章 Android性能優(yōu)化 讀書筆記
- Android 內(nèi)存優(yōu)化
- Android 內(nèi)存泄漏總結(jié)
- Android 性能優(yōu)化
- Android 性能優(yōu)化
- android開發(fā) 之 優(yōu)化篇
Android IPC(進(jìn)程間通信)
進(jìn)程間的通信方式:
1杠愧、Bundle,封裝數(shù)據(jù)通過intent進(jìn)行傳輸突想。優(yōu)點(diǎn)是簡(jiǎn)單易用殴蹄。缺點(diǎn)是只能傳輸bundle支持的數(shù)據(jù)類型。適用場(chǎng)景在四大組件之間?通信猾担。
2袭灯、** 文件共享,各個(gè)進(jìn)程通過讀寫同一份文件共享數(shù)據(jù)绑嘹。優(yōu)點(diǎn)是簡(jiǎn)單易用稽荧。缺點(diǎn)是不適合高并發(fā)的場(chǎng)景,并且無法做到即時(shí)通信工腋。適用場(chǎng)景姨丈,不存在?并發(fā)訪問的情況,對(duì)數(shù)據(jù)實(shí)時(shí)性沒有太高的要求擅腰。
3蟋恬、 AIDL,Android接口定義語言趁冈。優(yōu)點(diǎn)是功能強(qiáng)大歼争,支持一對(duì)多的并發(fā)通信,支持實(shí)時(shí)通信渗勘。缺點(diǎn)是適用相對(duì)復(fù)雜沐绒,需要處理好線程同步。適用場(chǎng)景旺坠,存在一對(duì)多的通信乔遮,且有RPC(遠(yuǎn)程調(diào)用)需求。
4取刃、Messenger蹋肮,基于消息的進(jìn)程間通信。優(yōu)點(diǎn)是功能簡(jiǎn)單璧疗,支持一對(duì)多串行通信括尸。缺點(diǎn),不能很好處理的高并發(fā)情形病毡,不支持RPC,數(shù)據(jù)通過Message進(jìn)行傳輸屁柏,因此只能傳輸Bundle支持的數(shù)據(jù)類型啦膜。適用場(chǎng)景有送,低并發(fā)一對(duì)多即時(shí)通信,無RPC需求僧家,或者無需返回結(jié)果的RPC需求雀摘。
5、ContentProvider八拱,內(nèi)容提供者阵赠,專門用于不同應(yīng)用間的數(shù)據(jù)共享。優(yōu)點(diǎn)是在數(shù)據(jù)源訪問方面功能強(qiáng)大肌稻,支持一對(duì)多并發(fā)的數(shù)據(jù)共享清蚀。可以通過call方法擴(kuò)展其他操作爹谭。缺點(diǎn)枷邪,可以理解其為受約束的AIDL,主要提供數(shù)據(jù)源的CRUD操作诺凡。適用場(chǎng)景东揣,一對(duì)多的進(jìn)程間的數(shù)據(jù)共享。
6腹泌、Socket **嘶卧,適用TCP或UDP協(xié)議進(jìn)行網(wǎng)絡(luò)通信。優(yōu)點(diǎn)是功能強(qiáng)大凉袱,可以通過網(wǎng)絡(luò)傳輸字節(jié)流芥吟,支持一對(duì)多并發(fā)實(shí)時(shí)通信。缺點(diǎn)是實(shí)現(xiàn)細(xì)節(jié)比較繁瑣和復(fù)雜绑蔫,不支持直接的RPC运沦。適用場(chǎng)景,網(wǎng)絡(luò)數(shù)據(jù)交換配深。
Android 多進(jìn)程模式
積極作用:android系統(tǒng)為每個(gè)應(yīng)用程序分配的內(nèi)存是有限的携添。雖然隨著硬件技術(shù)的發(fā)展,android手機(jī)內(nèi)存越來越大篓叶。但是可供app使用的內(nèi)存有時(shí)還會(huì)覺得有點(diǎn)捉襟見肘烈掠,不夠用。所以我們可以通過給四大組件指定android:process屬性的方式缸托,讓比較耗內(nèi)存的組件運(yùn)行在單獨(dú)的進(jìn)程中左敌。
但是多進(jìn)程模式也帶來了諸多問題:
1.單例模式和靜態(tài)成員完全失效。原因很簡(jiǎn)單俐镐,單例和靜態(tài)成員只在進(jìn)程作用域內(nèi)有效矫限。一個(gè)進(jìn)程相當(dāng)于一個(gè)應(yīng)用程序。所以,每個(gè)進(jìn)程都會(huì)有一份單例對(duì)象和靜態(tài)成員叼风。
2.同樣線程同步也會(huì)失效取董。原因與上述相同,線程同步只在進(jìn)程作用域內(nèi)有效无宿。
3.SharedPreferences的可靠性下降茵汰。雖然SharedPreferences有Context.MODE_MULTI_PROCESS
多進(jìn)程模式,但是可靠性任然很低孽鸡。這個(gè)模式在6.0的時(shí)候被標(biāo)記為deprecated過時(shí)的蹂午。
4.Applicaion會(huì)被多次創(chuàng)建。原因是一個(gè)進(jìn)程相當(dāng)于一個(gè)應(yīng)用程序彬碱,所以Application自然而然的就會(huì)被創(chuàng)建多次豆胸。
- Android面試題(二)——IPC機(jī)制
- Android開發(fā)藝術(shù)探索 第2章 IPC機(jī)制 讀書筆記
- Android IPC - Binder 學(xué)習(xí)總結(jié)
- android IPC通信機(jī)制梳理
- android AIDL
- Android IPC機(jī)制2-AIDL的使用
- Android IPC機(jī)制
- 使用Messenger進(jìn)行跨進(jìn)程通信
- Android IPC機(jī)制(二)——利用Messenger實(shí)現(xiàn)跨進(jìn)程通信
注:幾種IPC的代碼示例集錦
IPC幾種實(shí)現(xiàn)方式樣例
媒體相關(guān)
Android Data Binding(MVVM模式)
Data Binding 使用方法
準(zhǔn)備
新建一個(gè) Project,確保 Android 的 Gradle 插件版本不低于 1.5.0-alpha1:
classpath 'com.android.tools.build:gradle:1.5.0'
然后修改對(duì)應(yīng)模塊(Module)的 build.gradle:
android {
....
dataBinding {
enabled = true
}
}
基礎(chǔ)
工程創(chuàng)建完成后堡妒,我們通過一個(gè)最簡(jiǎn)單的例子來說明 Data Binding 的基本用法配乱。
布局文件
使用 Data Binding 之后,xml 的布局文件就不再用于單純地展示 UI 元素皮迟,還需要定義 UI 元素用到的變量搬泥。所以,它的根節(jié)點(diǎn)不再是一個(gè) ViewGroup伏尼,而是變成了 layout忿檩,并且新增了一個(gè)節(jié)點(diǎn) data。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data> </data>
<!--原先的根節(jié)點(diǎn)(Root Element)-->
<LinearLayout>
....
</LinearLayout>
</layout>
要實(shí)現(xiàn) MVVM 的 ViewModel就需要把數(shù)據(jù)(Model)與 UI(View) 進(jìn)行綁定爆阶,data節(jié)點(diǎn)的作用就像一個(gè)橋梁燥透,搭建了 View
和 Model
之間的通路。我們先在 xml 布局文件的 data節(jié)點(diǎn)中聲明一個(gè) variable
辨图,這個(gè)變量會(huì)為 UI 元素提供數(shù)據(jù)(例如 TextView
的android:text
)班套,然后在 Java 代碼中把『后臺(tái)』數(shù)據(jù)與這個(gè) variable
進(jìn)行綁定。下面我們使用 Data Binding 創(chuàng)建一個(gè)展示用戶信息的表格故河。
數(shù)據(jù)對(duì)象
添加一個(gè) POJO 類 - User吱韭,非常簡(jiǎn)單,兩個(gè)屬性以及他們的getter
和 setter
鱼的。
public class User {
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
稍后理盆,我們會(huì)新建一個(gè) User類型的變量,然后把它跟布局文件中聲明的變量進(jìn)行綁定凑阶。
定義 Variable
回到布局文件猿规,在data
節(jié)點(diǎn)中聲明一個(gè) User
類型的變量 user
。
<data>
<variable name="user"type="com.liangfeizc.databindingsamples.basic.User" />
</data>
其中type
屬性就是我們?cè)?Java 文件中定義的 User
類宙橱。當(dāng)然姨俩,data
節(jié)點(diǎn)也支持 import
蘸拔,所以上面的代碼可以換一種形式來寫。
<data>
<import type="com.liangfeizc.databindingsamples.basic.User" />
<variable name="user" type="User" />
</data>
然后我們剛才在 build.gradle
中添加的那個(gè)件- com.android.databinding
會(huì)根據(jù) xml
文件的名稱 Generate 一個(gè)繼承自 ViewDataBinding
的類哼勇。 當(dāng)然都伪,IDE 中看不到這個(gè)文件,需要手動(dòng)去 build
目錄下找积担。例如,這里 xml
的文件名叫 activity_basic.xml
猬仁,那么生成的類就是 ActivityBasicBinding
帝璧。
注意
java.lang.*
包中的類會(huì)被自動(dòng)導(dǎo)入,可以直接使用湿刽,例如要定義一個(gè) String類型的變量:
<variable name="firstName" type="String" />
綁定 Variable
修改 BasicActivity 的 onCreate
方法的烁,用 DatabindingUtil.setContentView()
來替換掉 setContentView()
,然后創(chuàng)建一個(gè) user
對(duì)象诈闺,通過binding.setUser(user)
與 variable
進(jìn)行綁定渴庆。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityBasicBinding binding = DataBindingUtil.setContentView( this, R.layout.activity_basic);
User user = new User("fei", "Liang");
binding.setUser(user);
}
除了使用框架自動(dòng)生成的 ActivityBasicBinding
,我們也可以通過如下方式自定義類名雅镊。
<data class="com.example.CustomBinding"></data>
注意ActivityBasicBinding
類是自動(dòng)生成的襟雷,所有的 set
方法也是根據(jù) variable
名稱生成的。例如仁烹,我們定義了兩個(gè)變量耸弄。
<data>
<variable name="firstName" type="String" />
<variable name="lastName" type="String" />
</data>
那么就會(huì)生成對(duì)應(yīng)的兩個(gè) set
方法。
setFirstName(String firstName);
setLastName(String lastName);
使用 Variable
數(shù)據(jù)與 Variable 綁定之后卓缰,xml 的 UI 元素就可以直接使用了计呈。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}" />
至此,一個(gè)簡(jiǎn)單的數(shù)據(jù)綁定就完成了征唬,可參考完整代碼
高級(jí)用法
使用類方法
首先定義一個(gè)靜態(tài)方法
public class MyStringUtils {
public static String capitalize(final String word) {
if (word.length() > 1) {
return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);
}
return word;
}
}
然后在 xml
的 data
節(jié)點(diǎn)中導(dǎo)入:
<import type="com.liangfeizc.databindingsamples.utils.MyStringUtils" />
使用方法與 Java 語法一樣:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{MyStringUtils.capitalize(user.firstName)}" />
類型別名
如果我們?cè)?code>data節(jié)點(diǎn)了導(dǎo)入了兩個(gè)同名的類怎么辦捌显?
<import type="com.example.home.data.User" />
<import type="com.examle.detail.data.User" />
<variable name="user" type="User" />
這樣一來出現(xiàn)了兩個(gè) User
類,那user
變量要用哪一個(gè)呢总寒?不用擔(dān)心扶歪,import
還有一個(gè) alias屬性。
<import type="com.example.home.data.User" />
<import type="com.examle.detail.data.User" alias="DetailUser" />
<variable name="user" type="DetailUser" />
Null Coalescing 運(yùn)算符
android:text="@{user.displayName ?? user.lastName}"
就等價(jià)于
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
屬性值
通過 @{}
可以直接把 Java 中定義的屬性值賦值給 xml 屬性偿乖。
<TextView android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
使用資源
這個(gè)例子击罪,官方教程有錯(cuò)誤,可以參考Android Data Binder 的一個(gè)bug贪薪,完整代碼在此
<TextView android:padding="@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}"
android:background="@android:color/black"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
Observable Binding
本來這一節(jié)的標(biāo)題應(yīng)該叫雙向綁定媳禁,但是很遺憾,現(xiàn)在的 Data Binding 暫時(shí)支持單向綁定画切,還沒有達(dá)到 Angular.js 的威力竣稽。要實(shí)現(xiàn) Observable Binding
,首先得有一個(gè)implement
了接口 android.databinding.Observable
的類,為了方便毫别,Android 原生提供了已經(jīng)封裝好的一個(gè)類 - BaseObservable
娃弓,并且實(shí)現(xiàn)了監(jiān)聽器的注冊(cè)機(jī)制。我們可以直接繼承 BaseObservable
岛宦。
public class ObservableUser extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return firstName;
}
@Bindable
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
BR是編譯階段生成的一個(gè)類台丛,功能與R.java
類似,用 @Bindable
標(biāo)記過getter
方法會(huì)在 BR中生成一個(gè) entry砾肺。通過代碼可以看出挽霉,當(dāng)數(shù)據(jù)發(fā)生變化時(shí)還是需要手動(dòng)發(fā)出通知。 通過調(diào)用notifyPropertyChanged(BR.firstName)
可以通知系統(tǒng) BR.firstName
這個(gè) entry
的數(shù)據(jù)已經(jīng)發(fā)生變化变汪,需要更新 UI侠坎。
除此之外,還有一種更細(xì)粒度的綁定方式裙盾,可以具體到成員變量实胸,這種方式無需繼承 BaseObservable
,一個(gè)簡(jiǎn)單的 POJO就可以實(shí)現(xiàn)番官。
public class PlainUser {
public final ObservableField<String> firstName = new ObservableField<>();
public final ObservableField<String> lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
系統(tǒng)為我們提供了所有的 primitive type 所對(duì)應(yīng)的 Observable類庐完,例如ObservableInt
、ObservableFloat
鲤拿、ObservableBoolean
等等假褪,還有一個(gè) ObservableField
對(duì)應(yīng)著 reference type。剩下的數(shù)據(jù)綁定與前面介紹的方式一樣近顷,具體可參考ObservableActivity生音。
帶 ID 的 View
Data Binding 有效降低了代碼的冗余性,甚至完全沒有必要再去獲取一個(gè)View
實(shí)例窒升,但是情況不是絕對(duì)的缀遍,萬一我們真的就需要了呢?不用擔(dān)心饱须,只要給 View 定義一個(gè) ID域醇,Data Binding 就會(huì)為我們生成一個(gè)對(duì)應(yīng)的 final
變量。
<TextView android:id="@+id/firstName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
上面代碼中定義了一個(gè) ID 為 firstName
的 TextView
蓉媳,那么它對(duì)應(yīng)的變量就是public final TextView firstName;
具體代碼可參考 ViewWithIDsActivity.java
ViewStubs
xml
中的 ViewStub
經(jīng)過 binding 之后會(huì)轉(zhuǎn)換成 ViewStubProxy, 具體代碼可參考 ViewStubActivity.java簡(jiǎn)單用代碼說明一下譬挚,xml 文件與之前的代碼一樣,根節(jié)點(diǎn)改為 layout
酪呻,在 LinearLayout
中添加一個(gè)ViewStub
减宣,添加ID。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout ...>
<ViewStub
android:id="@+id/view_stub"
android:layout="@layout/view_stub" ... />
</LinearLayout>
</layout>
在 Java 代碼中獲取 binding
實(shí)例玩荠,為ViewStubProy
注冊(cè) ViewStub.OnInflateListener
事件:
binding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
ViewStubBinding binding = DataBindingUtil.bind(inflated);
User user = new User("fee", "lang");
binding.setUser(user);
}
});
Dynamic Variables
完整代碼可以參考 dynamic以 RecyclerView
為例漆腌,Adapter
的 DataBinding 需要?jiǎng)討B(tài)生成贼邓,因此我們可以在onCreateViewHolder
的時(shí)候創(chuàng)建這個(gè)DataBinding,然后在onBindViewHolder
中獲取這個(gè) DataBinding闷尿。
public static class BindingHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public BindingHolder(View itemView) {
super(itemView);
}
public ViewDataBinding getBinding() {
return binding;
}
public void setBinding(ViewDataBinding binding) {
this.binding = binding;
}
}
@Override
public BindingHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
ViewDataBinding binding = DataBindingUtil.inflate( LayoutInflater.from(viewGroup.getContext()), R.layout.list_item, viewGroup, false);
BindingHolder holder = new BindingHolder(binding.getRoot());
holder.setBinding(binding);
return holder;
}
@Override
public void onBindViewHolder(BindingHolder holder, int position) {
User user = users.get(position);
holder.getBinding().setVariable(BR.user, user);
holder.getBinding().executePendingBindings();
}
注意此處 DataBindingUtil
的用法:
ViewDataBinding binding = DataBindingUtil.inflate( LayoutInflater.from(viewGroup.getContext()), R.layout.list_item, viewGroup, false);
還有另外一種比較簡(jiǎn)潔的方式塑径,直接在構(gòu)造 Holder 時(shí)把 View與自動(dòng)生成的 XXXBinding
進(jìn)行綁定。
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserHolder> {
private static final int USER_COUNT = 10;
@NonNull
private List<User> mUsers;
public UserAdapter() {
mUsers = new ArrayList<>(10);
for (int i = 0; i < USER_COUNT; i ++) {
User user = new User(RandomNames.nextFirstName(), RandomNames.nextLastName());
mUsers.add(user);
}
}
public static class UserHolder extends RecyclerView.ViewHolder {
private UserItemBinding mBinding;
public UserHolder(View itemView) {
super(itemView);
mBinding = DataBindingUtil.bind(itemView);
}
public void bind(@NonNull User user) {
mBinding.setUser(user);
}
}
@Override
public UserHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.user_item, viewGroup, false);
return new UserHolder(itemView);
}
@Override
public void onBindViewHolder(UserHolder holder, int position) {
holder.bind(mUsers.get(position));
}
@Override
public int getItemCount() {
return mUsers.size();
}
}
Attribute setters
有了 Data Binding填具,即使屬性沒有在 declare-styleable中定義统舀,我們也可以通過 xml 進(jìn)行賦值操作。 為了演示這個(gè)功能劳景,我自定義了一個(gè) View - NameCard绑咱,屬性資源 R.styleable.NameCard 中只定義了一個(gè) age
屬性,其中 firstName
和 lastName
只有對(duì)應(yīng)的兩個(gè) setter
方法枢泰。只要有setter
方法就可以像下面代碼一樣賦值:
<com.liangfeizc.databindingsamples.attributesetters.UserView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/largePadding"
app:onClickListener="@{activity.clickListener}"
app:firstName="@{@string/firstName}"
app:lastName="@{@string/lastName}" app:age="27" />
onClickListener
也是同樣道理,只不過我們是在 Activity
中定義了一個(gè)Listener
铝噩。
轉(zhuǎn)換器 (Converters)
非常重要
使用 Converter 一定要保證它不會(huì)影響到其他的屬性衡蚂,例如這個(gè) @BindingConversion- convertColorToString 就會(huì)影響到android:visibility, 因?yàn)樗麄兌际嵌挤蠌?int 到 int 的轉(zhuǎn)換。
在 xml 中為屬性賦值時(shí)骏庸,如果變量的類型與屬性不一致毛甲,通過 DataBinding 可以進(jìn)行轉(zhuǎn)換。例如具被,下面代碼中如果要為屬性 android:background
賦值一個(gè)int
型的 color
變量:
<View
android:background="@{isError.get() ? @color/red : @color/white}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_height="@{height}" />
只需要定義一個(gè)標(biāo)記了@BindingConversion
的靜態(tài)方法即可(方法的定義位置可以隨意):
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
具體代碼可參考 ConversionsActivity.java玻募。
其他
Android應(yīng)用程序安裝目錄:
/system/app系統(tǒng)自帶的應(yīng)用程序,無法刪除一姿。
/data/app 用戶程序安裝的目錄七咧,有刪除權(quán)限。安裝時(shí)把a(bǔ)pk文件復(fù)制到此目錄叮叹。
/data/data 存放應(yīng)用程序的數(shù)據(jù)艾栋。
data/dalvik-cache 將apk中的dex文件安裝到dalvik-cache目錄下(dex文件是dalvik虛擬機(jī)的可執(zhí)行文件,其大小約為原始apk文件大小的四分之一)。
APP安裝過程:復(fù)制APK安裝包到data/app目錄下蛉顽,解壓并掃描安裝包蝗砾,把dex文件(Dalvik字節(jié)碼)保存到dalvik-cache目錄,并data/data目錄下創(chuàng)建對(duì)應(yīng)的應(yīng)用數(shù)據(jù)目錄携冤。
Android 如何查找so文件所在目錄悼粮,安裝APK時(shí)so安裝到哪個(gè)目錄