终于把下载安装更新的功能整出来了,记录关键点
创始人
2024-01-29 09:13:08

我的第一个安卓应用终于也有了APP内安装更新的功能(赶上末班车了吗),记录一些关键点,方方面面的。

托管检测更新和下载服务

由于没有服务器,这两个核心功能可以托管到一些比较好的平台。检测我用的是蒲公英分发(内测阶段),下载用的则是无限蓝云(hhh)。如果蒲公英过审了也可以只用一个,不知道难度大不大……

安装apk

高版本需要fileprovider,其实不用的话直接vmpolicy微调一下也行。

两个关键点都需要在清单文件中处理:1、定义 fileprovider,2:声明权限(否则没有反应)。

manifest:



其中 android:resource="@xml/file_paths"需要提供一个清单文件res/xml/file_paths.xml,可如下:




这样,需要下载安装包到: 外部储存->临时文件夹( /storage/0/Android/data/包名/cache )中的 apks 文件夹: File target = new File(getExternalCacheDir(), "apks/"+versionName); ,才能被 FileProvider 识别。

Java 调用 :

	private void startUpdateInstall(File target) {try {Intent intent = new Intent(Intent.ACTION_VIEW);File file = target;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {Uri apkUri = FileProvider.getUriForFile(PDICMainActivity.this, "包名.fileprovider", file);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.setDataAndType(apkUri, "application/vnd.android.package-archive");} else {intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);Uri uri = Uri.fromFile(file);intent.setDataAndType(uri, "application/vnd.android.package-archive");}startActivity(intent);} catch (Exception e) {CMN.debug(e);}}

FileProvider还可以包含更多文件夹, 参考 Stack Overflow - ‘Failed to find configured root’:

  • --> Context.getFilesDir()
  • --> Context.getCacheDir()
  • --> Environment.getExternalStorageDirectory()
  • --> Context.getExternalFilesDir(String)
  • --> Context.getExternalCacheDir()
  • --> Context.getExternalMediaDirs()

Markdown文本 + 超链接混排,实现优雅界面

每一个版本都可以提炼一些简短的介绍,然后在检测更新的时候一起获取,显示出来。

建议用Markdown格式写更新日志,Markdown 之简洁优雅足以胜任一定的生产力。

有许多开源组件可以展示Markdown,比如io.noties.markwon或者org.commonmark,前者体积较大、更加完善,后者更简单,但只是转换为html,需要再配合Html.fromHtml转换Spannable成才行

而安卓的Textview虽然支持各种图文混排,但有一些bug,比如设置linkedmovement后、再设置文本可选,会导致滚动点击时随机崩溃。

两个办法解决,一是自定义textview,try-catch包绕一些会崩溃的方法如dispatchTouchEvent(不推荐)。二是自定义触摸监听器,在onTouch中自行调用ClickableSpan、UrlSpan、LinkSpan等的的点击方式。

// 自定义触摸监听器,手动调用 ClickableSpan
TextView tv = (TextView) v;
CharSequence text = tv.getText();
if(text instanceof Spannable) {Spannable span = (Spannable) text;int x = 触摸位置_X;int y = 触摸位置_Y;x -= tv.getTotalPaddingLeft();y -= tv.getTotalPaddingTop();x += tv.getScrollX();y += tv.getScrollY();Layout layout = tv.getLayout();if(layout!=null) {int line = layout.getLineForVertical(y);int off = layout.getOffsetForHorizontal(line, x);ClickableSpan[] link = span.getSpans(off, off, ClickableSpan.class);if (link.length > 0) {touching = ……// 记录 link[0], 然后在ACTION_UP或onClick时,调用点击监听器}}
}@Override
public void onClick(View v) {ClickableSpan touching = getTouchingSpan(v);if (touching!=null) {TextView widget = (TextView) v;Spannable span = (Spannable) widget.getText();if (clickInterceptor != null && clickInterceptor.onClick(widget, touching)) {// intentionally blank} else {touching.onClick(v);}Selection.setSelection(span,span.getSpanStart(touching),span.getSpanEnd(touching));}
}

轻松实现进度条

这里进度条参考的是百度第一个博客里的:给progressbar设置drawable和自定义progressbar:

purpose_drawable.xml




细看,里面是两层的layerdrawable,第一层是底色,另一层id/progress则是进度的颜色,不过里面的渐变色似乎没有用到啊。

然后布局代码里给seekbar添加android:progressDrawable:@drawable/purpose_drawable属性即可,非常快啊,简直不讲武德。

大佬说得好,不仅仅要创造 progress,还要创造 purpose,以后就叫做 purposeBar 吧。

Put Together

下载之时,我直接用进度条替换了对话框底部的其中一个按钮。这种替换操作用着很爽,我甚至提炼了一个方法 ViewUtils.replaceView ,一系列操作原生视图的方法 ……

public static View replaceView(View viewToAdd, View viewToRemove) {return replaceView(viewToAdd, viewToRemove, true);
}public static View replaceView(View viewToAdd, View viewToRemove, boolean layoutParams) {ViewGroup.LayoutParams lp = viewToRemove.getLayoutParams();ViewGroup vg = (ViewGroup) viewToRemove.getParent();if(vg!=null) {int idx = vg.indexOfChild(viewToRemove);removeView(viewToAdd);if (layoutParams) {vg.addView(viewToAdd, idx, lp);} else {vg.addView(viewToAdd, idx);}removeView(viewToRemove);}return viewToAdd;
}public static boolean removeView(View viewToRemove) {return removeIfParentBeOrNotBe(viewToRemove, null, false);
}public static boolean removeIfParentBeOrNotBe(View view, ViewGroup parent, boolean tobe) {if(view!=null) {ViewParent svp = view.getParent();if((parent!=svp) ^ tobe) {if(svp!=null) {((ViewGroup)svp).removeView(view);//CMN.Log("removing from...", svp, view.getParent(), view);return view.getParent()==null;}return true;}}return false;
}

效果图:
请添加图片描述

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
平遥古城在哪 平遥古城基本现状 城垣恢宏,雉堞连云市井街巷,店肆林立四合院落,青砖瓦屋这里是平遥古城历经千年沧桑而古朴宛然,风姿依旧...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
平遥古城在哪 平遥古城基本现状 城垣恢宏,雉堞连云市井街巷,店肆林立四合院落,青砖瓦屋这里是平遥古城历经千年沧桑而古朴宛然,风姿依旧...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...