本篇內(nèi)容主要講解“Bitmap知識點(diǎn)有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Bitmap知識點(diǎn)有哪些”吧!
公司主營業(yè)務(wù):成都做網(wǎng)站、網(wǎng)站制作、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)建站是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)建站推出石阡免費(fèi)做網(wǎng)站回饋大家。
Bitmap
,位圖,本質(zhì)上是一張圖片的內(nèi)容在內(nèi)存中的表達(dá)形式。它將圖片的內(nèi)容看做是由存儲數(shù)據(jù)的有限個(gè)像素點(diǎn)組成;每個(gè)像素點(diǎn)存儲該像素點(diǎn)位置的ARGB
值,每個(gè)像素點(diǎn)的ARGB
值確定下來,這張圖片的內(nèi)容就相應(yīng)地確定下來。其中,A代表透明度,RGB代表紅綠藍(lán)三種顏色通道值。
Bitmap一直都是Android
中的內(nèi)存大戶,計(jì)算大小的方式有三種:
getRowBytes()
這個(gè)在 API Level 1
添加的,返回的是bitmap一行所占的大小,需要乘以bitmap的高,才能得出btimap的大小
getByteCount()
這個(gè)是在 API Level 12
添加的,其實(shí)是對getRowBytes()乘以高的封裝
getAllocationByteCount()
這個(gè)是在 API Level 19
添加的
這里我將一張圖片放到項(xiàng)目的drawable-xxhdpi文件夾中,然后通過方法獲取圖片所占的內(nèi)存大小:
var bitmap = BitmapFactory.decodeResource(resources, R.drawable.test) img.setImageBitmap(bitmap) Log.e(TAG,"dpi = ${resources.displayMetrics.densityDpi}") Log.e(TAG,"size = ${bitmap.allocationByteCount}")
打印出來的結(jié)果是
size=1960000
具體是怎么計(jì)算的呢?
圖片內(nèi)存=寬 * 高 * 每個(gè)像素所占字節(jié)。
這個(gè)像素所占字節(jié)又和Bitmap.Config
有關(guān),Bitmap.Config
是個(gè)枚舉類,用于描述每個(gè)像素點(diǎn)的信息,比如:
ARGB_8888
。常用類型,總共32位,4個(gè)字節(jié),分別表示透明度和RGB通道。
RGB_565
。16位,2個(gè)字節(jié),只能描述RGB通道。
所以我們這里的圖片內(nèi)存計(jì)算就得出:
寬700 * 高700 * 每個(gè)像素4字節(jié)=1960000
首先放一張drawable
目錄對應(yīng)的屏幕密度對照表,來自郭霖的博客:
對照表
剛才的案例,我們是把圖片放到drawable-xxhdpi
文件夾,而drawable-xxhdpi
文件夾對應(yīng)的dpi就是我們測試手機(jī)的dpi—480。所以圖片的內(nèi)存就是我們所計(jì)算的寬 * 高 * 每個(gè)像素所占字節(jié)
。
如果我們把圖片放到其他的文件夾,比如drawable-hdpi
文件夾(對應(yīng)的dpi是240),會發(fā)生什么呢?
再次打印結(jié)果:
size = 7840000
這是因?yàn)橐粡垐D片的實(shí)際占用內(nèi)存大小計(jì)算公式是:
占用內(nèi)存 = 寬 * 縮放比例 * 高 * 縮放比例 * 每個(gè)像素所占字節(jié)
這個(gè)縮放比例就跟屏幕密度DPI有關(guān)了:
縮放比例 = 設(shè)備dpi/圖片所在目錄的dpi
所以我們這張圖片的實(shí)際占用內(nèi)存位:
寬700 * (480/240) * 高700 * (480/240) * 每個(gè)像素4字節(jié) = 7840000
常用的優(yōu)化方式是兩種:
修改Bitmap.Config
這一點(diǎn)剛才也說過,不同的Conifg
代表每個(gè)像素不同的占用空間,所以如果我們把默認(rèn)的ARGB_8888
改成RGB_565
,那么每個(gè)像素占用空間就會由4字節(jié)變成2字節(jié)了,那么圖片所占內(nèi)存就會減半了。
可能一定程度上會降低圖片質(zhì)量,但是我實(shí)際測試看不出什么變化。
修改inSampleSize
inSampleSize
,采樣率,這個(gè)參數(shù)是用于圖片尺寸壓縮的,他會在寬高的維度上每隔inSampleSize
個(gè)像素進(jìn)行一次采集,從而達(dá)到縮放圖片的效果。這種方法只會改變圖片大小,不會影響圖片質(zhì)量。
val options=BitmapFactory.Options() options.inSampleSize=2 val bitmap = BitmapFactory.decodeResource(resources, R.drawable.test2,options) img.setImageBitmap(bitmap)
實(shí)際項(xiàng)目中,我們可以設(shè)置一個(gè)與目標(biāo)圖像大小相近的inSampleSize
,來減少實(shí)際使用的內(nèi)存:
fun getImage(): Bitmap { var options = BitmapFactory.Options() options.inJustDecodeBounds = true BitmapFactory.decodeResource(resources, R.drawable.test2, options) // 計(jì)算最佳采樣率 options.inSampleSize = getImageSampleSize(options.outWidth, options.outHeight) options.inJustDecodeBounds = false return BitmapFactory.decodeResource(resources, R.drawable.test2, options) }
上面的例子大家應(yīng)該發(fā)現(xiàn)了,其中有個(gè)inJustDecodeBounds
,又設(shè)置為true,又設(shè)置成false的,總感覺多此一舉,那么他到底是干嘛呢?
因?yàn)槲覀円@取圖片本身的大小,如果直接decodeResource
加載一遍的話,那么就會增加內(nèi)存了,所以官方提供了這樣一個(gè)參數(shù)inJustDecodeBounds
。如果inJustDecodeBounds
為ture,那么decode
的bitmap
為null,也就是不返回實(shí)際的bitmap
,只把圖片的大小信息放到了options的值中。
所以這個(gè)參數(shù)就是用來獲取圖片的大小信息的同時(shí)不占用內(nèi)存。
如果有個(gè)需求,是在同一個(gè)imageview
中可以加載不同的圖片,那我們需要每次都去新建一個(gè)Bitmap
對象,占用新的內(nèi)存空間嗎?如果我們這樣寫的話:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.actvitiy_bitmap) btn1.setOnClickListener { img.setImageBitmap(getBitmap(R.drawable.test)) } btn2.setOnClickListener { img.setImageBitmap(getBitmap(R.drawable.test2)) } } fun getBitmap(resId: Int): Bitmap { var options = BitmapFactory.Options() return BitmapFactory.decodeResource(resources, resId, options) }
這樣就會Bitmap
就會頻繁去申請內(nèi)存,釋放內(nèi)存,從而導(dǎo)致大量GC
,內(nèi)存抖動。
為了防止這種情況呢,我們就可以用到inBitmap
參數(shù),用于Bitmap
的內(nèi)存復(fù)用。這樣同一塊內(nèi)存空間就可以被多個(gè)Bitmap
對象復(fù)用,從而減少了頻繁的GC。
val options by lazy { BitmapFactory.Options() } val reuseBitmap by lazy { options.inMutable = true BitmapFactory.decodeResource(resources, R.drawable.test, options) } fun getBitmap(resId: Int): Bitmap { options.inMutable = true options.inBitmap = reuseBitmap return BitmapFactory.decodeResource(resources, resId, options) }
這里有幾個(gè)要注意的點(diǎn)
inBitmap
要和 inMutable
屬性配套使用,否則將無法復(fù)用。
在 Android 4.4
之前,只能重用相同大小的 Bitmap
內(nèi)存區(qū)域; 4.4之后
只要復(fù)用內(nèi)存空間的Bitmap對象大小比 inBitmap
指向的內(nèi)存空間要小即可。
所以一般在復(fù)用之前,還要判斷下,新的Bitmap
內(nèi)存是不是小于可以復(fù)用的Bitmap
內(nèi)存,然后才能進(jìn)行復(fù)用。
如果是高清大圖,那就說明不允許進(jìn)行圖片壓縮,比如微博長圖,清明上河圖。
所以我們就要對圖片進(jìn)行局部顯示,這就用到BitmapRegionDecoder
屬性,主要用于顯示圖片的某一塊矩形區(qū)域。
比如我要顯示左上角的100 * 100區(qū)域:
fun setImagePart() { val inputStream: InputStream = assets.open("test.jpg") val bitmapRegionDecoder: BitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false) val options = BitmapFactory.Options() val bitmap = bitmapRegionDecoder.decodeRegion( Rect(0, 0, 100, 100), options) image.setImageBitmap(bitmap) }
實(shí)際項(xiàng)目使用中,我們可以根據(jù)手勢滑動,然后不斷更新我們的Rect參數(shù)來實(shí)現(xiàn)具體的功能即可。
具體實(shí)現(xiàn)源碼可以參考鴻洋的博客:https://blog.csdn.net/lmj623565791/article/details/49300989
Bundle直接傳遞
。bundle最常用于Activity間傳遞,也屬于跨進(jìn)程的一種方式,但是傳遞的大小有限制,一般為1M。
//intent.put的putExtra方法實(shí)質(zhì)也是通過bundleintent.putExtra("image",bitmap); bundle.putParcelable("image",bitmap)
Bitmap
之所以可以直接傳遞,是因?yàn)槠鋵?shí)現(xiàn)了Parcelable
接口進(jìn)行了序列化。而Parcelable的傳遞原理是利用了Binder
機(jī)制,將Parcel
序列化的數(shù)據(jù)寫入到一個(gè)共享內(nèi)存(緩沖區(qū))中,讀取的時(shí)候也會從這個(gè)緩沖區(qū)中去讀取字節(jié)流,然后再反序列化成對象使用。這個(gè)共享內(nèi)存也就是緩存區(qū)有一個(gè)大小限制—1M,而且是公用的。所以傳圖片的話很容易就容易超過這個(gè)大小然后報(bào)錯(cuò)TransactionTooLargeException
。
所以這個(gè)方案不可靠。
文件傳輸
。
將圖片保存到文件,然后只傳輸文件路徑,這樣肯定是可以的,但是不高效。
putBinder
這個(gè)就是考點(diǎn)了。通過傳遞binder
的方式傳遞bitmap。
//傳遞binderval bundle = Bundle() bundle.putBinder("bitmap", BitmapBinder(mBitmap))//接收binder中的bitmapval imageBinder: BitmapBinder = bundle.getBinder("bitmap") as BitmapBinderval bitmap: Bitmap? = imageBinder.getBitmap()//Binder子類class BitmapBinder :Binder(){ private var bitmap: Bitmap? = null fun ImageBinder(bitmap: Bitmap?) { this.bitmap = bitmap } fun getBitmap(): Bitmap? { return bitmap } }
為什么用putBinder
就沒有大小限制了呢?
因?yàn)?putBinder
中傳遞的其實(shí)是一個(gè)文件描述符fd,文件本身被放到一個(gè)共享內(nèi)存中,然后獲取到這個(gè)fd之后,只需要從共享內(nèi)存中取出Bitmap數(shù)據(jù)即可,這樣傳輸就很高效了。
而用 Intent/bundle
直接傳輸?shù)臅r(shí)候,會禁用文件描述符fd,只能在parcel的緩存區(qū)中分配空間來保存數(shù)據(jù),所以無法突破1M的大小限制。
文件描述符是一個(gè)簡單的整數(shù),用以標(biāo)明每一個(gè)被進(jìn)程所打開的文件和socket。第一個(gè)打開的文件是0,第二個(gè)是1,依此類推。
到此,相信大家對“Bitmap知識點(diǎn)有哪些”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!