Android中使用Notification实现应用更新显示下载进度

公司项目马上要进入内测阶段了,检查的时候发现还差一个更新的功能没有做,IOS那边儿有AppStore,Android可没有,虽然说各大应用市场也会推送吧,但是毕竟还要装个XX应用市场的软件不是,要是没装的话应用不就没法更新了么,考虑到这方面的原因还是决定写一下了。

然后就开始找资料吧,各种找,发现都不尽人意吧,在下资质愚钝,很多东西都是看个一只半解,希望自己写的清晰些,也能帮助一些人吧。

通知栏不会截图,,所以没办法看效果图了,直接上代码吧,最后会放出源码的。


首先是MainActivity类。其实就是个白页面,啥都没有。


package com.demo_download_notification.demo;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        showNoticeDialog("http://wsdownload.hdslb.net/app/BiliPlayer3.apk");
    }


    /**
     * 显示软件更新对话框
     */
    public void showNoticeDialog(final String apkUrl) {
        // 构造对话框

        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        builder.setTitle("软件更新");
        builder.setMessage("有新版本,建议更新!");
        // 更新
        builder.setPositiveButton("更新", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                // 显示下载对话框
                Intent serviceIntent = new Intent(mContext, DownUpdateApk.class);
                serviceIntent.putExtra("url", apkUrl);
                startService(serviceIntent);
            }
        });
        // 稍后更新
        builder.setNegativeButton("稍后更新", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        Dialog noticeDialog = builder.create();
        noticeDialog.show();
    }
}

然后是FileUtil,工具类直接从以前项目中拖过来的。其实也就用了其中一个方法。


package com.demo_download_notification.demo.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class FileUtil {


    /**
     * 将Bitmap 图片保存到本地路径,并返回路径
     *
     * @param c
     * @param mType    资源类型,参照  MultimediaContentType 枚举,根据此类型,保存时可自动归类
     * @param fileName 文件名称
     * @param bitmap   图片
     * @return
     */
    public static String saveFile(Context c, String fileName, Bitmap bitmap) {
        return saveFile(c, "", fileName, bitmap);
    }

    public static String saveFile(Context c, String filePath, String fileName, Bitmap bitmap) {
        byte[] bytes = bitmapToBytes(bitmap);
        return saveFile(c, filePath, fileName, bytes);
    }


    public static boolean saveFile(InputStream inputStream, String path, String fileName, Handler mHandler, long fileLength) throws IOException {
        File newFile = new File(path);
        if (!newFile.exists()) {
            newFile.mkdir();
        }
        newFile = new File(path + "/" + fileName);
        OutputStream outs = new FileOutputStream(newFile);
        int byteWritten = 0;
        int byteCount = 0;
        byte[] bytes = new byte[1024];
        int percentage = 0; //保存百分比信息
        int count = 0;
        int nowCount = 1;
        while ((byteCount = inputStream.read(bytes)) != -1) {
            count += byteCount;
            percentage = (int) (((float) count / fileLength) * 100);

            if (percentage > 0 && percentage == nowCount) { //每完成百分之一 通知Handler一次
                nowCount = percentage + 1;
                Message msg = mHandler.obtainMessage();
                msg.what = 1;
                msg.obj = percentage + "";
                mHandler.sendMessage(msg);
            }
            outs.write(bytes, 0, byteCount);
            byteWritten += byteCount;
        }
        //写入完成,通知Handler 可以进行安装,改变通知栏ui
        Message msg = mHandler.obtainMessage();
        msg.what = 2;
        msg.obj = percentage + "";
        mHandler.sendMessage(msg);
        inputStream.close();
        outs.close();
        if (byteWritten > 0) {
            return true;
        } else {
            return false;
        }
    }

    public static byte[] bitmapToBytes(Bitmap bm) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(CompressFormat.JPEG, 100, baos);
        return baos.toByteArray();
    }

    public static String saveFile(Context c, String filePath, String fileName, byte[] bytes) {
        String fileFullName = "";
        FileOutputStream fos = null;
        String dateFolder = new SimpleDateFormat("yyyyMMdd", Locale.CHINA)
                .format(new Date());
        try {
            String suffix = "";
            File file = new File(getPath(filePath));
            if (!file.exists()) {
                file.mkdirs();
            }
            File fullFile = new File(filePath, fileName + suffix);
            fileFullName = fullFile.getPath();
            fos = new FileOutputStream(new File(filePath, fileName + suffix));
            fos.write(bytes);
        } catch (Exception e) {
            fileFullName = "";
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    fileFullName = "";
                }
            }
        }
        return fileFullName;
    }


    public static String getPath(String filePath) {
        if (filePath == null || filePath.trim().length() == 0) {
            filePath = Environment.getExternalStorageDirectory() + "/JiaXT/";
        } else {
            filePath = "";
        }
        return filePath;
    }

    public static String getPath() {
        return Environment.getExternalStorageDirectory() + "/TsingpuAkp/";
    }


    /**
     * 删除单个文件
     *
     * @param filePath 被删除文件的文件名
     * @return 文件删除成功返回true,否则返回false
     */
    public static boolean deleteFile(String filePath) {
        File file = new File(filePath);
        if (file.isFile() && file.exists()) {
            return file.delete();
        }
        return false;
    }

    /**
     * 删除文件夹以及目录下的文件
     *
     * @param filePath 被删除目录的文件路径
     * @return 目录删除成功返回true,否则返回false
     */
    public static boolean deleteDirectory(String filePath) {
        boolean flag = false;
        //如果filePath不以文件分隔符结尾,自动添加文件分隔符
        if (!filePath.endsWith(File.separator)) {
            filePath = filePath + File.separator;
        }
        File dirFile = new File(filePath);
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        flag = true;
        File[] files = dirFile.listFiles();
        //遍历删除文件夹下的所有文件(包括子目录)
        for (int i = 0; i < files.length; i++) {
            if (files[i].isFile()) {
                //删除子文件
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) break;
            } else {
                //删除子目录
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) break;
            }
        }
        if (!flag) return false;
        //删除当前空目录
        return dirFile.delete();
    }

    /**
     * 根据路径删除指定的目录或文件,无论存在与否
     *
     * @param filePath 要删除的目录或文件
     * @return 删除成功返回 true,否则返回 false。
     */
    public static boolean DeleteFolder(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            return false;
        } else {
            if (file.isFile()) {
                // 为文件时调用删除文件方法
                return deleteFile(filePath);
            } else {
                // 为目录时调用删除目录方法
                return deleteDirectory(filePath);
            }
        }
    }


    /**
     * 保存文件
     *
     * @param bm
     * @param fileName
     * @throws IOException
     */
    public static File getFile(Bitmap bm, String fileName, String filePath) {
        try {
            String path = getPath(filePath);
            File dirFile = new File(path);
            if (!dirFile.exists()) {
                dirFile.mkdir();
            }
            File myCaptureFile = new File(path + fileName);
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
            bm.compress(CompressFormat.JPEG, 80, bos);
            bos.flush();
            bos.close();
            return myCaptureFile;
        } catch (IOException e) {
        }
        return null;
    }


}

