當前位置:首頁 » 文件管理 » android文件上傳oom

android文件上傳oom

發布時間: 2022-06-16 20:14:53

① android app上傳文件速度太慢怎麼處理

一、釋放XP自帶保留20%的帶寬,讓你的網速更快! 1、運行組策略編輯器程序(gpedit.msc)。在「『本地計算機』策略」中,逐級「計算機配置」→「管理模板」→「網路(互聯網)」→「QoS數據包調度程序」分支。在屏幕右邊會出現「QoS數據包調度程序」策略。接著單擊右邊子項目的「限制可保留帶寬」。這時,左邊會顯示「限制可保留帶寬」的清楚描述。從這里我們可了解到「限制可保留帶寬」的有些基本情形。了解之後我們就可以對「限制可保留帶寬」進行設置了。單擊「限制可保留帶寬」下「顯示」旁邊的「屬性」(或選取子項目「限制可保留帶寬」,再點擊右鍵→「屬性」也可),出現「限制可保留帶寬」對話框,先點擊「說明」,再進一步了解「限制可保留帶寬」確定系統(System)可保留的連接帶寬的百分比情形。 之後我們就可以對另外20%帶寬進設置了。點擊「設置」。「設置」為我們提供了三個選取(未配置、已啟用、已禁用),選取「已啟用」,接著再將帶寬限制旁邊的%設置為0%即可,之後按確定退出。 2、單擊「開始」→「連接到」→「顯示全部連接」。 選中你所建立的連接,用滑鼠右鍵單擊屬性,在出現的連接屬性中單擊網路(互聯網),在顯示的網路(互聯網)對話框中,檢查「此連接用下列項目」中「QoS數據包調度程序」是不是已打了勾,沒問題就按確定退出。 3、最後重新開啟系統(System)便完成對另外20%的頻寬利用了 二、清理 ①清空Internet臨時文件夾 他人查看「Internet臨時文件夾」下的圖、Flash等文件便能大體知道你曾到過的網站。要清理它們,可依次單擊IE菜單欄中的「工具」→「Internet選項」,打開(OPEN)「Internet選項」對話框,在「常規」標簽中點擊「刪掉文件」按鈕,在彈出的「刪掉文件」窗口中勾選「刪掉全部離線內容」,最後點擊「確定」。 三、關掉上網設備貓和路由,十多分鍾後再開機 四、還有網路(互聯網)網站問題

② android post 提交數據過大 會導致oom 求解決方案

你的圖有點小。看不清。。。。你用的是廣泛流行的OKHttp?還是Xutils。別用volley那個只適合顯示小部分數據。既然要上傳數據還是大數據就換個框架吧,認真的

③ android uil oom 怎麼處理

對於OOM,其實最重要的是注意不要Memory Leak。而Memory Leak是會有多個方面會引起的,比如Drawable, RemoteViews, Receiver, Cursor,InputStream, MediaPlayer等,此外,如果使用JNI也會因為C或C++的代碼導致Memory Leak。

除了Memory Leak,大數據量的操作也會導致OOM,比如之前其他回答提到的Bitmap,還有ListAdapter,如果在getView時處理不當,也很容易導致OOM,所以在ListAdapter時應該盡量使用convertView。

最後,可以用android.os.StrictMode以及Eclipse的MAT工具來進行OOM和Memory Leak的檢測。

④ android何時會發生oom怎麼解決oom

首先,OOM就是內存溢出,即Out Of Memory。也就是說內存佔有量超過了VM所分配的最大。
怎麼解決OOM,通常OOM都發生在需要用到大量內存的情況下(創建或解析Bitmap,分配特大的數組等),在這樣的一種情況下,就可能出現OOM,據我現在了解到,多數OOM都是因為Bitmap太大。所以,這里我就專門針對如何解決Bitmap的OOM。其實最核發的就是只載入可見范圍內的Bitmap,試想這樣一種情況,在GridView或ListView中,數據量有5000,每一屏只顯示20個元素,那麼不可見的,我們是不需要保存Bitmap在內在中的。所以我們就是只把那麼可見的Bitmap保留在內存中,那些不可見的,就釋放掉。當元素滑出來時,再去載入Bitmap。
這里我有兩種方式,都可以避免OOM。

⑤ android實現文件上傳的功能

