最近項目裏,需要集成PDF閱讀,翻閱了很多網站,發現Android系統不支持PDF閱讀,網上現有的庫和插件,都會增大apk的體積,綜合比較了一下,解決方案有如下幾種:
1.谷歌提供了在線閱讀,在webView中調用GoogleDocs
但是由於國內手機無法獲取Google提供支持,所以這種方案基本被否決,其使用方式如下:
public void setDocumentPath(final String path) {
WebView webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setPluginsEnabled(true);
webView.loadUrl("https://docs.google.com/viewer?url=http://www.asce1885.com/cms/wwwroot/ng/downLoad/011615200732.pdf");
}
2.最簡單的方式就是跳第三方的瀏覽器,下載閱讀
下載完成後,如果瀏覽器含有PDF閱讀插件,便可以在瀏覽器中直接打開,否則還要下載PDF閱讀器方可閱讀,使用方式如下:
public Intent getPdfFileIntent(File file) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/pdf");
return Intent.createChooser(intent, "Open File");
}
3.利用騰訊X5內核瀏覽器的Tbs,下載閱讀
下載完成後,首先加載X5插件,中間會有一段時間特別緩慢,官方建議可以再application中進行加載初始化,但這會導致APP啓動時間增長,更糟糕的是卡在啓動頁,出現的原因:手機沒有X5內核瀏覽器,需要開啓服務下載(鑑於自己的項目,僅僅是猜測),其具體的使用方法如下(我這裏自己封裝了一個下載工具,下面的幾種方式都採用這個工具):
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import com.mysteel.android.steelphone.bean.ebe.EBPdfMsg;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
import static android.app.DownloadManager.ACTION_DOWNLOAD_COMPLETE;
import static android.app.DownloadManager.ACTION_NOTIFICATION_CLICKED;
import static android.app.DownloadManager.STATUS_FAILED;
import static android.app.DownloadManager.STATUS_PAUSED;
import static android.app.DownloadManager.STATUS_PENDING;
import static android.app.DownloadManager.STATUS_RUNNING;
import static android.app.DownloadManager.STATUS_SUCCESSFUL;
public class DownloadPdfUtils {
//下載器
private DownloadManager downloadManager;
//上下文
private Context mContext;
//下載的ID
private long downloadId;
private boolean mReceiverTag = false;
public DownloadPdfUtils(Context context) {
if (context == null) {
return;
}
this.mContext = context;
}
public void downloadUrl(String url, String name) {
//創建下載任務
Request request = new Request(Uri.parse(url));
//移動網絡下是否允許
request.setAllowedOverRoaming(false);
//在通知欄中顯示,默認就是顯示的
request.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
request.setVisibleInDownloadsUi(true);
//設置下載路徑
final String sdcard = Environment.getExternalStorageDirectory().getAbsolutePath();
final String pdfPath = sdcard + "/mysteel/pdf";
if (!(new File(pdfPath)).exists()) {
(new File(pdfPath)).mkdirs();
}
File file = new File(pdfPath, name);
request.setDestinationUri(Uri.fromFile(file));
downloadManager = ((DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE));
removePdfFile();
downloadId = downloadManager.enqueue(request);
mContext.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
mReceiverTag = true;
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == ACTION_DOWNLOAD_COMPLETE) {
checkStatus();
}
}
};
private BroadcastReceiver receiver1 = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == DownloadManager.ACTION_NOTIFICATION_CLICKED) {
downloadManager.remove(downloadId);
}
}
};
/**
* 下載狀態
*/
private void checkStatus() {
Query query = new Query();
//通過下載的ID查找
Cursor cursor = downloadManager.query(query);
if (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
case STATUS_PAUSED:
break;
case STATUS_PENDING:
break;
case STATUS_RUNNING:
break;
case STATUS_SUCCESSFUL:
EBPdfMsg event = new EBPdfMsg();
event.setUrl(queryUrl());
EventBus.getDefault().post(event);
break;
case STATUS_FAILED:
break;
default:
break;
}
}
cursor.close();
mContext.unregisterReceiver(receiver);
mReceiverTag = false;
}
/**
* 返回文件下載路徑
*
* @return
*/
public String queryUrl() {
String url = "";
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cursor = downloadManager.query(query);
if (cursor != null) {
if (cursor.moveToFirst()) {
url = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
}
}
assert cursor != null;
cursor.close();
}
return url;
}
/**
* 取消下載
*/
public void cancelDownload() {
mContext.registerReceiver(receiver1, new IntentFilter(ACTION_NOTIFICATION_CLICKED));
}
/**
* 取消廣播註冊
*/
public void unRegister() {
if (mReceiverTag) {
mContext.unregisterReceiver(receiver);
}
mContext.unregisterReceiver(receiver1);
}
/**
* 刪除PDF文件
*/
private void removePdfFile() {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cursor = downloadManager.query(query);
if (cursor != null) {
while (cursor.moveToNext()) {
if (cursor.getColumnIndex(DownloadManager.COLUMN_ID) != -1) {
downloadManager.remove(cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)));
}
}
}
assert cursor != null;
cursor.close();
}
}
具體的集成方式可以去騰訊官網查看:https://x5.tencent.com/
首先,在Application中初始化X5瀏覽,雖說這是一個輕量級的不會造成ANR但是還是採用服務比較穩妥,具體如下:
寫一個服務類:
import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
import com.tencent.smtt.sdk.QbSdk;
/**
* 預加載X5內核瀏覽器
*/
public class X5CorePreLoadService extends IntentService{
private static final String TAG = X5CorePreLoadService.class.getSimpleName();
public X5CorePreLoadService() {
super(TAG);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//在這裏添加我們要執行的代碼,Intent中可以保存我們所需的數據,
//每一次通過Intent發送的命令將被順序執行
initX5();
}
/**
* 初始化X5內核
*/
private void initX5() {
if (!QbSdk.isTbsCoreInited()) {
QbSdk.preInit(getApplicationContext(), null);// 設置X5初始化完成的回調接口
}
QbSdk.initX5Environment(getApplicationContext(), cb);
}
QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {
@Override
public void onViewInitFinished(boolean arg0) {
// TODO Auto-generated method stub
//初始化完成回調
}
@Override
public void onCoreInitFinished() {
// TODO Auto-generated method stub
}
};
}
其次,在Application中初始化:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
preInitX5Core();
}
/**
* 初始化X5內核
*/
private void preInitX5Core() {
//預加載x5內核
Intent intent = new Intent(this, X5CorePreLoadService.class);
startService(intent);
}
}
在Activity中顯示,這裏需要注意6.0權限,我用的EasyPermission,具體用法:https://github.com/googlesamples/easypermissions,TBS在Activity中使用
public class WebActivity extends AppCompatActivity implements TbsReaderView.ReaderCallback {
private static final int PERMISSION_CODE3 = 1;
private TbsReaderView mTbsReaderView;
private FrameLayout mPdf;
public static final String url = "";//PDF鏈接
private DownloadPdfUtils downloadUtils;
private String[] PERMISSION_ARRAYS3 = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
mPdf = findViewById(R.id.read_pdf);
mTbsReaderView = new TbsReaderView(this, this);
mPdf.addView(mTbsReaderView, new RelativeLayout.LayoutParams(-1, -1));
downloadUtils = new DownloadPdfUtils(this);
if (url != null) {
checkPermission();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(EBPdfMsg event) {
String urls = event.getUrl();
displayFile(new File(urls).getName());
}
/**
* 檢查是否有權限
*/
@AfterPermissionGranted(PERMISSION_CODE3)
private void checkPermission() {
if (EasyPermissions.hasPermissions(this, PERMISSION_ARRAYS3)) {
downloadUtils.downloadUrl(url, pdfName);
} else {
//Auto to do
}
}
/**
* PDF閱讀
*
* @param name
*/
private void displayFile(String name) {
Bundle bundle = new Bundle();
bundle.putString("filePath", Environment.getExternalStorageDirectory().getAbsolutePath() + "/mysteel/pdf" + File.separator + name);
bundle.putString("tempPath", Environment.getExternalStorageDirectory().getAbsolutePath() + "/mysteel/pdf");
boolean result = mTbsReaderView.preOpen(getFileType(name), false);
if (result) {
mTbsReaderView.openFile(bundle);
}
}
/**
* 文件類型是否是.pdf
*
* @param paramString
* @return
*/
public static String getFileType(String paramString) {
String str = "";
if (TextUtils.isEmpty(paramString)) {
return str;
}
int i = paramString.lastIndexOf('.');
if (i <= -1) {
return str;
}
try {
str = paramString.substring(i + 1);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
@Override
public void onCallBackAction(Integer integer, Object o, Object o1) {
}
}
activity的xml如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/read_pdf"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
4.利用PDFVIew進行加載,可能會使apk增大4M左右,其依賴地址:https://github.com/barteksc/AndroidPdfViewer
使用方式如下:
public class ReadPdfActivity extends AppCompatActivity {
private static final int PERMISSION_CODE3 = 1;
private PDFView mPdf ;
public static final String url = "";//PDF鏈接
private DownloadPdfUtils downloadUtils;
private String[] PERMISSION_ARRAYS3 = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
mPdf = findViewById(R.id.read_pdf);
downloadUtils = new DownloadPdfUtils(this);
if (url != null) {
checkPermission();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(EBPdfMsg event) {
String urls = event.getUrl();
displayFile(new File(urls).getName());
}
/**
* 檢查是否有權限
*/
@AfterPermissionGranted(PERMISSION_CODE3)
private void checkPermission() {
if (EasyPermissions.hasPermissions(this, PERMISSION_ARRAYS3)) {
downloadUtils.downloadUrl(url, pdfName);
} else {
//Auto to do
}
}
/**
* PDF閱讀
*
* @param name
*/
private void displayFile(String name) {
mReadPdf.fromFile(new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/mysteel/pdf" + File.separator + name))
.swipeHorizontal(false)
.enableDoubletap(true)
.defaultPage(0)
.onError(new OnErrorListener() {
@Override
public void onError(Throwable t) {
showEmpty();
}
})
.load();
}
}
5.利用PDFJS,這種效果不是很好,感興趣的可以去官網自行查看:https://github.com/mozilla/pdf.js/
總結:
這五種方式,體積最小的是騰訊的TBS,體積最大的PDFView,個人比較推崇使用PDFView,pdfjs這個實現方式有三種,服務器前後端配合這種我沒有使用,其他兩者種,在下拉滑動時,可能會出現空白頁,界面不太友好,但最終能顯示。當然刨去網絡問題還是Google docs最好,但是這種在國內無法使用。以上代碼中有誤的地方還望各位小夥伴指出。