android软件内在线更新已经算是android开发中必不可少的环节了,我也是因为项目需求,花了半天的时候调试好了前后端,期间参考了诸多博客,由于没有做记录,这里就不引用了。不多bb,直接上代码:
下面是工具类:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* 文件下载工具类(单例模式)
*/
public class DownloadUtil {
private static DownloadUtil downloadUtil;
private final OkHttpClient okHttpClient;
public static DownloadUtil get() {
if (downloadUtil == null) {
downloadUtil = new DownloadUtil();
}
return downloadUtil;
}
public DownloadUtil() {
okHttpClient = getUnsafeOkHttpClient();
}
private OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
OkHttpClient okHttpClient = builder.build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param url 下载连接
* @param destFileDir 下载的文件储存目录
* @param destFileName 下载文件名称,后面记得拼接后缀,否则手机没法识别文件类型
* @param listener 下载监听
*/
public void download(final String url, final String destFileDir, final String destFileName, final OnDownloadListener listener) {
Request request = new Request.Builder()
.url(url)
.build();
//异步请求
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 下载失败监听回调
listener.onDownloadFailed(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
//储存下载文件的目录
File dir = new File(destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, destFileName);
try {
is = response.body().byteStream();
long total = response.body().contentLength();
fos = new FileOutputStream(file);
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
//下载中更新进度条
listener.onDownloading(progress);
}
fos.flush();
//下载完成
listener.onDownloadSuccess(file);
} catch (Exception e) {
listener.onDownloadFailed(e);
}finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
}
}
}
});
}
public interface OnDownloadListener{
/**
* 下载成功之后的文件
*/
void onDownloadSuccess(File file);
/**
* 下载进度
*/
void onDownloading(int progress);
/**
* 下载异常信息
*/
void onDownloadFailed(Exception e);
}
}
下面是应用:
private static ProgressDialog progressDialog = null;
private final static int MAXVALUE = 100;
private String apkFilePath=null;
/**
* 文件下载
*/
private void downFile(String url) {
DownloadUtil.get().download(url, apkFilePath, "kylvd.apk",
new DownloadUtil.OnDownloadListener() {
@Override
public void onDownloadSuccess(File file) {
Log.e("tag","down success");
sendMsg(2,0);
sendMsg(3,0);
}
@Override
public void onDownloading(int progress) {
sendMsg(1,progress);
}
@Override
public void onDownloadFailed(Exception e) {
sendMsg(2,0);
Log.e("tag","down Failed");
}
});
}
//安装apk,这里需要注意,8.0之后不可以把url直接暴露,需要先建立一个共享文件,再安装
private void installAPK() {
Intent intent = new Intent(Intent.ACTION_VIEW);
File apkFile = new File(apkFilePath+"/xxxx.apk");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(MainActivity.this, "com.example.administrator.provider", apkFile);//com.example.administrator.provider即是共享文件区
intent.setDataAndType(uri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
}
startActivity(intent);
};
//软件更新对话框
private void alertSofetUpdata(){
final TextView tv=new TextView(this);
tv.setText("\n 检测到有新版本软件,点击确定更新\n\n");
tv.setTextSize(alert_text_size);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("警告")
.setIcon(R.mipmap.warning)
.setView(tv)
.setPositiveButton(" 确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (progressDialog ==null){
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setMax(MAXVALUE);
progressDialog.setTitle("软件下载中");
progressDialog.setMessage("软件下载中,请稍后...");
//这里设置为不可以通过按取消按钮关闭进度条
progressDialog.setCancelable(false);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
//这里设置的是是否显示进度,设为false才是显示的哦!
progressDialog.setIndeterminate(false);
progressDialog.show();
}
downFile("http://xxxx");
}
})
.setNegativeButton("取消 ",null);
AlertDialog dialog = builder.create();
dialog.show();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextSize(24);
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextSize(24);
}
/**
* 发送Handler
*/
public void sendMsg(int what, Object object) {
Message msg = new Message();
msg.what = what;
msg.obj = object;
mHandler.sendMessage(msg);
}
/**
* Handler
*/
private Handler mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
progressDialog.setProgress((int)msg.obj);
break;
case 2:
progressDialog.dismiss();
progressDialog=null;
break;
case 3:
installAPK();
break;
}
}
};
在Manifest中添加共享文件,注意,android:authorities=“com.example.administrator.provider"需要修改为自己的包名,android:resource=”@xml/filepaths"为文件路径
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.administrator.provider"
android:exported="false"
android:grantUriPermissions="true"
tools:ignore="WrongManifestParent">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
下面建立在res下建立xml/filepaths文件,path修改为自己的包名
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="files_root" path="Android/data/com.example.administrator.demo/"/>
</paths>
以上是android端的代码,后端实现起来比较简单了,建立一个文件下载服务器,建立一个http服务器,android请求服务,对比服务器版本和当前版本,不一致则下载更新。