使用Retrofit+Rxjava实现版本更新的功能
效果图
实现思路:
(一)从服务器上下载数据,得到版本号,与手机中app的版本号进行对比,如果服务器版本号大于当前应用的版本号,则提示,下载更新(因为无法对外提供服务器数据,因此 本文只提供下载apk的方法,判断是否更新的方法,根据服务器传递的数据,自行加载判断,获取当前手机的版本号方法,会提供。)
(二)下载apk,并且监听下载的进度,通过自定义的AlertDialog实现进度的展示
(三)下载完成,进行自定安装
具体实现:
I、得到当前手机应用的版本号
我们可以在AndroidManifest.xml中设置程序的版本号等,如Android:versionName="1.0",那如果想在代码中获取这个版本号呢,可以用如下方法(这些修改版本号时只需要修改AndroidManifest.xml中的设置,不用修改代码了):
- /**
- * 返回当前程序版本名
- */
- public static String getAppVersionName(Context context) {
- String versionName = "";
- try {
- // ---get the package info---
- PackageManager pm = context.getPackageManager();
- PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
- versionName = pi.versionName;
- versioncode = pi.versionCode;
- if (versionName == null || versionName.length() <= 0) {
- return "";
- }
- } catch (Exception e) {
- Log.e("VersionInfo", "Exception", e);
- }
- return versionName;
- }
- 获取当前应用的版本号:
- private String getVersionName() throws Exception {
// 获取packagemanager的实例
PackageManager packageManager = getPackageManager();
// getPackageName()是你当前类的包名,0代表是获取版本信息 、
PackageInfo packInfo = packageManager.getPackageInfo(getPackageName(),0);
String version = packInfo.versionName;
return version; }
II、从服务器得到版本号getServerVersion()。。。(由于保密原因该方法无法提供,比较简单,自己可以根据自己项目的接口,下载得到,在此不再赘述)
III、自定义Dialog,用于显示下载的进度条。
Android SDK已经提供有进度条组件ProgressDialog组件,但用的时候我们会发现可能风格与我们应用的整体风格不太搭配,而且ProgressDialog的可定制性也不太强,这时就需要我们自定义实现一个ProgressDialog。
通过看源码我们发现,ProgressDialog继承自Alertdialog,有一个ProgressBar和两个TextView组成的,通过对ProgressDialog的源码进行改进就可以实现一个自定义的ProgressDialog。
1、效果:
首先看一下自定义CommonProgressDialog和原生ProgressDialog的对比:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="wrap_content" android:layout_height="460px" android:gravity="center" android:layout_gravity="center" android:orientation="vertical" android:background="@mipmap/bgdialog"> <TextView android:id="@+id/progress_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="44px" android:layout_marginTop="113px" android:layout_gravity="center_horizontal" android:textColor="#ffffff" /> <ProgressBar android:id="@+id/progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="712px" android:layout_height="30px" android:layout_marginTop="100px" android:layout_gravity="center_horizontal" android:layout_centerHorizontal="true" android:progressDrawable="@drawable/bb" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/progress_percent" android:layout_width="80px" android:layout_height="wrap_content" android:textSize="30px" android:layout_marginLeft="280px" android:gravity="center_horizontal" android:textColor="#ffffff" /> <TextView android:id="@+id/progress_number" android:layout_width="250px" android:layout_height="wrap_content" android:layout_marginLeft="120px" android:textSize="30px" android:gravity="center_horizontal" android:textColor="#ffffff" /> </LinearLayout> <Button android:id="@+id/btn_cancle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="取消下载 " /> </LinearLayout> </FrameLayout>
2 、bb.xml Progressbar进度条图片和背景图片设置(主要设置了进度条的形状(圆角),以及进度显色的颜色)
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <shape> <corners android:radius="5dip" /> <gradient android:endColor="#ddd" android:startColor="#ddd" /> </shape> </item> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="5dip" /> <gradient android:endColor="#ddd" android:startColor="#80ff0000" /> </shape> </clip> </item> </layer-list>3、CustomerDialog.java类:
package org.jokar.download_test.utils; import android.app.AlertDialog; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.annotation.RequiresApi; import android.text.Spannable; import android.text.SpannableString; import android.text.style.StyleSpan; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import org.jokar.download_test.R; import java.text.NumberFormat; /** * Created by Administrator on 2016/12/8. */ public class CustomerDialog extends AlertDialog { private ProgressBar mProgress; private TextView mProgressNumber; private TextView mProgressPercent; private TextView mProgressMessage; private Handler mViewUpdateHandler; private int mMax; private CharSequence mMessage; private boolean mHasStarted; private int mProgressVal; private Button btn_cancle; private String TAG="CommonProgressDialog"; private String mProgressNumberFormat; private NumberFormat mProgressPercentFormat; public void setBonClickListenre(BonClickListenre bonClickListenre) { this.bonClickListenre = bonClickListenre; } private BonClickListenre bonClickListenre; @RequiresApi(api = Build.VERSION_CODES.N) public CustomerDialog(Context context) { super(context); // TODO Auto-generated constructor stub initFormats(); } @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.aa); mProgress=(ProgressBar) findViewById(R.id.progress); mProgressNumber=(TextView) findViewById(R.id.progress_number); mProgressPercent=(TextView) findViewById(R.id.progress_percent); mProgressMessage=(TextView) findViewById(R.id.progress_message); btn_cancle= (Button) findViewById(R.id.btn_cancle); btn_cancle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { bonClickListenre.BBonClick(view); } }); // LayoutInflater inflater = LayoutInflater.from(getContext()); mViewUpdateHandler = new Handler() { @RequiresApi(api = Build.VERSION_CODES.N) @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); int progress = mProgress.getProgress(); int max = mProgress.getMax(); double dProgress = (double)progress/(double)(1024 * 1024); double dMax = (double)max/(double)(1024 * 1024); if (mProgressNumberFormat != null) { String format = mProgressNumberFormat; mProgressNumber.setText(String.format(format, dProgress, dMax)); } else { mProgressNumber.setText(""); } if (mProgressPercentFormat != null) { double percent = (double) progress / (double) max; SpannableString tmp = new SpannableString(mProgressPercentFormat.format(percent)); tmp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, tmp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); mProgressPercent.setText(tmp); } else { mProgressPercent.setText(""); } } }; if (mMessage != null) { setMessage(mMessage); } if (mMax > 0) { setMax(mMax); } if (mProgressVal > 0) { setProgress(mProgressVal); } } @RequiresApi(api = Build.VERSION_CODES.N) private void initFormats() { mProgressNumberFormat = "%1.2fM/%2.2fM"; mProgressPercentFormat = NumberFormat.getPercentInstance(); mProgressPercentFormat.setMaximumFractionDigits(0); } private void onProgressChanged() { mViewUpdateHandler.sendEmptyMessage(0); } public void setProgressStyle(int style) { //mProgressStyle = style; } public int getMax() { if (mProgress != null) { return mProgress.getMax(); } return mMax; } public void setMax(int max) { if (mProgress != null) { mProgress.setMax(max); onProgressChanged(); } else { mMax = max; } } public void setIndeterminate(boolean indeterminate) { if (mProgress != null) { mProgress.setIndeterminate(indeterminate); } // else { // mIndeterminate = indeterminate; // } } public void setProgress(int value) { if (mHasStarted) { mProgress.setProgress(value); onProgressChanged(); } else { mProgressVal = value; } } @Override public void setMessage(CharSequence message) { // TODO Auto-generated method stub //super.setMessage(message); if(mProgressMessage!=null){ mProgressMessage.setText(message); } else{ mMessage = message; } } public interface BonClickListenre{ public void BBonClick(View view); } @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); mHasStarted = true; } @Override protected void onStop() { // TODO Auto-generated method stub super.onStop(); mHasStarted = false; } }IV:下载apk,并且监听进度(关键步骤)(1):增加下载进度监听public class DownloadProgressInterceptor implements Interceptor { private DownloadProgressListener listener; public DownloadProgressInterceptor(DownloadProgressListener listener) { this.listener = listener; } @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(new DownloadProgressResponseBody(originalResponse.body(), listener)) .build(); } }ii:/** * 下载进度listener * Created by JokAr on 16/5/11. */ public interface DownloadProgressListener { void update(long bytesRead, long contentLength, boolean done); }iii:/** * ResponseBody for download * Created by JokAr on 16/5/11. */ public class DownloadProgressResponseBody extends ResponseBody { private ResponseBody responseBody; private DownloadProgressListener progressListener; private BufferedSource bufferedSource; public DownloadProgressResponseBody(ResponseBody responseBody, DownloadProgressListener progressListener) { this.responseBody = responseBody; this.progressListener = progressListener; } @Override public MediaType contentType() { return responseBody.contentType(); } @Override public long contentLength() { return responseBody.contentLength(); } @Override public BufferedSource source() { if (bufferedSource == null) { bufferedSource = Okio.buffer(source(responseBody.source())); } return bufferedSource; } private Source source(Source source) { return new ForwardingSource(source) { long totalBytesRead = 0L; @Override public long read(Buffer sink, long byteCount) throws IOException { long bytesRead = super.read(sink, byteCount); // read() returns the number of bytes read, or -1 if this source is exhausted. totalBytesRead += bytesRead != -1 ? bytesRead : 0; if (null != progressListener) { progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1); } return bytesRead; } }; } }(2) 创建下载进度的元素类:/** * Created by JokAr on 16/7/5. */ public class Download implements Parcelable { private int progress; private int currentFileSize; private int totalFileSize; public int getProgress() { return progress; } public void setProgress(int progress) { this.progress = progress; } public int getCurrentFileSize() { return currentFileSize; } public void setCurrentFileSize(int currentFileSize) { this.currentFileSize = currentFileSize; } public int getTotalFileSize() { return totalFileSize; } public void setTotalFileSize(int totalFileSize) { this.totalFileSize = totalFileSize; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.progress); dest.writeLong(this.currentFileSize); dest.writeLong(this.totalFileSize); } public Download() { } protected Download(Parcel in) { this.progress = in.readInt(); this.currentFileSize = (int) in.readLong(); this.totalFileSize = (int) in.readLong(); } public static final Parcelable.Creator<Download> CREATOR = new Parcelable.Creator<Download>() { @Override public Download createFromParcel(Parcel source) { return new Download(source); } @Override public Download[] newArray(int size) { return new Download[size]; } }; }(3)下载文件网络接口:注:这里@Url是传入完整的的下载URL;不用截取/** * Created by JokAr on 16/7/5. */ public interface DownloadService { @Streaming @GET Observable<ResponseBody> download(@Url String url); }/** * Created by JokAr on 16/7/5. */ public class DownloadAPI { private static final String TAG = "DownloadAPI"; private static final int DEFAULT_TIMEOUT = 15; public Retrofit retrofit; public DownloadAPI(String url, DownloadProgressListener listener) { DownloadProgressInterceptor interceptor = new DownloadProgressInterceptor(listener); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(interceptor) .retryOnConnectionFailure(true) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .build(); retrofit = new Retrofit.Builder() .baseUrl(url) .client(client) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); } public void downloadAPK(@NonNull String url, final File file, Subscriber subscriber) { Log.d(TAG, "downloadAPK: " + url); retrofit.create(DownloadService.class) .download(url) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .map(new Func1<ResponseBody, InputStream>() { @Override public InputStream call(ResponseBody responseBody) { return responseBody.byteStream(); } }) .observeOn(Schedulers.computation()) .doOnNext(new Action1<InputStream>() { @Override public void call(InputStream inputStream) { try { FileUtils.writeFile(inputStream, file); } catch (IOException e) { e.printStackTrace(); } } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } }(5)然后就是调用了: 该网络是在service里完成的/** * Created by JokAr on 16/7/5. */ public class DownloadService extends IntentService { private static final String TAG = "DownloadService"; private NotificationCompat.Builder notificationBuilder; private NotificationManager notificationManager; int downloadCount = 0; // private String apkUrl = "http://download.fir.im/v2/app/install/5818acbcca87a836f50014af?download_token=a01301d7f6f8f4957643c3fcfe5ba6ff"; private String apkUrl="http://123.57.221.150/guquanguanjia_v1.2.apk"; public DownloadService() { super("DownloadService"); } private File outputFile; @Override protected void onHandleIntent(Intent intent) { notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationBuilder = new NotificationCompat.Builder(this) .setContentTitle("Download") .setContentText("Downloading File") .setAutoCancel(true); notificationManager.notify(0, notificationBuilder.build()); download(); } private void download() { DownloadProgressListener listener = new DownloadProgressListener() { @Override public void update(long bytesRead, long contentLength, boolean done) { //不频繁发送通知,防止通知栏下拉卡顿 int progress = (int) ((bytesRead * 100) / contentLength); if ((downloadCount == 0) || progress > downloadCount) { Download download = new Download(); download.setTotalFileSize((int) contentLength); download.setCurrentFileSize((int) bytesRead); download.setProgress(progress); sendNotification(download); } } }; outputFile = new File(Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS), "file.apk"); if (outputFile.exists()) { outputFile.delete(); } String baseUrl = StringUtils.getHostName(apkUrl); new DownloadAPI(baseUrl, listener).downloadAPK(apkUrl, outputFile, new Subscriber() { @Override public void onCompleted() { downloadCompleted(); } @Override public void onError(Throwable e) { e.printStackTrace(); downloadCompleted(); Log.e(TAG, "onError: " + e.getMessage()); } @Override public void onNext(Object o) { } }); } private void downloadCompleted() { Download download = new Download(); download.setProgress(100); sendIntent(download); notificationManager.cancel(0); notificationBuilder.setProgress(0, 0, false); notificationBuilder.setContentText("File Downloaded"); notificationManager.notify(0, notificationBuilder.build()); //安装apk Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive"); startActivity(intent); } private void sendNotification(Download download) { sendIntent(download); notificationBuilder.setProgress(100, download.getProgress(), false); notificationBuilder.setContentText( StringUtils.getDataSize(download.getCurrentFileSize()) + "/" + StringUtils.getDataSize(download.getTotalFileSize())); notificationManager.notify(0, notificationBuilder.build()); } private void sendIntent(Download download) { Intent intent = new Intent(MainActivity.MESSAGE_PROGRESS); intent.putExtra("download", download); LocalBroadcastManager.getInstance(DownloadService.this).sendBroadcast(intent); } @Override public void onTaskRemoved(Intent rootIntent) { notificationManager.cancel(0); } }(6)MainActivity.Class类public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final String MESSAGE_PROGRESS = "message_progress"; private LocalBroadcastManager bManager; private CustomerDialog mDialog; Intent intent; private AppCompatButton btn_download; private ProgressBar progress; private TextView progress_text; private Boolean flag=false; private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @RequiresApi(api = Build.VERSION_CODES.N) @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(MESSAGE_PROGRESS)) { Download download = intent.getParcelableExtra("download"); if (download.getProgress() == 100) { if (mDialog.isShowing()) { mDialog.setMessage("下载完成"); mDialog.dismiss(); } flag=false; } else { if (mDialog.isShowing()){ int a=StringUtils.getDataSize((int) download.getCurrentFileSize()); int b=StringUtils.getDataSize((int) download.getTotalFileSize()); mDialog.setMax(b); mDialog.setProgress(a); } } } } }; @RequiresApi(api = Build.VERSION_CODES.N) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_download = (AppCompatButton) findViewById(R.id.btn_download); progress = (ProgressBar) findViewById(R.id.progress); progress_text = (TextView) findViewById(R.id.progress_text); mDialog = new CustomerDialog(MainActivity.this); registerReceiver(); btn_download.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { intent = new Intent(MainActivity.this, DownloadService.class); startService(intent); flag=true; mDialog.setMessage("正在下载"); mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mDialog.setCanceledOnTouchOutside(false); mDialog.show(); } }); mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { if (flag){ Toast.makeText(MainActivity.this,"下载任务后台运行中",Toast.LENGTH_LONG).show(); } } }); } private void registerReceiver() { bManager = LocalBroadcastManager.getInstance(this); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(MESSAGE_PROGRESS); bManager.registerReceiver(broadcastReceiver, intentFilter); } @Override protected void onDestroy() { super.onDestroy(); //解除注册时,使用注册时的manager解绑 bManager.unregisterReceiver(broadcastReceiver); } /* @RequiresApi(api = Build.VERSION_CODES.N) public void ShowDialog() { }*/ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode==KeyEvent.KEYCODE_BACK){ } super.onKeyDown(keyCode,event); return true; } }源码下载链接。http://download.csdn.net/detail/zlb_lover/9706447