我是這樣做的
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(intent, "請選擇一個要上傳的文件"), 1);
然後選擇文件後調用
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
String url= uri.toString();


獲得路徑,根據路徑調用
public String convertCodeAndGetText(String str_filepath) {// 轉碼\
try {
File file1 = new File(str_filepath);
file_name = file1.getName();
FileInputStream in = new FileInputStream(file1);
byte[] buffer = new byte[(int) file1.length() + 100];
int length = in.read(buffer);
load = Base64.encodeToString(buffer, 0, length,
Base64.DEFAULT);
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return load;
}
對文件進行編碼

⑥ 如何解決上傳多張圖片時遇到的oom問題

一、OOM問題出現的場景和原因
一個好的app總少不了精美的圖片,所以Android開發中圖片的載入總是避免不了的,而在載入圖片過程中,如果處理不當則會出現OOM的問題。那麼如何徹底解決這個問題呢?本文將具體介紹這方面的知識。
首先我們來總結一下,在載入圖片過程中出現的OOM的場景無非就這么幾種:
1、 載入的圖片過大
2、 一次載入的圖片過多
3、 以上兩種情況兼有
那麼為什麼在以上場景下會出現OOM問題呢?實際上在API文檔中有著明確的說明,出現OMM的主要原因有兩點:
1、移動設備會限制每個app所能夠使用的內存,最小為16M,有的設備分配的會更多,如24、32M、64M等等不一,總之會有限制,不會讓你無限制的使用。
2、在andorid中圖片載入到內存中是以點陣圖的方式存儲的,在android2.3之後默認情況下使用ARGB_8888,這種方式下每個像素要使用4各位元組來存儲。所以載入圖片是會佔用大量的內存。
場景和原因我們都分析完了,下面我們來看看如何解決這些問題。
二、解決大圖載入問題
首先先來解決大圖載入的問題,一般在實際應用中展示圖片時,因屏幕尺寸及布局顯示的原因,我們沒有必要載入原始大圖,只需要按照比例采樣縮放即可。這樣即節省內存又能保證圖片不失真,具體實施步驟如下:
1、在不載入圖片內容的基礎上,去解碼圖片得到圖片的尺寸信息
這里需要用的BitmapFactory的decode系列方法和BitmapFactory.Options。當使用decode系列方法載入圖片時,一定要將Options的inJustDecodeBounds屬性設置為true。
BitmapFactory.Options ptions = new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeFile(path, options);2、根據獲取的圖片的尺寸和要展示在界面的尺寸計算縮放比例。public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height reqHeight || width reqWidth) { if (width height) { inSampleSize = Math.round((float) height / (float) reqHeight); } else { inSampleSize = Math.round((float) width / (float) reqWidth); } } return inSampleSize; }3、根據計算的比例縮放圖片。//計算圖片的縮放比例 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; Bitmap bitmap= BitmapFactory.decodeFile(path, options);
根據縮放比例,會比原始大圖節省很多內存,效果圖如下:

三、批量載入大圖
下面我們看看如何批量載入大圖,首先第一步還是我們上面所講到的,要根據界面展示圖片控制項的大小來確定圖片的縮放比例。在此我們使用gridview載入本地圖片為例,具體步驟如下:
1、通過系統提供的contentprovider載入外部存儲器中的所有圖片地址private void loadPhotoPaths(){ Cursor cursor= getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null); while(cursor.moveToNext()){ String path = cursor.getString(cursor.getColumnIndex(MediaColumns.DATA)); paths.add(path); } cursor.close(); }2、自定義adapter,在adapter的getview方法中載入圖片@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder=null; if(convertView==null){ convertView = LayoutInflater.from(this.mContext).inflate(R.layout.grid_item_layout, null); holder = new ViewHolder(); holder.photo=(ImageView)convertView.findViewById(R.id.photo); convertView.setTag(holder); }else{ holder=(ViewHolder)convertView.getTag(); } final String path = this.paths.get(position); holder.photo.setImageBitmap(imageLoader.getBitmapFromCache(path)); return convertView; }
通過以上關鍵兩個步驟後,我們發現程序運行後,用戶體驗特別差,半天沒有反應,很明顯這是因為我們在主線程中載入大量的圖片,這是不合適的。在這里我們要將圖片的載入工作放到子線程中進行,改造自定義的ImageLoader工具類,為其添加一個線程池對象,用來管理用於下載圖片的子線程。
private ExecutorService executor; private ImageLoader(Context mContxt) { super(); executor = Executors.newFixedThreadPool(3); } //載入圖片的非同步方法,含有回調監聽 public void loadImage(final ImageView view, final String path, final int reqWidth, final int reqHeight, final onBitmapLoadedListener callback){ final Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: Bitmap bitmap = (Bitmap)msg.obj; callback.displayImage(view, bitmap); break; default: break; } } }; executor.execute(new Runnable() { @Override public void run() { Bitmap bitmap = loadBitmapInBackground(path, reqWidth, reqHeight); putBitmapInMemey(path, bitmap); Message msg = mHandler.obtainMessage(1); msg.obj = bitmap; mHandler.sendMessage(msg); } }); }
通過改造後用戶體驗明顯好多了,效果圖如下:

雖然效果有所提升,但是在載入過程中還存在兩個比較嚴重的問題:
1、 圖片錯位顯示
2、 當我們滑動速度過快的時候,圖片載入速度過慢
經過分析原因不難找出,主要是因為我們時候holder緩存了grid的item進行重用和線程池中的載入任務過多所造成的,只需要對程序稍作修改,具體如下:
Adapter中:
holder.photo.setImageResource(R.drawable.ic_launcher); holder.photo.setTag(path); imageLoader.loadImage(holder.photo, path, DensityUtil.dip2px(80), DensityUtil.dip2px(80), new onBitmapLoadedListener() { @Override public void displayImage(ImageView view, Bitmap bitmap) { String imagePath= view.getTag().toString(); if(imagePath.equals(path)){ view.setImageBitmap(bitmap); } } });
ImageLoader中:
executor.execute(new Runnable() { @Override public void run() { String key = view.getTag().toString(); if (key.equals(path)) { Bitmap bitmap = loadBitmapInBackground(path, reqWidth, reqHeight); putBitmapInMemey(path, bitmap); Message msg = mHandler.obtainMessage(1); msg.obj = bitmap; mHandler.sendMessage(msg); } } });
為了獲得更好的用戶體驗,我們還可以繼續優化,即對圖片進行緩存,緩存我們可以分為兩個部分內存緩存磁碟緩存,本文例子載入的是本地圖片所有隻進行了內存緩存。對ImageLoader對象繼續修改,添加LruCache對象用於緩存圖片。
private ImageLoader(Context mContxt) { super(); executor = Executors.newFixedThreadPool(3); //將應用的八分之一作為圖片緩存 ActivityManager am=(ActivityManager)mContxt.getSystemService(Context.ACTIVITY_SERVICE); int maxSize = am.getMemoryClass()*1024*1024/8; mCache = new LruCachestring, bitmap=""(maxSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()*value.getHeight(); } }; } //存圖片到緩存 public void putBitmapInMemey(String path,Bitmap bitmap){ if(path==null) return; if(bitmap==null) return; if(getBitmapFromCache(path)==null){ this.mCache.put(path, bitmap); } } public Bitmap getBitmapFromCache(String path){ return mCache.get(path); }/string,
在loadImage方法中非同步載入圖片前先從內存中取,具體代碼請下載案例。
四、總結
總結一下解決載入圖片出現OOM的問題主要有以下方法:
1、 不要載入原始大圖,根據顯示控制項進行比例縮放後載入其縮略圖。
2、 不要在主線程中載入圖片,主要在listview和gridview中使用非同步載入圖片是要注意處理圖片錯位和無用線程的問題。
3、 使用緩存,根據實際情況確定是否使用雙緩存和緩存大小。
小夥伴們看懂了嘛?想要自己測試的,可以點擊下載工程運行測試哦!

⑦ android開發內存優化之如何有效避免oom

  1. 減小對象的內存佔用

  2. 內存對象的重復利用

  3. 避免對象的內存泄露

  4. 內存使用策略優化

  • 設計風格很大程度上會影響到程序的內存與性能,相對來說,如果大量使用類似Material Design的風格,不僅安裝包可以變小,還可以減少內存的佔用,渲染性能與載入性能都會有一定的提升。

  • 內存優化並不就是說程序佔用的內存越少就越好,如果因為想要保持更低的內存佔用,而頻繁觸發執行gc操作,在某種程度上反而會導致應用性能整體有所下降,這里需要綜合考慮做一定的權衡。

  • Android的內存優化涉及的知識面還有很多:內存管理的細節,垃圾回收的工作原理,如何查找內存泄漏等等都可以展開講很多。OOM是內存優化當中比較突出的一點,盡量減少OOM的概率對內存優化有著很大的意義。

⑧ android 圖片轉BASE64上傳提示java.lang.OutOfMemoryError

我最近也碰到了這個問題,但是網上沒有找到相關有效的直接解決方法。
後來看到了一篇解釋base64編碼原理的文章,研究了一番後解決了。
一般碰到這個問題的,都涉及到"大文件上傳"的問題,"大文件上傳"過程中除了base64編碼時可能OOM,其實還有其他問題,雖然提問中沒有提出,可能是因為這個問題還沒有解決,所以還沒有遇到其它問題,我就圍繞著"大文件上傳"來解決這個問題吧。
(提問時間在下看的清楚)

————————————————————

做項目的過程中碰到一個需求:
在java客戶端,使用http通信,把客戶端的本地文件通過http發送上傳到伺服器;

請求格式是xml(不管是json還是xml都是字元串,所以這個無所謂),中間包含[文件流字元串];

之前的做法是,把文件流通過base64編碼轉換為base64Byte,然後和其它字元串信息放到一起,post的時候通過HttpURLConnection的write方法寫入到伺服器中去,這個上傳的過程就完成了。

——————————
但是碰到一個問題,當文件體積較大時,從文件流轉換成base64Byte後,體積會很大,可能會導致OOM;
(以二進制流的方式保存,體積最小;以byte數組的方式保存,體積會相對變大一些;以String形式保存,體積最大;)

出錯原因是:
FileInputStream fis = new FileInputStream(file); //這一步打開了一個對准file准備進行讀取的文件指針,但是還沒有開始讀寫,file的相關數據沒有從本地載入到內存中來;所以即使file的體積有10G那麼大,這一步也是不會OOM的

//把文件流轉換為位元組數組
byte[] fileBytes;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] byteBuf = new byte[1024];
int count;
while((count=fis.read(buf))!=-1)
{
baos.write(buf,0,count); //實際上,如果文件體積比較大的話,不用轉碼,在這一步就可能OOM了
}
fileBytes= baos.toByteArray();

