无法在外部应用程序中打开PDF文件

15

当用户点击按钮时,我希望能够打开一个PDF文件。 目前,我使用以下代码来实现此目的:

Uri path = Uri.fromFile(new File("file:///android_asset/regola11_1.pdf"));
            Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
            pdfIntent.setDataAndType(path, "application/pdf");
            pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(pdfIntent);

但它无法正常工作。

当我选择使用Adobe Acrobat时,会弹出一条消息,显示为Toast,上面写着:

"This file could not be accessed Check the location or the network and try again."

当我尝试使用Drive PDF Viewer时,出现了

"Cannot display PDF ( regola11_1.pdf cannot be opened)"

该PDF文件存储在

app > build > intermediates > assets
问题出在哪里? 编辑 现在我正在使用以下代码:
File file = new File("\"file:///android_asset/regola11_1.pdf");
            Uri path = Uri.fromFile(file);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(path, "application/pdf");
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

            try {
                context.startActivity(intent);
            }
            catch (ActivityNotFoundException e) {
                Toast.makeText(context, "No application available to view PDF", Toast.LENGTH_LONG).show();
            }

但是,当我尝试通过点击按钮打开PDF文件时,应用程序崩溃。

这是我收到的日志:

05-31 10:05:25.132  24474-24474/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.andrey.andreyvedis.iamaref, PID: 24474
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.content.Context.startActivity(android.content.Intent)' on a null object reference
        at com.andrey.andreyvedis.iamaref.FragmentR11.onClick(FragmentR11.java:147)
        at android.view.View.performClick(View.java:4781)
        at android.view.View$PerformClick.run(View.java:19873)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5293)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

这是我的班级:

public class FragmentR11 extends Fragment implements  View.OnClickListener{
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
Context context;



// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;


/**
 * Use this factory method to create a new instance of
 * this fragment using the provided parameters.
 *
 * @param param1 Parameter 1.
 * @param param2 Parameter 2.
 * @return A new instance of fragment FragmentR11.
 */
// TODO: Rename and change types and number of parameters
public static FragmentR11 newInstance(String param1, String param2) {
    FragmentR11 fragment = new FragmentR11();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

public FragmentR11() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }



}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_r11, container, false);


}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    getActivity().findViewById(R.id.bD1).setOnClickListener(this);
    getActivity().findViewById(R.id.bD2).setOnClickListener(this);
    getActivity().findViewById(R.id.bD3).setOnClickListener(this);
    getActivity().findViewById(R.id.bD4).setOnClickListener(this);
    getActivity().findViewById(R.id.bD5).setOnClickListener(this);
    getActivity().findViewById(R.id.bD6).setOnClickListener(this);
    getActivity().findViewById(R.id.bD7).setOnClickListener(this);
    getActivity().findViewById(R.id.bD8).setOnClickListener(this);
    getActivity().findViewById(R.id.bD9).setOnClickListener(this);
    getActivity().findViewById(R.id.bD10).setOnClickListener(this);
    getActivity().findViewById(R.id.bD11).setOnClickListener(this);
    getActivity().findViewById(R.id.bD12).setOnClickListener(this);
    getActivity().findViewById(R.id.bD13).setOnClickListener(this);
    getActivity().findViewById(R.id.bD14).setOnClickListener(this);
    getActivity().findViewById(R.id.bD15).setOnClickListener(this);
    getActivity().findViewById(R.id.bD16).setOnClickListener(this);
    getActivity().findViewById(R.id.bD17).setOnClickListener(this);


}

/**private void openPDF(final String pathToPDF) {
    File file = new File(pathToPDF);
    Uri path = Uri.fromFile(file);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    intent.setDataAndType(path, "application/pdf");
    try {
        startActivity(intent);
    } catch (ActivityNotFoundException e) {
        Toast.makeText(getActivity(), "Devi installare un'app per aprire PDF, come Adobe Acrobat Reader ", Toast.LENGTH_SHORT).show();
    }
}*/


@Override
public void onClick(View v) {



    switch(v.getId()){

        case R.id.bD1: {

            /**Uri path = Uri.fromFile(new File("regola11_1.pdf"));
            Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
            pdfIntent.setDataAndType(path, "application/pdf");
            pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(pdfIntent);
            Toast.makeText(getActivity(), "Hai cliccato Regola 1 in Reg11 ", Toast.LENGTH_SHORT).show();*/

            File file = new File("\"file:///android_asset/regola11_1.pdf");
            Uri path = Uri.fromFile(file);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(path, "application/pdf");
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

            try {
                context.startActivity(intent);
            }
            catch (ActivityNotFoundException e) {
                Toast.makeText(context, "No application available to view PDF", Toast.LENGTH_LONG).show();
            }
            break;
        }

        case R.id.bD2:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 2 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }


        case R.id.bD3:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 3 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD4:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 4 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD5:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 5 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD6:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 6 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD7:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 7 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD8:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 8 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD9:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 9 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD10:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 10 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD11:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 11 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD12:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 12 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD13:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 13 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD14:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 14 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD15:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 15 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD16:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 16 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }

        case R.id.bD17:

        {
            Toast.makeText(getActivity(), "Hai cliccato Regola 17 in Reg11 ", Toast.LENGTH_SHORT).show();

            break;
        }


    }

}

有人能帮我吗?

编辑2

我已经找到解决方案,请查看下面的答案代码。

感谢大家的回答。

10个回答

12

好的,问题解决了!

这是我用来打开存储在外部存储器上的PDF文件的代码:

File pdfFile = new File(Environment.getExternalStorageDirectory(),"namePdfFile.pdf");//File path
            if (pdfFile.exists()) //Checking if the file exists or not
            {
                Uri path = Uri.fromFile(pdfFile);
                Intent objIntent = new Intent(Intent.ACTION_VIEW);
                objIntent.setDataAndType(path, "application/pdf");
                objIntent.setFlags(Intent. FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(objIntent);//Starting the pdf viewer
            } else {

                Toast.makeText(getActivity(), "The file not exists! ", Toast.LENGTH_SHORT).show();

            }

3
出现android.os.FileUriExposedException: file:///storage/emulated/0/Download/privacy_notice.pdf exposed beyond app through Intent.getData()错误。 - Sujay
嘿,@Sujay,你必须给处理你的意图的活动授予权限。 - Ayokunle Paul
如果没有应用程序可以打开PDF,它将调用“android.content.ActivityNotFoundException:No Activity found to handle Intent”。 - CoolMind

4

第三方应用程序无法通过 file:///android_asset URL 访问您的资源。

您可以:

  • 尝试使用 我的StreamProvider,在您的应用程序中拥有一个可以直接从资源中提供PDF的ContentProvider,或者

  • 在从资源中复制文件到内部存储后,使用来自 Android Support 包的 FileProvider,如此示例应用程序所演示的那样,或者

  • 将文件复制到外部存储并使用通过Uri.fromFile()创建的Uri指向该复制文件


抱歉,我是一个初学者,不知道如何使用https://github.com/commonsguy/cwac-provider。您能否给我提供逐步说明? - shannontesla
@AndreyVedishchev:如果你是初学者,我建议你选择我的回答中的第三个要点。 - CommonsWare
好的,但是怎么做呢? - shannontesla
第二个要点是将这个方法应用到意图上:https://dev59.com/FmAf5IYBdhLWcg3wmztL#33754937 - limlim
非常感谢,第二个选项非常好用。FileProvider 允许其他应用程序在获得权限的情况下打开应用程序的内部文件。 - Hiren Dabhi
@CommonsWare Intent.ACTION_VIEW 工作得很好。但是,使用 Intent.ACTION_SEND 共享存储在应用程序内部存储中的文件时,不能将文件附加到建议的应用程序中。任何帮助将不胜感激。 Intent i = new Intent(); i.setAction(Intent.ACTION_SEND); i.putExtra(Intent.EXTRA_SUBJECT, "主题"); i.putExtra(Intent.EXTRA_TEXT, "文本"); i.putExtra(Intent.EXTRA_STREAM, uri); i.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - Hiren Dabhi

3
对于较新的API,您可以在WebView中打开PDF,请参见在Webview中加载PDF文件。我测试过,在API 21设备上它提供了几个应用程序来打开,在API 27中它在WebView内打开。
对于通常在外部阅读器中打开,请复制此代码。我使用了:intent.resolveActivity != null但启动意图会抛出ActivityNotFound异常https://dev59.com/H2Ml5IYBdhLWcg3w966L#57141679。您应该首先定义FileProvider。
// Try to open PDF and return false if it is not possible.
fun openPdf(file: File, context: Context): Boolean {
    val uri = getUriFromFile(file, context)
    if (uri == null) {
        return false
    } else {
        val intent = Intent(Intent.ACTION_VIEW).apply {
            setDataAndType(uri, "application/pdf")
            flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        }
        // Validate that the device can open your File.
        val activityInfo = intent.resolveActivityInfo(context.packageManager, intent.flags)
        return if (activityInfo?.exported == true) {
            context.startActivity(Intent.createChooser(intent, "Open PDF")))
            true
        } else {
            false
        }
    }
}

