-
熱更新原理
客戶端向服務(wù)器請求時,在請求頭部帶上range: bytes=2000070-谴古,表示文件請求從2000070開始。服務(wù)端收到請求時怯屉,在響應(yīng)中帶上content-range=bytes 2000070-106786027/106786028抛猫,同時將return code從200改為206。
而文件的續(xù)寫刃泡,可以使用io包中的RandomAccessFile
巧娱。
Url url = new Url("http://www.jizhuomi.com/android/down.zip");
Httpurlconnection httpconnection = (httpurlconnection)url.openconnection();
//設(shè)置user-agent
httpconnection.setrequestproperty("user-agent","netfox");
//設(shè)置斷點續(xù)傳的開始位置
httpconnection.setrequestproperty("range","bytes=2000070");
//獲得輸入流
Inputstream input = httpconnection.getinputstream();
//從輸入流中取出的字節(jié)流就是down.zip文件從2000070開始的字節(jié)流。
randomaccess osavedfile = new randomaccessfile("local_down.zip","rw");
long npos = 2000070;
//定位文件指針到npos位置
osavedfile.seek(npos);
byte[] b = new byte[1024];
int nread;
//從輸入流中讀入字節(jié)流烘贴,然后寫到文件中
while((nread=input.read(b,0,1024)) > 0) {
osavedfile.write(b,0,nread);
}
- ANR
anr最近三天的trace.txt會保存到/data/system/dropbox目錄下禁添。這是由DropBoxManager實現(xiàn)的,該類可以理解為一個持久化的logcat桨踪。讀源碼時發(fā)現(xiàn)老翘,anr的logcat是在ActivityManagerService的appNotResponding()輸出的,其中用到了一個很實用的工具類Os
锻离,里面有個Os.chmod(path,mode)
方法可以便利地設(shè)置文件權(quán)限铺峭。而trace.txt的寫入必須在20s內(nèi)完成,ActivityManagerService通過FileObserver
實現(xiàn)對文件讀寫的監(jiān)聽汽纠。一旦文件有修改卫键,會觸發(fā)FileObserver.onEvent()
通知監(jiān)聽者。
3.熱更新原理
Java運行時的類加載是通過抽象類ClassLoader
實現(xiàn)的虱朵,而Android中其默認(rèn)實現(xiàn)是PathClassLoader
莉炉。后者的父類是BaseDexClassLoader
钓账。PathClassLoader
只是簡單地從文件系統(tǒng)中加載類文件,而BaseDexClassLoader
重寫了findClass()
方法絮宁,并在其中委托DexPathList
去調(diào)用它的findClass()
梆暮。
public Class findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
要實現(xiàn)熱更新,就需要我們在運行時去更改PathClassLoader.pathList.dexElements.并將我們的hotpacth dex文件出現(xiàn)在dexElements列表的前面羞福。
今日頭條屏幕適配原理:屏幕寬度(px)/設(shè)計稿寬度(dp) = 目標(biāo)屏幕寬度(px)/目標(biāo)值(dp)惕蹄。利用的是動態(tài)修改屏幕密度,使得兩者屏幕密度相等(density = px / dp)治专。
參考:
Android 目前最穩(wěn)定和高效的UI適配方案
Android屏幕適配方案(出自今日頭條)
關(guān)于今日頭條的屏幕適配方案學(xué)習(xí)理解顯示大圖卖陵,但是不能壓縮:
BitmapRegionDecoder
:
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
bitmapRegionDecoder.decodeRegion(rect, options);
7.本地緩存DiskLruCache
主要記住有一個journal.temp文件去保存每次緩存讀寫狀態(tài):DIRTY(寫入緩存,但是沒有commit),CLEAN(緩存已被commit),REMOVE,READ张峰。同時內(nèi)部LRU邏輯是通過LikedHashMap
來實現(xiàn)泪蔫,trimToSize()
觸發(fā)回收。
渲染:
Paint.setShader(Shader shader)
喘批。常用的shader:BitmapShader
位圖圖像渲染實例撩荣、LinearGradient
線性渲染、RadialGradient
環(huán)形渲染饶深、SweepGradient
掃描漸變渲染/梯度渲染餐曹、ComposeShader
組合渲染.-
圖像相交
paint.setXfermode(Xfermode xfermode)
。 常用的是Xfermode
的PorterDuffXfermode
子類
PorterDuffXfermode效果 Alpha濾鏡:
setMaskFilter(MaskFilter maskfilter)
敌厘。常用的是模糊濾鏡BlurMaskFilter
,浮雕濾鏡EmbossMaskFilter
-
繪制路徑效果:
Paint.setPathEffect(PathEffect effect)
PathEffect -
顏色過濾:
Paint.setColorFilter(ColorFilter filter)
台猴,主要對RGB(A)進行過濾。
ColorFilter 曝光顏色過濾:
Paint.setColorFilter(new LightingColorFilter(int mul, int add))
俱两。實質(zhì)計算方式就是(mul * 原色值 + add)% 255饱狂,不過一定要注意,該過濾器是不處理Alpha的宪彩,也就是說只對圖片里的RGB有效休讳,A無效圖層混合顏色過濾:
PorterDuffColorFilter(int color, PorterDuff.Mode mode)
兩個Activity跳轉(zhuǎn),必然會執(zhí)行什么方法尿孔?
一般地俊柔,Activity A 跳轉(zhuǎn)到Activity B,A會先調(diào)用onPause()活合,然后B會調(diào)用onCreate()雏婶,onStart(),onResume()芜辕。
如果此時B已經(jīng)完全遮擋A尚骄,A會調(diào)用onStop()块差;否則侵续,如果B是個透明窗口倔丈,或者是對話框主題,則A不會調(diào)用onPause()状蜗。
如果B已經(jīng)存在于Activity棧中需五,則不會調(diào)用onCreate()。如果同時使用startService 與bindService 方法啟動Service轧坎,需要終止該Service時宏邮,要調(diào)用stopService 和unbindService 方法(unbindService 依附于啟動它的Context,startServicec 并不依附于啟動它的Context缸血。如果先調(diào)用unbindService 蜜氨,這時服務(wù)并不會被終止,當(dāng)調(diào)用stopService 后捎泻,服務(wù)才會被終止飒炎;如果先調(diào)用stopService ,服務(wù)也不會被終止笆豁,當(dāng)調(diào)用unbindService 或者之前調(diào)用bindService 的Context不存在了(如Activity被finish掉了)服務(wù)才會自動停止)
屬性動畫原理:屬性動畫的運行機制是通過不斷地對值進行操作來實現(xiàn)的郎汪,而初始值和結(jié)束值之間的過渡則是通過ValueAnimator來負(fù)責(zé)計算。其中TypeEvaluator決定了初始值過渡到結(jié)束值的計算算法(y = sin(x))闯狱,TimeInterpolator決定了初始值過渡到結(jié)束值的節(jié)奏煞赢。
SharedPreferences是線程安全的,但不是進程安全的哄孤。
第一次getSharedPreferences()會開啟一個單線程池去異步加載照筑。讀取磁盤文件,get/set之前有awaitLoadedLocked()同步操作录豺,在磁盤文件加載完成前會卡住調(diào)用線程朦肘。加載完成之后會從內(nèi)存中讀取。
apply是內(nèi)存同步操作双饥,但是會開啟一個異步任務(wù)放到單線程隊列中媒抠,更新磁盤緩存。commit同apply類似咏花,但是會等磁盤任務(wù)完成才返回趴生,會有boolean結(jié)果返回。每次 apply / commit 都會把全部的數(shù)據(jù)一次性寫入磁盤, 所以單個的配置文件不應(yīng)該過大, 影響整體性能昏翰。在瀏覽器中輸入一個網(wǎng)址苍匆,并得到網(wǎng)頁呈現(xiàn)結(jié)果。這個過程中經(jīng)歷了什么棚菊?
DNS域名解析->TCP三次握手->TCP建立并發(fā)送HTTP請求->服務(wù)器響應(yīng)HTTP請求->瀏覽器解析HTTP代碼浸踩,同時獲取css,js统求,圖片等資源->瀏覽器渲染頁面并呈現(xiàn)結(jié)果為什么是三次握手检碗,四次揮手据块?
關(guān)閉連接時,當(dāng)Server端收到FIN報文時折剃,很可能數(shù)據(jù)信息沒有傳完并不會立即關(guān)閉連接另假,所以只能先回復(fù)一個ACK報文(告訴Client端,"你發(fā)的FIN報文我收到了")怕犁。只有等到Server端所有的報文都發(fā)送完了边篮,我才能發(fā)送FIN報文,因此不能一起發(fā)送奏甫。故需要四步揮手戈轿。
類型 | 調(diào)用方式 | 優(yōu)點 | 缺點 | 使用場景 |
---|---|---|---|---|
Android調(diào)用js |
WebView.loadUrl("javascript:jsMethod()" ) |
簡潔方便 | 效率低,獲取返回值麻煩 | 不需要返回值阵子,對性能要求不高 |
Android調(diào)用js | WebView.evaluateJavascript() |
效率高 | 只兼容Android4.4以上 | Android4.4以上 |
js調(diào)用Android | WebView.addJavascriptInterface(new Object(), "objectName") |
簡潔方便 | Android4.2以下存在安全漏洞 | Android4.2以上相對簡單的互調(diào) |
js調(diào)用Android |
WebViewClient.shouldOverrideUrlLoading() 回調(diào)攔截 url |
不存在安全漏洞 | 使用復(fù)雜凶杖,需要進行協(xié)議約定;從Native層往Webview傳值比較繁瑣 | 不需要返回值的互調(diào)(IOS主要使用該方式) |
js調(diào)用Android |
WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調(diào)攔截JS對話框alert()催训、confirm()窖梁、prompt() 消息 回調(diào)攔截 url |
不存在安全漏洞 | 使用復(fù)雜,需要進行協(xié)議約定 | 滿足大多數(shù)情況下的互調(diào) |
16.進程保活
- 黑色保活:不同APP之間相互配合,互相喚醒漆撞;利用系統(tǒng)廣播喚醒,包括ACTION_NEW_PICTURE(拍照)于宙,ACTION_NEW_VIDEO(拍視頻)浮驳,CONNECTIVITY_ACTION(網(wǎng)絡(luò)切換)等;
- 白色崩炭活:啟動前臺service
- 灰色敝粱幔活:利用系統(tǒng)的漏洞啟動前臺Service
- Native進程:利用 Linux 中的 fork 機制創(chuàng)建 Native 進程,在 Native 進程中監(jiān)控主進程的存活谱俭,當(dāng)主進程掛掉后奉件,在 Native 進程中立即對主進程進行拉活
- JobScheduler:系統(tǒng)會定時調(diào)用該進程以使應(yīng)用進行一些邏輯操作
- 廠商白名單
總結(jié):進程保活本就是個偽命題昆著,不存在不死的方法县貌。只能從提升和優(yōu)化自身應(yīng)用的性能,降低進程的oom_adj凑懂。同時不同廠商從系統(tǒng)層對APP進行優(yōu)待處理煤痕。
notify和notifyAll的區(qū)別
首先區(qū)分兩個概念,鎖池和等待池:
鎖池:等待當(dāng)前鎖的擁有者線程釋放鎖,以便執(zhí)行該對象的synchronized方法的線程隊列摆碉。
等待池:線程調(diào)用wait方法釋放鎖祟敛,就會進入等待池。等待池中的線程不會去競爭該對象的鎖兆解。
當(dāng)擁有鎖的線程調(diào)用notify時,會從等待池中隨機將某一條線程移動到鎖池中跑揉,去競爭鎖的擁有權(quán)锅睛。而調(diào)用notifyAll,所有等待池中的線程都會被移動到鎖池中競爭鎖历谍。優(yōu)先級高的線程競爭到對象鎖的概率大现拒,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中望侈,唯有線程再次調(diào)用 wait()方法印蔬,它才會重新回到等待池中。而競爭到對象鎖的線程則繼續(xù)往下執(zhí)行脱衙,直到執(zhí)行完了 synchronized 代碼塊侥猬,它會釋放掉該對象鎖,這時鎖池中的線程會繼續(xù)競爭該對象鎖捐韩。屏幕適配
dp(dip,也就是密度無關(guān)像素)解決了同一控件數(shù)值在不同分辨率中展示相同大小的問題(也就是屏幕像素密度匹配退唠,dpi),但是沒法解決屏幕尺寸大小帶來的匹配問題(也就是屏幕尺寸匹配)荤胁。
Nexus5的總寬度為360dp瞧预,我們現(xiàn)在在水平方向上放置兩個按鈕,一個是150dp左對齊仅政,另外一個是200dp右對齊垢油,那么中間留有10dp間隔;
但假如同樣地設(shè)置在Nexus S(屏幕寬度是320dp)圆丹,會發(fā)現(xiàn)滩愁,兩個按鈕會重疊,因為320dp<200+150dp
本質(zhì)上是希望使得布局組件在不同屏幕密度上顯示相同的像素效果辫封,對于屏幕尺寸匹配惊楼,可以考慮百分比匹配。
-
Android動態(tài)加載ClassLoader
JVM的ClassLoader體系
ClassLoader使用雙親委托機制來實現(xiàn)秸讹。其中熱修復(fù)基本都是利用DexClassLoader來實現(xiàn)檀咙;而PatheClassLoader為Android用來加載應(yīng)用類以及系統(tǒng)類,不推薦開發(fā)者使用璃诀。其父加載器為BootClassLoader弧可。
參考:Android動態(tài)加載ClassLoader、熱修復(fù)入門
- 樂觀鎖和悲觀鎖
樂觀鎖:不加鎖劣欢,使用CAS和版本號機制實現(xiàn)棕诵。
悲觀鎖:加鎖裁良,Java中使用synchronzied關(guān)鍵字實現(xiàn)。
CAS校套,在Java中使用Unsafe對象實現(xiàn)价脾,它只能保證單個變量的原子性,需要與volatile來保證線程安全笛匙。同時侨把,它還存在ABA問題:
假設(shè)有兩個線程——線程1和線程2,兩個 線程按照順序進行以下操作:
- 線程1讀取內(nèi)存中數(shù)據(jù)為A妹孙;
- 線程2將該數(shù)據(jù)修改為B秋柄;
- 線程2將該數(shù)據(jù)修改為A;
- 線程1對數(shù)據(jù)進行CAS操作
在第(4)步中蠢正,由于內(nèi)存中數(shù)據(jù)仍然為A骇笔,因此CAS操作成功,但實際上該數(shù)據(jù)已經(jīng)被線程2修改過了嚣崭。這就是ABA問題笨触。
在AtomicInteger的例子中,ABA似乎沒有什么危害雹舀。但是在某些場景下旭旭,ABA卻會帶來隱患,例如棧頂問題:一個棧的棧頂經(jīng)過兩次(或多次)變化又恢復(fù)了原值葱跋,但是棾旨模可能已發(fā)生了變化。
解決這個問題可以使用AtomicStampedReference
娱俺。
同時稍味,在高并發(fā)沖突下的失敗重試,也會給CPU帶來很大的開銷荠卷。
因此總結(jié)使用場景:
- 當(dāng)競爭不激烈 (出現(xiàn)并發(fā)沖突的概率小)時模庐,樂觀鎖更有優(yōu)勢,因為悲觀鎖會鎖住代碼塊或數(shù)據(jù)油宜,其他線程無法同時訪問掂碱,影響并發(fā),而且加鎖和釋放鎖都需要消耗額外的資源慎冤。
- 當(dāng)競爭激烈(出現(xiàn)并發(fā)沖突的概率大)時疼燥,悲觀鎖更有優(yōu)勢,因為樂觀鎖在執(zhí)行更新時頻繁失敗蚁堤,需要不斷重試醉者,浪費CPU資源。
- 網(wǎng)絡(luò)安全
- 消息摘要&數(shù)字簽名
消息摘要:唯一一個對應(yīng)一段文本的固定長度的值,由單向hash加密函數(shù)對消息進行運算生成撬即。由于其唯一性立磁,不可逆,可用于校驗文本或文件是否篡改剥槐。不需要密鑰唱歧。常用算法有MD(Message Digest,消息摘要算法)和SHA(Secure Hash Algorithm粒竖,安全散列算法)颅崩,最具代表性對分別是MD5,SHA1。
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
// 更新要計算的內(nèi)容
md5Digest.update(msg.getBytes());
// 完成哈希計算温圆,得到摘要
byte[] md5Encoded = md5Digest.digest();
MessageDigest shaDigest = MessageDigest.getInstance("SHA");
// 更新要計算的內(nèi)容
shaDigest.update(msg.getBytes());
// 完成哈希計算,得到摘要
byte[] shaEncoded = shaDigest.digest()
- 數(shù)字簽名:數(shù)字簽名算法可以看做是一種帶有密鑰的消息摘要算法孩革,并且這種密鑰包含了公鑰和私鑰岁歉。也就是說,數(shù)字簽名算法是非對稱加密算法和消息摘要算法的結(jié)合體膝蜈。要求能夠驗證數(shù)據(jù)完整性锅移、認(rèn)證數(shù)據(jù)來源,并起到抗否認(rèn)的作用饱搏。其原理是利用私鑰簽名非剃,公鑰驗簽,其核心算法是消息摘要算法推沸。最主要對算法有RAS备绽,DSA。總結(jié):公鑰加密鬓催、私鑰解密肺素、私鑰簽名、公鑰驗簽
/**
* 獲取私鑰
*
* @param privateKey 私鑰字符串
* @return
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
return keyFactory.generatePrivate(keySpec);
}
/**
* 獲取公鑰
*
* @param publicKey 公鑰字符串
* @return
*/
public static PublicKey getPublicKey(String publicKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
return keyFactory.generatePublic(keySpec);
}
/**
* <p>
* 用私鑰對信息生成數(shù)字簽名
* </p>
*
* @param data 已加密數(shù)據(jù)
* @param privateKey 私鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static String sign(byte[] data, String privateKey) throws Exception {
byte[] keyBytes = Base64Utils.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateK);
signature.update(data);
return Base64Utils.encode(signature.sign());
}
/**
* <p>
* 用公鑰校驗數(shù)字簽名
* </p>
*
* @param data 已加密數(shù)據(jù)
* @param publicKey 公鑰(BASE64編碼)
* @param sign 數(shù)字簽名
* @return
* @throws Exception
*/
public static boolean verify(byte[] data, String publicKey, String sign)
throws Exception {
byte[] keyBytes = Base64Utils.decode(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicK = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicK);
signature.update(data);
return signature.verify(Base64Utils.decode(sign));
}
/**
* <p>
* 公鑰加密
* </p>
*
* @param data 源數(shù)據(jù)
* @param publicKey 公鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String publicKey)
throws Exception {
byte[] keyBytes = Base64Utils.decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 對數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對數(shù)據(jù)分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
/**
* <p>
* 公鑰解密
* </p>
*
* @param encryptedData 已加密數(shù)據(jù)
* @param publicKey 公鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
throws Exception {
byte[] keyBytes = Base64Utils.decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對數(shù)據(jù)分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
/**
* <p>
* 私鑰加密
* </p>
*
* @param data 源數(shù)據(jù)
* @param privateKey 私鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
throws Exception {
byte[] keyBytes = Base64Utils.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對數(shù)據(jù)分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
/**
* <P>
* 私鑰解密
* </p>
*
* @param encryptedData 已加密數(shù)據(jù)
* @param privateKey 私鑰(BASE64編碼)
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
throws Exception {
byte[] keyBytes = Base64Utils.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 對數(shù)據(jù)分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}