byte[] base64Bytes = Base64.encodeBase64(fileBytes); //在這一步也可能OOM
(文件轉換為byte[]時,是有可能OOM的;而轉換為base64Bytes後,體積會增大1/3,所以有可能前一步沒有OOM,卻在這一步出現OOM;
為什麼轉碼後體積會增大1/3,後面我會解釋)

——————————
解決方法

既然file在本地沒有載入到內存來的時候不會出現內存溢出的情況,我就想到了一個解決的方法:分段上傳
(加大內存並不能從根本上解決內存溢出的問題,問題的根本原因不是內存不夠大,而是代碼有問題)

在本地的file通過HttpURLConnection的getOutputStream()進行write時,不是一次性全部寫入,而是循環配合flush進行寫入:
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int count;
while((count = fis.read(buf)) != -1)
{
os.write(Base64.encodeBase64(buf), 0, count);
os.flush();
}
(我從本地讀1024位元組,然後馬上上傳到伺服器,清空本地緩存,然後再從本地讀1024位元組,這樣循環讀取,即使文件有20G,理論上也不有OOM問題出現,因為我從本地文件中讀到的數據不會在內存中駐留)

——————————
解決問題的思路對了,但是出現了其他的細節問題
os.write(Base64.encodeBase64(buf), 0, count); //這一行代碼報錯了,出現了OOM
我搜集了一下資料,發現原因是:
HttpURLConnection的getOutputStream的實際對象是sun.net.<a href="http://www.http.PosterOutputStream" target="_blank">www.http.PosterOutputStream</a>,這個對象的flush方法代碼是空的,write配合flush,並沒有達到即時上傳數據的效果。PosterOutputStream其實是自己在本地維護了一個緩沖區,你通過write寫入的數據其實還是在這個本地的緩沖區里,只有當你getInputStream後,HttpURLConnection才會把這段緩沖區中的數據上傳到伺服器上。而flush達不到上傳數據,清空本地緩存的效果。

