App实现自动更新

时间:2021-03-22 05:59:35

app上线之后会经常面临版本更新,因此每当有新版本发布时,应用自动更新就显得很有必要!其实整个实现并不复杂,通过后台返回的url,利用Httpurl去下载即可,同时在通知栏显示下载进度.当下载完成时,自动跳转至安装页面.

首先仍然是我代码的目录结构
App实现自动更新


/**事件的监听回调
* Created by Administrator on 2017/8/14.
*/


public interface UpdateDownloadListener {

//下载请求开始回调
public void onStarted();

//进度更新回调
public void onProgressChanged(int progress,String downloadUrl);

//下载完成回调
public void onFinished(int completeSize,String downloadUrl);

//下载失败回调
public void onFailure();

}

/**真正的负责处理文件的下载和线程间的通信
* Created by Administrator on 2017/8/14.
*/


public class UpdateDownloadRequest implements Runnable{

private String downloadUrl;
private String localFilePath;
private UpdateDownloadListener downloadListener;
//下载的标记位
private boolean isDownloading=false;
//文件长度
private long currentLength;

private DownloadResponseHandler downloadHandler;

public UpdateDownloadRequest(String downloadUrl,String localFilePath,UpdateDownloadListener downloadListener) {
this.downloadUrl=downloadUrl;
this.localFilePath=localFilePath;
this.downloadListener=downloadListener;
//构造方法完成则开始下载
this.isDownloading=true;
this.downloadHandler=new DownloadResponseHandler();
}

//真正的去建立连接的方法
private void makeRequest() throws IOException,InterruptedException
{
try {
if (!Thread.currentThread().isInterrupted())
{
URL url = new URL(downloadUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
//默认也是如此,重新设置避免关闭
connection.setRequestProperty("Connection","Keep-Alive");
connection.connect();
currentLength = connection.getContentLength();
if (!Thread.currentThread().isInterrupted())
{
//真正完成文件的下载
downloadHandler.sendResponseMessage(connection.getInputStream());
}
}
}catch (IOException e){
throw e;
}

}

@Override
public void run() {

try{
makeRequest();
}
catch (IOException e){

}catch (InterruptedException e){

}

}

//格式化数据,保留两位小数
private String getTwoPointFloatStr(float value)
{
DecimalFormat format = new DecimalFormat("0.00");
return format.format(value);
}

//包含了下载过程中所有可能出现的异常情况
public enum FailureCode{
UnknowHost,Socket,SocketTimeout,ConnectTimeout,IO,HttpResponse,JSON,Interrupted
}

//用来真正的去下载文件,并发送消息和回调接口
public class DownloadResponseHandler{

protected static final int SUCCESS_MESSAGE=0;
protected static final int FAILURE_MESSAGE=1;
protected static final int START_MESSAGE=2;
protected static final int FINISH_MESSAGE=3;
protected static final int NETWORK_OFF=4;
protected static final int PROGRESS_CHANGED=5;

private int mCompleteSize=0;
private int progress=0;
private int preProgress=0;

//真正的完成线程间的通信
private Handler handler;

public DownloadResponseHandler() {
//依附于主线程
handler=new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
handleSelfMessage(msg);
}
};
}

//用来发送不同的消息对象
protected void sendFinishMessage()
{
sendMessage(obtainMessage(FINISH_MESSAGE,null));
}

private void sendProgressChangedMessage(int progress)
{
sendMessage(obtainMessage(PROGRESS_CHANGED,new Object[]{progress}));
}

protected void sendFailureMessage(FailureCode failureCode)
{
sendMessage(obtainMessage(FAILURE_MESSAGE,new Object[]{failureCode}));
}

protected void sendMessage(Message msg)
{
if (handler!=null)
{
handler.sendMessage(msg);
}
else
{
handleSelfMessage(msg);
}
}

//获取一个消息对象
protected Message obtainMessage(int responseMessage,Object response)
{
Message msg=null;
if (handler!=null)
{
msg=handler.obtainMessage(responseMessage,response);
}
else
{
msg=Message.obtain();
msg.what=responseMessage;
msg.obj=response;
}
return msg;
}

protected void handleSelfMessage(Message msg)
{
Object[] response;
switch (msg.what)
{
case FAILURE_MESSAGE:
response= (Object[]) msg.obj;
handleFailureMessage((FailureCode)response[0]);
break;
case PROGRESS_CHANGED:
response= (Object[]) msg.obj;
handleProgressChangedMessage(((Integer)response[0]).intValue());
break;
case FINISH_MESSAGE:
onFinish();
break;
default:
break;
}
}

//各种消息的处理逻辑
protected void handleProgressChangedMessage(int progress)
{
downloadListener.onProgressChanged(progress,"");
}

protected void handleFailureMessage(FailureCode failureCode)
{
onFailure(failureCode);
}

//外部接口的回调
public void onFinish()
{
downloadListener.onFinished(mCompleteSize,"");
}

public void onFailure(FailureCode failureCode)
{
downloadListener.onFailure();
}

//文件下载方法,会发送各种类型的事件,比如下载开始,下载结束,更新进度条等
void sendResponseMessage(InputStream inputStream)
{
RandomAccessFile randomAccessFile=null;
//记录下载进度
mCompleteSize=0;
try{
byte[] buff = new byte[1024];
int length=-1;
int limit=0;
randomAccessFile=new RandomAccessFile(localFilePath,"rwd");
while ((length=inputStream.read(buff))!=-1)
{
if (isDownloading)
{
randomAccessFile.write(buff,0,length);
mCompleteSize+=length;
if (mCompleteSize<=currentLength)
{
progress= (int) (mCompleteSize*1.0f/currentLength*100);
if (progress>=99||(limit%30==0&&progress<=100))
{
Log.e("======","====progress===="+progress);
/*if (preProgress!=progress)
{*/

sendProgressChangedMessage(progress);
/*}*/
}
limit++;
preProgress=progress;
}
}
}
sendFinishMessage();
}catch (IOException e){
//异常时发送消息
sendFailureMessage(FailureCode.IO);
}
finally {
try {
if (inputStream!=null)
{
inputStream.close();
}
if (randomAccessFile!=null)
{
randomAccessFile.close();
}
}catch (IOException e)
{
sendFailureMessage(FailureCode.IO);
}

}
}
}
}

