Android #04 下载文件并查看

2019-05-28  本文已影响0人  EmilyCH
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、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日

上一篇 下一篇

猜你喜欢

热点阅读