最后就是最关键的service服务类了,DownUpdateApk

package com.demo_download_notification.demo;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.annotation.Nullable;
import android.widget.RemoteViews;
import com.demo_download_notification.demo.utils.FileUtil;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by color on 16/5/16.
 */
public class DownUpdateApk extends Service {

    NotificationManager notificationManager;
    Notification myNotify;

    private String url;

    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message message) {
            int progress = Integer.parseInt(message.obj.toString());
            RemoteViews rv;
            switch (message.what) {
                case 1:  //更改通知栏UI布局
                    rv = new RemoteViews(getPackageName(),
                            R.layout.my_notification);
                    rv.setTextViewText(R.id.text_title, "正在下载中");
                    rv.setProgressBar(R.id.progress, 100, progress, false);
                    rv.setTextViewText(R.id.text_content, progress + "%");
                    myNotify.contentView = rv;
                    notificationManager.notify(0, myNotify);
                    break;
                case 2:
                    rv = new RemoteViews(getPackageName(),
                            R.layout.my_notification);
                    rv.setTextViewText(R.id.text_title, "下载完成,点击安装");
                    rv.setProgressBar(R.id.progress, 100, progress, false);
                    rv.setTextViewText(R.id.text_content, progress + "%");
                    myNotify.contentView = rv;


                    //下载完成,点击可以去安装文件
                    Intent intent = new Intent();
                    intent.setAction(Intent.ACTION_VIEW);// android.intent.action.VIEW
                    intent.setDataAndType(Uri.fromFile(new File(FileUtil.getPath() + "/Tsingpu.apk")),
                            "application/vnd.android.package-archive");
                    myNotify.flags = Notification.FLAG_AUTO_CANCEL;
                    myNotify.contentIntent = PendingIntent.getActivity(DownUpdateApk.this, 1, intent, 0);
                    notificationManager.notify(0, myNotify);


                    break;
            }
            return false;
        }
    });

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        myNotify = new Notification();
        myNotify.icon = R.mipmap.ic_launcher;
        myNotify.tickerText = "准备下载...";
        myNotify.when = System.currentTimeMillis();

        myNotify.flags = Notification.FLAG_NO_CLEAR;// 不能够自动清除

        RemoteViews rv = new RemoteViews(getPackageName(),
                R.layout.my_notification);
        rv.setProgressBar(R.id.progress, 100, 0, false);
        rv.setTextViewText(R.id.text_content, "开始下载"); //这里就是使用自定义布局了 初始化的时候不设置Intent,点击的时候就不会有反应了,亏得我还找了好久  T-T

        myNotify.contentView = rv;
        notificationManager.notify(0, myNotify);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        url = null;
        myNotify = null;
        notificationManager = null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        url = intent.getStringExtra("url");
        init();

        return super.onStartCommand(intent, flags, startId);
    }


    public void init() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient(); //使用okhttp下载文件
                Request request = new Request.Builder()
                        .url(url)
                        .build();

                client.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        String s = e.getMessage();
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if (!response.isSuccessful())
                            throw new IOException("Unexpected code " + response);
                        InputStream is = response.body().byteStream(); //成功的回调中拿到字节流
                        String path = FileUtil.getPath();
                        long fileLength = response.body().contentLength(); //获取文件长度
                        FileUtil.saveFile(is, path, "test.apk", mHandler, fileLength); //保存下载的apk文件
                    }

                });


            }
        }).start();
    }
}


自定义Notification的布局文件也贴出来吧


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">


        <TextView
            android:id="@+id/text_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="正在下载中"
            android:textSize="12sp"
            android:visibility="visible" />

        <TextView
            android:id="@+id/text_finish"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_below="@id/text_title"
            android:text="下载完成,点击安装"
            android:textSize="12sp"
            android:visibility="gone" />

        <TextView
            android:id="@+id/text_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="下载进度"
            android:textSize="12sp"
            android:visibility="visible" />


        <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_alignParentBottom="true"
            android:layout_below="@id/text_content"
            android:layout_weight="1"
            android:max="100"
            android:visibility="visible" />

    </RelativeLayout>


</LinearLayout>


就是这么多了,关键的地方我应该都写了注释的。


最后附上下载地址 :https://github.com/color9169/DownloadUpdateOnNotification/tree/master









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值