// Get URI from file.
fun getUriFromFile(file: File, context: Context): Uri? =
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        Uri.fromFile(file)
    } else {
        try {
            FileProvider.getUriForFile(context, context.packageName + ".provider", file)
        } catch (e: Exception) {
            if (e.message?.contains("ProviderInfo.loadXmlMetaData") == true) {
                throw Error("FileProvider doesn't exist or has no permissions")
            } else {
                throw e
            }
        }
    }

在API 29仿真器(没有PDF应用程序)中,我发现它在内置库中打开PDF。

1
将此权限添加后再重新检查:

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

看起来,访问文件时出现了问题。

尝试打开另一个PDF文件,并告诉我们日志信息。

我认为你不能使用这个:file:///android_asset

同时,看一下这个:

选择带有ExternalStorageDirectory的文件:

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");

来自: https://dev59.com/hGQm5IYBdhLWcg3wowSw#17453242

使用资产文件夹:

如果您正在使用 Assets 文件夹,请使用以下代码!

File file = new File("file:///android_asset/example.pdf");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);

然后添加上述权限并使用Assets代码再次检查。同时,请查看此链接:https://stackoverflow.com/q/28032029/4945820


我已经用你的代码替换了我的代码,并添加了上述权限。不幸的是,当我将“+文件名”替换为PDF文件的名称时,例如“regola11_2.pdf”,它会变成红色,并出现“无法解析符号'regola11_2.pdf'”的错误。你能告诉我我做错了什么吗? - shannontesla
编辑并更新。您应该使用代码的第二部分(带有资产)。 - developerdroid
这个地址存在问题:"file:///android_asset/regola11_1.pdf" & app > build > intermediates > assets,使用以下回复创建Assets文件夹:https://dev59.com/tF8d5IYBdhLWcg3wiip9#26706866 - developerdroid
我简直不敢相信,它一直不能正常工作。现在我的PDF文件在“AppName>app>src>main>assets”中。当我尝试在手机上的应用程序中打开它们时,我收到了相同的错误消息。我不明白为什么会这样... - shannontesla
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) 解决了我的问题。 - supro_96

1

对我来说,将数据设置为"file:///" + filePath很有效。

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(filePath));
intent.setDataAndType(Uri.parse("file:///" + filePath), "application/pdf");
context.startActivity(intent);

0

请求运行时权限以进行写入存储,否则文件本身将无法存储到外部存储器 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


0
有同样的问题。这听起来很傻,甚至可能非常愚蠢,但是让我告诉你, 我尝试了所有方法,从正确的权限FileProviderContentProvider自定义类,但都没有用(花费了近7个小时)。猜猜什么有效,卸载应用程序并重新安装后运行,就像魔法一样,不需要任何额外的东西。如果什么都不起作用,试试这个方法。
如果您正在使用Android 9+,则至少需要在较低版本中进行测试一次。

0

我曾经遇到过同样的问题,花了几个小时来解决它,最终发现我忘记在文件路径和文件名之间添加“/”。

因此,文件找不到,我收到了“无法访问此文件,请检查位置或网络并重试。”的消息。

希望这能帮助到某些人;)


0

在添加了这个权限之后,我的问题得到了解决 :)

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

0
错误是因为每次 PDF 开始下载时都会重新开始,但由于在有限的时间内无法完成下载,因此会显示无效格式错误,并且文件 ID 仍处于下载状态。使用以下代码...
Searchedtext = text.getText().toString();
File file = new File(Environment.getExternalStorageDirectory() + "/pdf", Searchedtext + ".pdf");

if (file.exists()) {
    showPdf();
}
else {
    download();
    showPdf();
}

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