——————————
(我是不能通過getInputStream來刷新緩沖流的,因為那就不是分段上傳而是"分次"上傳了)
那這就不是我的思路的問題了。再去搜索解決方法後,得知:
在創建HttpURLConnection對象的時候,要調用一個方法
hurlc.setChunkedStreamingMode(1024); //設置分塊流模式 也就是分塊上傳 1024是getOutputStream維護的本地緩沖區的大小
調用該方法後,只要本地緩存區滿了,HttpURLConnection就會自動把緩沖區里的數據發送到伺服器,同時清空本地緩存(ps:HttpURLConnection的getOutputStream似乎是個抽象的工廠方法,在調用setChunkedStreamingMode方法後,我發現getOutputStream獲取到的實例對象從sun.net.<a href="http://www.http.PosterOutputStream" target="_blank">www.http.PosterOutputStream</a>變成了sun.net.<a href="http://www.protocol.http.HttpURLConnection$StreamingOutputStream" target="_blank">www.protocol.http.HttpURLConnection$StreamingOutputStream</a>)

——————————
果然,調用setChunkedStreamingMode方法後,os.write(Base64.encodeBase64(buf), 0, count);沒有再出現OOM異常了

但是,又出現了一個新的問題
我發現
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int count;
while((count = fis.read(buf)) != -1)
{
os.write(Base64.encodeBase64(buf), 0, count);
os.flush();
}
這段分段編碼寫入的代碼,其編碼所得結果,與非分段編碼所得結果是不一樣的
通過分段編碼上傳的圖片內容出現了錯誤

我通過下面代碼測試:
//分段編碼
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] buf1 = new byte[1024];
int count1;
while((count1 = file1.read(buf1)) != -1)
{
os1.write(Base64.encodeBase64(buf1), 0, count1);
os1.flush();
}
file1.close();
System.out.println(os1.toString());

//非分段編碼
ByteArrayOutputStream os2 = new ByteArrayOutputStream();
InputStream file2 = new FileInputStream(path);
byte[] buf2 = new byte[1024];
int count2;
while((count2 = file2.read(buf2)) != -1)
{
os2.write(buf2, 0, count2);
os2.flush();
}
file2.close();
System.out.println(new String(Base64.encodeBase64(os2.toByteArray())));

兩者的結果:
/9j/4AAQSkZJR...wDtUAVs7eF...
/9j/4AAQSkZJR...wDt89ymnxJ...
前面一段還是相同的,轉到後面,就開始南轅北轍了

——————————
原因我去網上找了一下但是沒有找到直接答案,但是看到一篇解釋base64編碼原理的文章
原文鏈接:<a href="http://www.cnblogs.com/luguo3000/p/3940197.html" target="_blank">http://www.cnblogs.com/luguo3000/p/3940197.html</a>

假設有文件A(txt文件,包含文本內容"ABCDEFG"),轉換為InputStream->byte[]後
它們的ASIIC碼分別對應65、66、67、68、69、70、71
二進製表現形式為:
1000001 1000010 1000011 1000100 1000101 1000110 1000111
對高位補零後:
01000001 01000010 01000011 01000100 01000101 01000110 01000111
在內存中的實際表現:


而base64編碼,使用的字元包括(A-Z、a-z、0-9、+、/、=)這些常規可讀字元,使用base64編碼的原因,用途,在於把一些亂碼字元、不可讀字元轉換為常規可讀字元;
(因為java底層的通信協議、或者說其它的通信協議,很多地方用到遠程通信這一塊的,對一些亂碼字元不支持傳輸,所以需要把亂碼字元轉換成常規可讀字元才能進行傳輸)

比如對於'矙'這個字元,部分傳輸協議的編碼集就不認識它,所以無法直接傳輸,必須base64轉碼
'矙'的UTF-8編碼值為30681,二進製表現形式為111011111011001->(0)111011111011001
需要兩個位元組來存儲01110111 11011001

base64編碼只有(A-Z、a-z、0-9、+、/、=)這些字元來表示。需要強調的是,在base64編碼規范中,字元'A'不等於65、'B'也不是66...。base64字元與數值(二進制值)的對應關系如下:
也就是說,常規字元'A'=65=01000001;而base64字元'A'=0=00000000;

base64字元代表的二進制值是無法直接表示'矙'這個字元的,因為base64字元的值范圍在0~63之間(二進制值在(00)000000~(00)111111之間)。
那如何通過(00)000000~(00)111111之間的數值來表示01110111 11011001呢?

這就是base64的編碼演算法
一個base64字元的二進制值在(00)000000~(00)111111之間,也就是說它可以表示000000~111111之間的二進制數,一個base64字元的有效位為後6位。如何通過以6bit為單位的base64字元表示以8bit為單位的常規位元組?
6和8的最小公倍數為24,即 每4個base64字元可以表示3個常規位元組;

回到剛才的文件A,編碼過程:
(初始文件A)->"ABCDEFG"
(轉UTF-8碼 int)->65 66 67 68 69 70 71
("ABCDEFG"的二進製表示;7位元組)->1000001 1000010 1000011 1000100 1000101 1000110 1000111
(高位補零)->01000001 01000010 01000011 01000100 01000101 01000110 01000111
(連寫)->
(按6bit為單位對所有bit進行分割;得到10位元組)->010000 010100 001001 000011 010001 000100 010101 000110 010001 11
(按6bit*4=8bit*3的對應關系再分割;得到3組6*4位元組)->(010000 010100 001001 000011) (010001 000100 010101 000110) (010001 11)
(高位補2個零;末尾的低位也補零)->(00010000 00010100 00001001 00000011) (00010001 00000100 00010101 00000110) (00010001 00110000)
(二進制值換算成十進制)->(16 20 9 3) (17 4 21 6) (17 48)
(按base64編碼的值-字元對應表,得出上面的十進制值對應的base64字元)->(Q U J D) (R E V G) (R w)
(每組base64字元都要求是4個,空白的位置補'='字元)->(Q U J D) (R E V G) (R w = =)
(文件A的最終轉碼結果)->QUJDREVGRw==
這里以文本文件作為演示,因為文本文件機器可讀人也可讀;實際情況中,很多時候轉碼的目標文件並不是文本文件,那就不能以可讀字元串形式表示了,會直接以二進制格式表示

體積增大的原因,是因為3位元組=24bit=分割成4個6bit-,對4個6bit高位補零後,就得到4個位元組
也就是說3個常規位元組經base64編碼後會生成4個base64位元組,這就是文件經base64轉碼後體積會增加1/3的原因

——————————
base64編碼原理解釋了,再看剛才的分段編碼
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] buf1 = new byte[1024];
int count1;
while((count1 = file1.read(buf1)) != -1)
{
os1.write(Base64.encodeBase64(buf1), 0, count1); //可以發現一個問題:Base64.encodeBase64(buf1)編碼後,體積會增加1/3,所以這里的Base64.encodeBase64(buf1)編碼轉換後的實際長度和count1並不相等,所以實際寫入到os1中的base64字元數只有Base64.encodeBase64(buf1)編碼產生的字元數的3/4
os1.flush();
}
file1.close();
System.out.println(os1.toString());

修改後:
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] byteBuf = new byte[1024];
byte[] base64ByteBuf;
while(file1.read(byteBuf) != -1)
{
base64ByteBuf = Base64.encodeBase64(byteBuf);
os1.write(base64ByteBuf, 0, base64ByteBuf.length);
os1.flush();
}
file1.close();
System.out.println(os1.toString());

——————————
修改後,發現分段編碼的結果發生了變化,跟之前不一樣了
但仍然不是正確的結果

原因在於,base64字元的基礎單位是(6bit*4=4位元組),而3個常規位元組(8bit*3)才能剛好產生4個base64位元組
根本原因在於,如果進行編碼的常規位元組數不是3的倍數,最後就會餘下1或2個位元組,而這1、2個位元組編碼的結果就會產生'='字元;
使用1024作為分段編碼緩沖時,編碼的結果是3+3+3+...+1
也就是每次都會餘1位元組

而沒有使用分段編碼時,當編碼到第1024個位元組時,"餘下"的1位元組會跟後面的位元組形成連續,就不會產生'='字元
(對一段byte字元進行base64編碼時,中間是絕不會產生'='字元的,因為只有在結尾才可能餘下1或2個位元組,所以對一段byte字元進行編碼時,只有結尾才可能產生1或2個'='補全字元)

——————————
解決方法是,使用3的公倍數作為緩沖區大小

修改後:
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] byteBuf = new byte[3*1000];
byte[] base64ByteBuf;
while(file1.read(byteBuf) != -1)
{
base64ByteBuf = Base64.encodeBase64(byteBuf);
os1.write(base64ByteBuf, 0, base64ByteBuf.length);
os1.flush();
}
file1.close();
System.out.println(os1.toString());
測試結果再次發生了改變
中間不再有'='字元了,因為中間每次都是3位元組3位元組的編碼,沒有餘下多餘的位元組
對比之後發現,中間段的結果已經正常了

——————————
但是,發現,結尾處兩個轉碼的結果有些許不同

原因在於,假設文件A的長度為3001個位元組;
在第二次循環讀取時,只讀到1個有效位元組,而byteBuf的剩餘2999個位元組都是無效位元組,而此時編碼時,卻把多餘的2999個無效位元組也編碼了進去
(如果是非分段轉碼,就不會出現這種情況)

解決方法:
ByteArrayOutputStream os1 = new ByteArrayOutputStream();
InputStream file1 = new FileInputStream(path);
byte[] byteBuf = new byte[3*1000];
byte[] base64ByteBuf;
int count1; //每次從文件中讀取到的有效位元組數
while((count1=file1.read(byteBuf)) != -1)
{
if(count1!=byteBuf.length) //如果有效位元組數不為3*1000,則說明文件已經讀到尾了,不夠填充滿byteBuf了
{
byte[] = Arrays.Of(byteBuf, count1); //從byteBuf中截取包含有效位元組數的位元組段
base64ByteBuf = Base64.encodeBase64(); //對有效位元組段進行編碼
}
else
{
base64ByteBuf = Base64.encodeBase64(byteBuf);
}

os1.write(base64ByteBuf, 0, base64ByteBuf.length);
os1.flush();
}
file1.close();
System.out.println(os1.toString());

至此,base64分段編碼才算大功告成。大文件上傳核心代碼才算大功告成。
其實代碼改起來非常簡單,但是不知道原因不知道原理的話,是無法無中生有的
對我本人來說原本只是想隨便答一下,但沒想到答的過程中發現自己有很多坑沒有發現。答的過程中把自己不懂的地方沒有發現的坑也完善了。不說碰到一個知識點就要追根究底,但實際開發中,每一個自己能親身碰到的實際問題都是鍛煉自己的絕佳機會,這種近距離觸碰問題、解決問題的機會是難得的。雖然開發中還有很多其它問題也很重要,但你沒有親手碰到過,是無法共鳴的。所以自己在開發中碰到了問題,還是建議大概弄清原因。
弄清原理後,即使以後出現這個問題的其它"變種",也能找到原因並自己解決,但僅僅粘貼復制無法做到這一點。

⑨ android 怎麼避免oom

在 Java中,JavaVM擁有自動管理內存的功能,Java的GC能夠進行垃圾回收,但是Android中如果ImageView使用過多的Bitmap的話,經常會報OOM(內存溢出)。 造成內存溢出及解決方案: 1.使用BitmapFactory.decodeStream替代createBitmap方法 原因是該方法直讀取圖片位元組,調用JNI>>nativeDecodeAsset()來完成decode,無需再使用java層的createBitmap。 2.使用壓縮讀取技術 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imageSdUri, options); final int height = options.outHeight; final int width = options.outWidth; options.inSampleSize = 1; int w = 320; int h = 480; h = w*height/width;//計算出寬高等比率 int a = options.outWidth/ w; int b = options.outHeight / h; options.inSampleSize = Math.max(a, b); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeFile(imageSdUri, options); 3.及時釋放Bitamp Bitmap對象在不使用時,我們應該先調用recycle()釋放內存,然後才它設置為null.雖然recycle()從源碼上看,調用它應該能立即釋放Bitmap的主要內存,但是測試結果顯示它並沒能立即釋放內存。但是我它應該還是能大大的加速Bitmap的主要內存的釋放。

⑩ Android Green插入10萬條數據OOM