/**下载调度管理器,调用我们的updateDownloadRequest
* Created by Administrator on 2017/8/14.
*/


public class UpdateManager {

private static UpdateManager manager;
private ThreadPoolExecutor threadPoolExecutor;
private UpdateDownloadRequest request;

public UpdateManager() {
threadPoolExecutor= (ThreadPoolExecutor) Executors.newCachedThreadPool();
}

public static UpdateManager getInstance()
{
if (manager==null)
{
manager=new UpdateManager();
}
return manager;
}

public void startDownloads(String downloadUrl,String localPath,UpdateDownloadListener listener)
{
if (request!=null)
{
return;
}
checkLocalFilePath(localPath);
request=new UpdateDownloadRequest(downloadUrl,localPath,listener);
threadPoolExecutor.submit(request);
}

//检查本地文件路径
private void checkLocalFilePath(String localPath) {
File dir = new File(localPath.substring(0, localPath.lastIndexOf("/") + 1));
if (!dir.exists())
{
dir.mkdirs();
}
File file = new File(localPath);
if (!file.exists())
{
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

/**app更新下载后台服务
* Created by Administrator on 2017/8/14.
*/


public class UpdateService extends Service{

private String apkUrl;
private String filePath;
private NotificationManager notificationManager;
private Notification notification;

@Override
public void onCreate() {
super.onCreate();
notificationManager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
filePath= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+
File.separator+"autoupdate"+File.separator+"auto.apk";

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent==null)
{
notifyUser("下载失败","下载地址错误",0);
stopSelf();
}
else
{
apkUrl=intent.getStringExtra("apkUrl");
notifyUser("开始下载","开始下载",0);
startDownload();
}
return super.onStartCommand(intent, flags, startId);
}

private void startDownload() {
UpdateManager.getInstance().startDownloads(apkUrl, filePath, new UpdateDownloadListener() {

@Override
public void onStarted() {

}

@Override
public void onProgressChanged(int progress, String downloadUrl) {
notifyUser("正在下载","正在下载",progress);
}

@Override
public void onFinished(int completeSize, String downloadUrl) {
notifyUser("下载完成","下载完成",100);
stopSelf();
}

@Override
public void onFailure() {
notifyUser("下载失败","下载失败",0);
stopSelf();
}
});
}

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

//更新我们的notification来告知用户当前下载的进度
private void notifyUser(String result, String reason,int progress) {
NotificationCompat.Builder builder=new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.logol)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.logol))
.setContentTitle("autoupdate");
//Log.e("===progress===","======"+progress);
if (progress>0&&progress<100)
{
builder.setProgress(100,progress,false);
}
else
{
builder.setProgress(0,0,false);
}
builder.setAutoCancel(true);
builder.setWhen(System.currentTimeMillis());
builder.setTicker(result);
if (progress>=100)
{
getContentIntent();
}
else
{
/*builder.setContentIntent(progress>=100?getContentIntent():
PendingIntent.getActivity(this,0,new Intent(),PendingIntent.FLAG_UPDATE_CURRENT));*/

builder.setContentIntent(PendingIntent.getActivity(this,0,new Intent(),PendingIntent.FLAG_UPDATE_CURRENT));
notification=builder.build();
notificationManager.notify(0,notification);
}
}

private PendingIntent getContentIntent() {
//创建一个开启安装App界面的意图
File apkFile = new File(filePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//判断版本是否在7.0以上
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
{
Uri apkUri = FileProvider.getUriForFile(this, "com.example.administrator.myautoupdate" +
".fileprovider", apkFile);
//添加这一句表示对目标应用临时授权该uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri,"application/vnd.android.package-archive");
}
else
{
intent.setDataAndType(Uri.parse("file://"+apkFile.getAbsolutePath()),"application/vnd.android.package-archive");
//intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
}
startActivity(intent);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
}

public class MainActivity extends AppCompatActivity {

Button button_bt;

String url="http://wap.sogou.com/app/redir.jsp?appdown=1&u=0Gd8piB6093-W-QPHXpNcQJvLH6U5CYd9F0_TSI0vGQYAGh2YbdCmC1hNfN-kHhp&docid=2355133373028692437&sourceid=-5321678202930570851&w=1906&stamp=20170814";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button_bt= (Button) findViewById(R.id.button_bt);

button_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
checkVersion();
}
});
checkVersion();
}

private void checkVersion() {
CommonDialog dialog = new CommonDialog(this);
dialog.setListener(new CommonDialog.CommonDialogListener() {

@Override
public void confirm() {
Intent intent = new Intent(MainActivity.this, UpdateService.class);
intent.putExtra("apkUrl",url);
startService(intent);
}
});

dialog.show();
}
}

虽然代码不复杂,但还是得敲过才知道!

程序运行时,界面效果如下
App实现自动更新