如何提升HttpURLConnection加载Firebase PDF文件的速度?
如何提升从Firebase加载PDF的速度?
我现在能直接从Firebase打开PDF文件,但加载速度太慢——15MB的文件要等30秒左右,而且我的网络状况很好。我想试试增大缓冲区,但不知道具体怎么操作,也不确定这有没有用。请问怎么优化HttpURLConnection的下载速度?
相关代码
public class ViewPDFFiles extends AppCompatActivity { PDFView pdfView; ProgressBar pb; SearchView searchView; ArrayAdapter kek; ListView myPDFListView; DatabaseReference databaseReference; StorageReference storageReference; List<uploadPDF> uploadPDFS; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.viewallpdf_activity); searchView = findViewById(R.id.name_input); if(getResources().getBoolean(R.bool.portrait_only)){ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } int id = getResources().getIdentifier("android:id/search_src_text", null, null); EditText searchEditText = (EditText) searchView.findViewById(id); if (searchEditText != null) { searchEditText.setGravity(Gravity.CENTER); } searchView.setIconifiedByDefault(false); searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() { private boolean activityStartup = true; @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { if (activityStartup) { searchView.clearFocus(); activityStartup = false; } } } }); searchView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { searchView.setIconified(false); } }); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { return false; } @Override public boolean onQueryTextChange(String s) { kek = (ArrayAdapter) myPDFListView.getAdapter(); kek.getFilter().filter(s); return false; } }); AlertDialog.Builder builder = new AlertDialog.Builder(ViewPDFFiles.this); builder.setCancelable(true); builder.setTitle("Предупреждение!"); builder.setMessage("Файлы с темами могут открываться некоторое время и отображать пустой белый экран(10-30 секунд), не закрывайте страницу"); builder.setPositiveButton("Хорошо,я подожду", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.setNegativeButton("", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); AlertDialog dialog = builder.create(); dialog.show(); storageReference = FirebaseStorage.getInstance().getReference(); myPDFListView = (ListView) findViewById(R.id.files_list); uploadPDFS = new ArrayList<>(); viewAllFiles(); myPDFListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { setContentView(R.layout.viewfile_activity); if(getResources().getBoolean(R.bool.portrait_only)){ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } pdfView=findViewById(R.id.pdfView); uploadPDF uploadPDF = uploadPDFS.get(position); new RetrievePDFStream().execute(uploadPDF.url); } }); } class RetrievePDFStream extends AsyncTask<String,Void,InputStream> { @Override protected InputStream doInBackground(String... strings) { InputStream inputStream = null; try { URL urlx = new URL(strings[0]); HttpURLConnection urlConnection = (HttpURLConnection) urlx.openConnection(); if (urlConnection.getResponseCode() == 200) { inputStream = new BufferedInputStream(urlConnection.getInputStream()); } urlConnection.setConnectTimeout(1000); } catch (IOException e) { return null; } return inputStream; } @Override protected void onPostExecute(InputStream inputStream) { pdfView.fromStream(inputStream) .enableSwipe(true) .load(); } } private void viewAllFiles() { databaseReference = FirebaseDatabase.getInstance().getReference("uploads"); databaseReference.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { for(DataSnapshot postSnapshot : snapshot.getChildren()){ uploadPDF uploadPDF = postSnapshot.getValue(com.tokha.diplom.uploadPDF.class); uploadPDFS.add(uploadPDF); } String[] uploads = new String[uploadPDFS.size()]; for(int i = 0;i<uploads.length;i++){ uploads[i] = uploadPDFS.get(i).getName(); } ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, uploads){ @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { View view = super.getView(position, convertView, parent); TextView myText = (TextView) view.findViewById(android.R.id.text1); myText.setTextColor(Color.BLACK); return view; } }; myPDFListView.setAdapter(adapter); } @Override public void onCancelled(@NonNull DatabaseError error) { } }); } public void goBack(View view) { finish(); } }
优化方案
增大缓冲区大小
默认BufferedInputStream的缓冲区是8KB,手动设置更大的缓冲区(比如64KB或128KB)可以减少IO操作次数,提升读取效率。修改RetrievePDFStream中的代码:inputStream = new BufferedInputStream(urlConnection.getInputStream(), 1024 * 64); // 64KB缓冲区修复超时设置
你当前的setConnectTimeout调用在getResponseCode()之后,完全没有生效,而且1秒的连接超时过短,容易导致连接失败。把超时设置移到打开连接后、获取响应码前:URL urlx = new URL(strings[0]); HttpURLConnection urlConnection = (HttpURLConnection) urlx.openConnection(); urlConnection.setConnectTimeout(5000); // 5秒连接超时 urlConnection.setReadTimeout(15000); // 15秒读取超时 if (urlConnection.getResponseCode() == 200) { inputStream = new BufferedInputStream(urlConnection.getInputStream(), 1024 * 64); }启用HTTP缓存
给HttpURLConnection启用缓存,避免重复下载相同文件,节省带宽和时间:urlConnection.setUseCaches(true); // 可选:设置全局缓存(需要在Application初始化或Activity onCreate中调用一次) File cacheDir = getCacheDir(); HttpResponseCache.install(cacheDir, 10 * 1024 * 1024); // 10MB缓存空间改用Firebase Storage SDK获取流
不要用HttpURLConnection直接请求Firebase URL,官方SDK内部做了连接池、重试等优化,效率更高。替换RetrievePDFStream的逻辑:// 在ListView的Item点击事件中替换原有AsyncTask调用 uploadPDF uploadPDF = uploadPDFS.get(position); StorageReference pdfRef = FirebaseStorage.getInstance().getReferenceFromUrl(uploadPDF.url); pdfRef.getStream().addOnSuccessListener(taskSnapshot -> { InputStream stream = taskSnapshot.getStream(); pdfView.fromStream(stream) .enableSwipe(true) .load(); // 记得在合适的时机关闭流 try { stream.close(); } catch (IOException e) { e.printStackTrace(); } }).addOnFailureListener(e -> { // 处理加载失败的情况 });分块加载PDF
如果你使用的是AndroidPdfViewer库,开启分块加载可以让PDF先显示已下载的部分,不需要等整个文件下载完成:pdfView.fromStream(inputStream) .enableSwipe(true) .enableDoubletap(true) .swipeHorizontal(false) .enableAntialiasing(true) .load(); // 确保使用最新版本的AndroidPdfViewer,新版本对加载逻辑有优化
内容的提问来源于stack exchange,提问作者SmyhkS Channe
相关产品推荐
相关产品推荐

