您的位置:首页 > 移动开发 > Android开发

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

2016-05-17 16:43 1041 查看
公司项目马上要进入内测阶段了,检查的时候发现还差一个更新的功能没有做,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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: