Android 下载 pdf 文件并查看

2019-03-06

目录:

  1. 功能需求–分析
  2. 具体实现–动手
  3. 源码分析–优化
  4. 造轮子–demo
  5. 总结

1. 功能需求–分析


文字描述:点击 Button 下载 pdf 文件,并在应用内显示。

具体展示:
2019-3-6

思考实现方式:
[1] 文件下载–AsyncHttpClient;
[2] 应用内打开 pdf 文件 – pdfviewpager;
尝试过 android-pdf-viewer 开源库,对 assets 目录下的文件打开很友好,但是对于下载后显示 pdf 文件有时会闪退。
也尝试过 TBS SDK , 对于异常情况显示出一片空白,最终用了pdfviewpager ,终于可以了。

#2. 具体实现–动手

2.1 add compile in your build.gradle

compile ‘es.voghdev.pdfviewpager:library:1.0.6’

2.2 use it in your Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
[ 1、Button 点击]
case R.id.btn_download:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Intent intent = new Intent(FlightCertificateInfoActivity.this, WatchPDFActivity.class);
intent.putExtra("etno", etNo);
startActivity(intent);
} else {
loadPdf(etNo);
}
break;


[2、WatchPDFActivity.class]

import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.pdf.PdfRenderer;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.jiuair.booking.R;
import com.jiuair.booking.tools.AsyncHttpUtils;
import com.jiuair.booking.tools.ViewTool;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;

import org.apache.http.Header;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class WatchPDFActivity extends FragmentActivity implements View.OnClickListener {

// private PDFView pdfView;
private ImageView mImageView;
private ParcelFileDescriptor mFileDescriptor;
private PdfRenderer mPdfRenderer;
private PdfRenderer.Page mCurrentPage;
private Button mButtonPrevious;
private Button mButtonNext;
private LinearLayout ll_1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_watch_pdf);
mImageView = (ImageView) findViewById(R.id.image);
ll_1 = (LinearLayout) findViewById(R.id.ll_1);
// pdfView = (PDFView) findViewById(R.id.pdfView);
mButtonPrevious = (Button) findViewById(R.id.previous);
mButtonNext = (Button) findViewById(R.id.next);
// Bind events.
mButtonPrevious.setOnClickListener(this);
mButtonNext.setOnClickListener(this);
String id = getIntent().getStringExtra("id");
String abpno = getIntent().getStringExtra("abpno");
loadPdf(id, abpno);
}

/**
* @param etno
* @param abpno 下载并打开pdf文件
*/
private void loadPdf(String id, String abpno) {
String url = "/downloadFile";
RequestParams params = new RequestParams();
params.put("id", id);
final ProgressDialog layerMask = ViewTool.showLayerMask(WatchPDFActivity.this);
AsyncHttpUtils.get(url, params, new AsyncHttpResponseHandler() {
@Override
public void onStart() {
// called before request is started
Log.e("ATG", "开始获取pdf》》");
}


@Override
public void onSuccess(int statusCode, Header[] headers,
byte[] response) {
layerMask.dismiss();
comLoad(response);

}

@Override
public void onFailure(int statusCode, Header[] headers,
byte[] errorResponse, Throwable e) {
e.printStackTrace();
Toast.makeText(WatchPDFActivity.this, "PDF打开失败", Toast.LENGTH_SHORT).show();
WatchPDFActivity.this.finish();
//called when response HTTP status is "4XX" (eg. 401, 403, 404)
}

@Override
public void onRetry(int retryNo) {
// called when request is retried
}
});
}

/**
* @param response 普通的下载pdf到本地然后再打开pdf
*/

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void comLoad(byte[] response) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
try {

if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
String fileName = Environment.getExternalStorageDirectory() + "/cxy/" + "resume.pdf";
file = new File(fileName);
// 目录不存在创建目录
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(response);

mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
// This is the PdfRenderer we use to render the PDF.
if (mFileDescriptor != null) {
mPdfRenderer = new PdfRenderer(mFileDescriptor);
}

showPage(0);
}

} catch (Exception e) {
e.printStackTrace();
Toast.makeText(WatchPDFActivity.this, "PDF打开失败", Toast.LENGTH_SHORT).show();
WatchPDFActivity.this.finish();
} finally {
try {
if (bos != null) {
bos.close();
}
if (fos != null) {
fos.close();
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDestroy() {
super.onDestroy();
try {
closeRenderer();
} catch (IOException e) {
e.printStackTrace();
}
}


@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void closeRenderer() throws IOException {
if (null != mCurrentPage) {
mCurrentPage.close();
}
if (null != mPdfRenderer)
mPdfRenderer.close();
if (null != mFileDescriptor)
mFileDescriptor.close();
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void showPage(int index) {
if (mPdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != mCurrentPage) {
mCurrentPage.close();
}
// Use `openPage` to open a specific page in PDF.
mCurrentPage = mPdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),
Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
// To render a portion of the page, use the second and third parameter. Pass nulls to get
// the default result.
// Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// We are ready to show the Bitmap to user.
mImageView.setImageBitmap(bitmap);
updateUi();
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void updateUi() {
int index = mCurrentPage.getIndex();
int pageCount = mPdfRenderer.getPageCount();
mButtonPrevious.setEnabled(0 != index);
mButtonNext.setEnabled(index + 1 < pageCount);
if (pageCount <= 1) {
ll_1.setVisibility(View.GONE);
} else {
ll_1.setVisibility(View.VISIBLE);
}
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.previous: {
// Move to the previous page
showPage(mCurrentPage.getIndex() - 1);
break;
}
case R.id.next: {
// Move to the next page
showPage(mCurrentPage.getIndex() + 1);
break;
}
}
}
}

3. 框架分析–优化


3.1 android-async-http
需求中的 pdf 文件一般是一页,大小为 23 kb,属于小文件了,而且整个项目使用的是 android-async-http 框架,所以就用了。但如果是大文件,考虑到网络问题,可能有需要暂停、或者有 wifi 了再下载的情况,那么就应该用 HttpURLConnection 实现断点续传,AsyncTask<String, Integer, Boolean>异步管理下载文件 。

3.2 pdfviewpager
如果需求没有指定一定要在应用内打开,更优化的操作步骤是 [1] 判断用户手机是否有安装 wps 之类的文件查看器,[2] 如果有则调用 WPS 等应用打开,反之则按上述步骤,先判断文件是否存在,[3] 存在则直接打开,反之下载后再打开。

4. 造轮子–demo


demo 地址

5. 总结


最近看了一本书《不敢止步(一个软件工匠的12年)》,作者大四那份工作的历程的感悟让我产生巨大的共鸣,从毕业到现在有快1年了,事情经历得多了就会有迷惑,掉坑里多了就会去反思,愿不负 cxy 这个名字!

文章是 Android 面向需求开发系列中的一文,更多相关文章请关注。如若有什么问题,也可以通过扫描二维码发消息给我。转载请注明出处,谢谢!

二维码

作者:Emily CH
2019年3月7日