<p>在Android App都会有版本更新的功能,以前我们公司是用友盟SDK更新功能,自己服务器没有这样的功能。版本检测、Apk下载都是使用友盟。最近看到友盟的版本更新SDK文档:十月份更新功能将会停止服务器,建议开发者迁移到自己的服务器中。</p><p>本文章的主要逻辑:</p><p>第一次下载成功,弹出安装界面;</p><p>如果用户没有点击安装,而是按了返回键,在某个时候,又再次使用了我们的APP</p><p>如果下载成功,则判断本地的apk的包名是否和当前程序是相同的,并且本地apk的版本号大于当前程序的版本,如果都满足则直接启动安装程序。</p><p>下载功能,Google官方推荐使用 DownloadManager 服务。</p><p>1. 如何使用DownloadManager</p><pre class="brush:java;toolbar:false">FileDownloadManager.java publiclongstartDownload(Stringuri,Stringtitle,Stringdescription){ DownloadManager.Requestreq=newDownloadManager.Request(Uri.parse(uri)); req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); //req.setAllowedOverRoaming(false); req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //设置文件的保存的位置[三种方式] //第一种 //file:///storage/emulated/0/Android/data/your-package/files/Download/update.apk req.setDestinationInExternalFilesDir(context,Environment.DIRECTORY_DOWNLOADS,&quot;update.apk&quot;); //第二种 //file:///storage/emulated/0/Download/update.apk //req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,&quot;update.apk&quot;); //第三种自定义文件路径 //req.setDestinationUri() //设置一些基本显示信息 req.setTitle(title); req.setDescription(description); req.setMimeType(&quot;application/vnd.android.package-archive&quot;); //加入下载队列 returndm.enqueue(req); //longdownloadId=dm.enqueue(req); //Log.d(&quot;DownloadManager&quot;,downloadId+&quot;&quot;); //dm.openDownloadedFile() }</pre><p>Android自带的DownloadManager模块来下载, 我们通过通知栏知道, 该模块属于系统自带, 它已经帮我们处理了下载失败、重新下载等功能。</p><p>2. 如何检测下载完成,然后启动安装界面</p><p>DownloadManager下载完成后会发出一个广播 android.intent.action.DOWNLOAD_COMPLETE 新建一个广播接收者即可:</p><pre class="brush:java;toolbar:false">publicclassApkInstallReceiverextendsBroadcastReceiver{ @Override publicvoidonReceive(Contextcontext,Intentintent){ if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){ //@TODOSOMETHING } } }</pre><p>3. 完善App更新逻辑</p><p>1&gt; 第一次下载成功,弹出安装界面</p><p>这个逻辑在ApkInstallReceiver里做即可:</p><pre class="brush:java;toolbar:false">publicclassApkInstallReceiverextendsBroadcastReceiver{ @Override publicvoidonReceive(Contextcontext,Intentintent){ if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){ installApk(context,downloadApkId); } } privatestaticvoidinstallApk(Contextcontext,longdownloadApkId){ DownloadManagerdManager=(DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE); Intentinstall=newIntent(Intent.ACTION_VIEW); UridownloadFileUri=dManager.getUriForDownloadedFile(downloadApkId); if(downloadFileUri!=null){ Log.d(&quot;DownloadManager&quot;,downloadFileUri.toString()); install.setDataAndType(downloadFileUri,&quot;application/vnd.android.package-archive&quot;); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); }else{ Log.e(&quot;DownloadManager&quot;,&quot;downloaderror&quot;); } } }</pre><p>2&gt; 如果用户没有点击安装,而是按了返回键</p><p>这个时候用户可能没有安装,而是退出了安装界面,用户退出了我们的APP,在某个时候,又再次使用了我们的APP,这个时候我们不应该去下载新版本,而是使用已经下载已经存在本地的APK。</p><p>第一次下载的 downloadManager.enqueue(req)会返回一个downloadId,把downloadId保存到本地,用户下次进来的时候,取出保存的downloadId,然后通过downloadId来获取下载的状态信息。</p><pre class="brush:java;toolbar:false">ApkUpdateUtils.java publicstaticvoiddownload(Contextcontext,Stringurl,Stringtitle){ longdownloadId=SpUtils.getInstance(context).getLong(KEY_DOWNLOAD_ID,-1L); if(downloadId!=-1L){ FileDownloadManagerfdm=FileDownloadManager.getInstance(context); intstatus=fdm.getDownloadStatus(downloadId); if(status==DownloadManager.STATUS_SUCCESSFUL){ //启动更新界面 Uriuri=fdm.getDownloadUri(downloadId); if(uri!=null){ if(compare(getApkInfo(context,uri.getPath()),context)){ startInstall(context,uri); return; }else{ fdm.getDm().remove(downloadId); } } start(context,url,title); }elseif(status==DownloadManager.STATUS_FAILED){ start(context,url,title); }else{ if(Config.DEV_MODE){ Log.d(TAG,&quot;apkisalreadydownloading&quot;); } } }else{ start(context,url,title); } }</pre><p>获取APK包名、版本号</p><pre class="brush:java;toolbar:false">/** *获取apk程序信息[packageName,versionName...] * *@paramcontextContext *@parampathapkpath */ privatestaticPackageInfogetApkInfo(Contextcontext,Stringpath){ if(Config.DEV_MODE){ Log.d(TAG,&quot;apkdownloadpath:&quot;+path); } PackageManagerpm=context.getPackageManager(); PackageInfoinfo=pm.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES); if(info!=null){ //StringpackageName=info.packageName; //Stringversion=info.versionName; //Log.d(TAG,&quot;packageName:&quot;+packageName+&quot;;version:&quot;+version); //StringappName=pm.getApplicationLabel(appInfo).toString(); //Drawableicon=pm.getApplicationIcon(appInfo);//得到图标信息 returninfo; } returnnull; }</pre><p>获取下载完成的APK地址</p><pre class="brush:java;toolbar:false">/** *获取保存文件的地址 * *@paramdownloadIdanIDforthedownload,uniqueacrossthesystem. *ThisIDisusedtomakefuturecallsrelatedtothisdownload. *@seeFileDownloadManager#getDownloadPath(long) */ publicUrigetDownloadUri(longdownloadId){ returndm.getUriForDownloadedFile(downloadId); } publicDownloadManagergetDm(){ returndm; }</pre><p>获取下载状态信息</p><pre class="brush:java;toolbar:false">publicintgetDownloadStatus(longdownloadId){ DownloadManager.Queryquery=newDownloadManager.Query().setFilterById(downloadId); Cursorc=dm.query(query); if(c!=null){ try{ if(c.moveToFirst()){ returnc.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); } }finally{ c.close(); } } return-1; }</pre><p>如果下载失败,则重新下载并且把downloadId存起来。</p><pre class="brush:java;toolbar:false">privatestaticvoidstart(Contextcontext,Stringurl,Stringtitle){ longid=FileDownloadManager.getInstance(context).startDownload(url, title,&quot;下载完成后点击打开&quot;); SpUtils.getInstance(context).putLong(KEY_DOWNLOAD_ID,id); if(Config.DEV_MODE){ Log.d(TAG,&quot;apkstartdownload&quot;+id); } }</pre><p>如果下载成功,则判断本地的apk的包名是否和当前程序是相同的,并且本地apk的版本号大于当前程序的版本,如果都满足则直接启动安装程序。</p><pre class="brush:java;toolbar:false">/** *下载的apk和当前程序版本比较 * *@paramapkInfoapkfile&#39;spackageInfo *@paramcontextContext *@return如果当前应用版本小于apk的版本则返回true */ privatestaticbooleancompare(PackageInfoapkInfo,Contextcontext){ if(apkInfo==null){ returnfalse; } StringlocalPackage=context.getPackageName(); if(apkInfo.packageName.equals(localPackage)){ try{ PackageInfopackageInfo=context.getPackageManager().getPackageInfo(localPackage,0); if(apkInfo.versionCode&gt;packageInfo.versionCode){ returntrue; }else{ if(Config.DEV_MODE){ Log.d(TAG,&quot;apk&#39;sversionCode&lt;=app&#39;sversionCode&quot;); } } }catch(PackageManager.NameNotFoundExceptione){ e.printStackTrace(); } } if(Config.DEV_MODE){ Log.d(TAG,&quot;apk&#39;spackagenotmatchapp&#39;spackage&quot;); } returnfalse; }</pre><p>启动安装界面</p><pre class="brush:java;toolbar:false">publicstaticvoidstartInstall(Contextcontext,Uriuri){ Intentinstall=newIntent(Intent.ACTION_VIEW); install.setDataAndType(uri,&quot;application/vnd.android.package-archive&quot;); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); }</pre><p>为了严谨起见,在ApkInstallReceiver里不仅要对downloadId判断,还应当把当前程序和本地apk包名和版本号对比。</p><p>如果用户禁用了下载服务[下载管理程序]</p><p>可以通过如下代码进入 启用/禁用 下载管理程序 界面:</p><pre class="brush:java;toolbar:false">StringpackageName=&quot;com.android.providers.downloads&quot;; Intentintent=newIntent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse(&quot;package:&quot;+packageName)); startActivity(intent);</pre><p>我们先停止下载管理程序,然后点击demo里的Download 按钮报出如下错误:</p><pre class="brush:java;toolbar:false">Causedby:java.lang.IllegalArgumentException:UnknownURLcontent://downloads/my_downloads atandroid.content.ContentResolver.insert(ContentResolver.java:1227) atandroid.app.DownloadManager.enqueue(DownloadManager.java:946) atcom.chiclam.download.FileDownloadManager.startDownload(FileDownloadManager.java:61) atcom.chiclam.download.ApkUpdateUtils.start(ApkUpdateUtils.java:47) atcom.chiclam.download.ApkUpdateUtils.download(ApkUpdateUtils.java:42) atcom.chiclam.download.MainActivity.download(MainActivity.java:34) atjava.lang.reflect.Method.invoke(NativeMethod) atandroid.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) atandroid.view.View.performClick(View.java:5204) atandroid.view.View$PerformClick.run(View.java:21153) atandroid.os.Handler.handleCallback(Handler.java:739) atandroid.os.Handler.dispatchMessage(Handler.java:95) atandroid.os.Looper.loop(Looper.java:148) atandroid.app.ActivityThread.main(ActivityThread.java:5417) atjava.lang.reflect.Method.invoke(NativeMethod) atcom.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)</pre><p>也就是说如果停止了下载管理程序 调用dm.enqueue(req);就会上面的错误,从而程序闪退.</p><p>所以在使用该组件的时候,需要判断该组件是否可用:</p><pre class="brush:xml;toolbar:false">privatebooleancanDownloadState(){ try{ intstate=this.getPackageManager().getApplicationEnabledSetting(&quot;com.android.providers.downloads&quot;); if(state==PackageManager.COMPONENT_ENABLED_STATE_DISABLED ||state==PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER ||state==PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED){ returnfalse; } }catch(Exceptione){ e.printStackTrace(); returnfalse; } returntrue; } ---------------------</pre>
RangeTime:0.008504s
RangeMem:215.6 KB
返回顶部 留言