下面寫個小程序測試一下。
private Runnable runnable = new Runnable() { @Override
public void run() {
List<Book> bookList = new ArrayList<>(); for (int i = 0; i < 5000; i++) {
Book book = new Book();
book.setUuid(UUID.randomUUID().toString());
book.setName("name"); //其他set方法略
bookList.add(book);
} try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

mBookDao.insertOrReplaceInTx(bookList);
Log.d(TAG, "插入book數據:" + bookList.size());
}
};private void insert() {
Log.d(TAG, "線程池開始");
mBookDao.deleteAll(); long time = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 200; i++) {
executorService.submit(runnable);
}
executorService.shutdown(); for (; ; ) { if (executorService.isTerminated()) { break;
} try {
executorService.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(TAG, "線程池完成:" + (System.currentTimeMillis() - time) + "ms");
}

runnable任務模擬1秒從網路拉取5000條數據並插入DB,insert方法使用線程池執行runnable任務。
執行時間超過1000秒,查看內存佔用超過180M。如果數據量更多,肯定會發生OOM,基本上可以定位是greenDAO的問題。現在需要在兩個方面優化,一是尋找內存佔用的原因,二是提高數據的插入速度。
查看內存堆
內存的佔用隨著insert的數據量越多而遞增,從中間mp出java堆,得到hprof文件。注意這個文件不是標准格式,只能用AndroidStudio打開。

圖1
右擊文件導出標準的hprof文件,用更加強大的MAT分析。

圖2

圖3
看到IdentityScope佔了一半內存,可以確定是greenDAO緩存了插入數據。
mBookDao.insertOrReplaceInTx(bookList);mBookDao.detachAll();

greenDAO的緩存功能是有用的,沒必要關閉,改成在插入數據後,調用一次detachAll,將identityScope清空。
public void detachAll() { if (identityScope != null) {
identityScope.clear();
}
}

重建索引
對表插入大量數據,如果中間沒有涉及到業務,可以先失效索引,待插入完成後重建索引。
String sql = "drop index index_isbn";
mDb.execSQL(sql);
sql = "drop index index_publisherid";
mDb.execSQL(sql);
sql = "drop index index_author";
mDb.execSQL(sql);

插入數據前,drop掉表中的索引。沒有見到greenDAO有操作索引的方法,直接執行sql命令。
sql = "create index index_isbn on book(isbn)";
mDb.execSQL(sql);
sql = "create index index_publisherid on book(publisherid)";
mDb.execSQL(sql);
sql = "create index index_author on book(author)";
mDb.execSQL(sql);

插入數據完成後,重建索引。最後執行100w數據插入大約耗時450秒,比什麼都不做快了兩三倍。
非同步操作
上一個步驟的耗時包含了模擬網路和資料庫操作的時間,使用多線程將兩個環節分離,可以減少總時間。
greenDAO提供了AsyncSession這個非同步操作類,使用Session.startAsyncSession()獲取實例,內部實現使用了線程池和阻塞隊列,原理很簡單不用多講。
mAsyncSession.runInTx(new Runnable() { @Override
public void run() {
mBookDao.insertOrReplaceInTx(bookList);
mBookDao.deleteAll();
}
});

獲取數據後,提交給AsyncSession非同步插入資料庫。要注意在合適地方使用waitForCompletion,等待AsyncSession完成已有任務。如果獲取數據速度很快,而操作資料庫很慢,會導致過多數據緩存在AsyncSession的內部阻塞隊列。
最後測試一下100w數據插入資料庫,耗時不到150秒,又快了幾倍。

作者:展翅而飛
鏈接:https://www.jianshu.com/p/6589c6d3f551
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。

熱點內容
加密區的圖片 發布:2025-01-18 20:22:17 瀏覽:473
key文件加密 發布:2025-01-18 20:12:07 瀏覽:735
etl伺服器怎麼用 發布:2025-01-18 20:08:18 瀏覽:280
硫酸鎂演算法 發布:2025-01-18 19:53:00 瀏覽:669
華為什麼時候做安卓 發布:2025-01-18 19:44:23 瀏覽:712
電腦超凡先鋒選則不了伺服器 發布:2025-01-18 19:23:46 瀏覽:961
wifi賬號wifi賬號密碼怎麼修改 發布:2025-01-18 19:17:07 瀏覽:78
餐飲消毒液如何配置 發布:2025-01-18 19:11:01 瀏覽:591
入侵php 發布:2025-01-18 19:01:09 瀏覽:802
存儲的下標范圍 發布:2025-01-18 19:00:57 瀏覽:338