安卓如何打开PDF文件

42

我正在开发一款Android应用程序,需要打开一些文件。

这是使用意图的代码:

public class FacturaActivity extends Activity {

    (...)

    public void downloadInvoice(View view) {
        File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+ filename);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(file),"application/pdf");
        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        startActivity(intent);
    }
}

文件位于SD卡的根目录,我可以手动打开它。

问题

当它到达startActivity(intent)时,应用程序关闭。 我认为问题在AndroidManifest.xml文件中,但我不知道如何正确放置它。

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="8" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:name="###.MyApplication" > <!--cant show complete name-->
    <activity
        android:name="###.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity 
        android:name=".FacturaActivity" >
    </activity>

</application>

日志记录器(LogCat)

07-03 15:49:13.094: E/AndroidRuntime(1032): FATAL EXCEPTION: main
07-03 15:49:13.094: E/AndroidRuntime(1032): java.lang.IllegalStateException: Could not execute method of the activity
(...)
07-03 15:49:13.094: E/AndroidRuntime(1032): Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=file:///mnt/sdcard/201209_F2012212782.PDF typ=application/pdf flg=0x40000000 }
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1408)
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Instrumentation.execStartActivity(Instrumentation.java:1378)
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Activity.startActivityForResult(Activity.java:2817)
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Activity.startActivity(Activity.java:2923)

你能帮我完成AndroidManifest吗?或者我该如何打开那个PDF文件?


似乎您的安卓手机未安装任何PDF阅读器应用,请添加一个。 - user529543
我可以使用ThinkFree PDF Viewer打开其他PDF文件。 - Lyd
这篇博客文章可以帮助你使用ContentProvider(现在已被推荐):http://www.blogc.at/2014/03/23/share-private-files-with-other-apps-fileprovider/ - Sakiboy
6个回答

109
问题在于没有安装应用程序来处理打开 PDF 文件。您应该使用 Intent Chooser,像这样:
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+ filename);
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(Uri.fromFile(file),"application/pdf");
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

Intent intent = Intent.createChooser(target, "Open File");
try {
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // Instruct the user to install a PDF reader here, or something
}   

我已经在我的手机上尝试过了。当我打开另一个应用程序的一些PDF文件时,它会提示我注册ThinkFree,我选择“稍后”,然后“ThinkFree PDF Viewer Mobile for Android”就会打开,显示PDF文件。但是使用您的代码,它只会要求注册ThinkFree。如果我选择“稍后”,它就会关闭。 - Lyd
1
这与此代码无关。您无法控制ThinkFree处理此意图的方式。尝试安装替代的PDF查看器。 - Paul Burke
我使用Intent intent = new Intent(this, FacturaActivity.class); startActivity(intent); 在onResume()中没有写任何内容。错误是:“抱歉!应用程序###(进程com.###)意外停止。请重试。[强制关闭]”,并返回到另一个之前的活动(不是处理pdf查看器的活动),没有任何应该有的数据。我想返回到启动PDF查看器的同一活动。 - Lyd
我指的是来自日志的实际错误信息(异常堆栈)。在用户界面上显示的“强制关闭”消息对于所有异常都是相同的。 - Paul Burke
我的应用程序提示文件不存在的消息。我在AndroidStudioProjects/Myapp/app/src/main/res/myfilefolder/test.pdf中有这个文件。你能给出文件路径的语法吗?我正在使用Android Studio并在模拟器中尝试它。 - Apollon
显示剩余6条评论

13

从API 24开始,向另一个应用程序发送file:// URI将会抛出FileUriExposedException异常。相反,请使用FileProvider来发送content:// URI:

public File getFile(Context context, String fileName) {
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
        return null;
    }

    File storageDir = context.getExternalFilesDir(null);
    return new File(storageDir, fileName);
}

public Uri getFileUri(Context context, String fileName) {
    File file = getFile(context, fileName);
    return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
}

您还必须在清单文件中定义FileProvider:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.mydomain.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

示例文件 file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="name" path="path" />
</paths>

根据需要替换“名称”和“路径”。

为了让PDF查看器访问文件,您还需要在意图中添加FLAG_GRANT_READ_URI_PERMISSION标志:

private void displayPdf(String fileName) {
    Uri uri = getFileUri(this, fileName);

    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(uri, "application/pdf");

    // FLAG_GRANT_READ_URI_PERMISSION is needed on API 24+ so the activity opening the file can read it
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_GRANT_READ_URI_PERMISSION);

    if (intent.resolveActivity(getPackageManager()) == null) {
        // Show an error
    } else {
        startActivity(intent);
    }
}

有关更多详细信息,请参阅FileProvider文档


8
String dir="/Attendancesystem";

 public void displaypdf() {

        File file = null;
            file = new File(Environment.getExternalStorageDirectory()+dir+ "/sample.pdf");
        Toast.makeText(getApplicationContext(), file.toString() , Toast.LENGTH_LONG).show();
        if(file.exists()) {
            Intent target = new Intent(Intent.ACTION_VIEW);
            target.setDataAndType(Uri.fromFile(file), "application/pdf");
            target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

            Intent intent = Intent.createChooser(target, "Open File");
            try {
                startActivity(intent);
            } catch (ActivityNotFoundException e) {
                // Instruct the user to install a PDF reader here, or something
            }
        }
        else
            Toast.makeText(getApplicationContext(), "File path is incorrect." , Toast.LENGTH_LONG).show();
    }

当您的文件存储在内部存储器中时,请使用上述代码。 - Sanjit Majee

2

Kotlin版本如下(@paul-burke回答的更新版本):

最初的回答

fun openPDFDocument(context: Context, filename: String) {
    //Create PDF Intent
    val pdfFile = File(Environment.getExternalStorageDirectory().absolutePath + "/" + filename)
    val pdfIntent = Intent(Intent.ACTION_VIEW)
    pdfIntent.setDataAndType(Uri.fromFile(pdfFile), "application/pdf")
    pdfIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)

    //Create Viewer Intent
    val viewerIntent = Intent.createChooser(pdfIntent, "Open PDF")
    context.startActivity(viewerIntent)
}

1
你没有打开文件的权限是因为你没有授权其他应用程序在你的意图中打开或查看该文件。要授权其他应用程序打开下载的文件,请包括以下标志:FLAG_GRANT_READ_URI_PERMISSION。
Intent browserIntent = new Intent(Intent.ACTION_VIEW);
browserIntent.setDataAndType(getUriFromFile(localFile), "application/pdf");
browserIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|
Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(browserIntent);

并且对于函数:

getUriFromFile(localFile)

private Uri getUriFromFile(File file){
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
        return Uri.fromFile(file);
    }else {
        return FileProvider.getUriForFile(itemView.getContext(), itemView.getContext().getApplicationContext().getPackageName() + ".provider", file);
    }
}

1

希望能在以上答案中提供帮助。代码几乎相同,只是在Android Jetpack Compose可组合中(因此使用Kotlin)。此外,我还进行了两个视频的讲解。这里是快速版本(时长为10分钟)。

如果您想要完整的内容,这个30分钟的巨兽级屏幕展示将为您提供大量上下文和代码的A/B选项。

如果您想查看代码,可以在此存储库分支中找到它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接