成都网站建设设计

将想法与焦点和您一起共享

android分享,android分享到微信朋友圈

Android技术分享|Android 中部分内存泄漏示例及解决方案

内存泄漏:

成都创新互联公司专注为客户提供全方位的互联网综合服务,包含不限于网站制作、成都做网站、汉台网络推广、微信小程序开发、汉台网络营销、汉台企业策划、汉台品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们最大的嘉奖;成都创新互联公司为所有大学生创业者提供汉台建站搭建服务,24小时服务热线:13518219792,官方网址:www.cdcxhl.com

举例:

请注意以下的例子是虚构的

内存抖动

源自Android文档中的 Memory churn 一词,中文翻译为内存抖动。

指快速频繁的创建对象从而产生的性能问题。

引用Android文档原文:

Java内存泄漏的根本原因是 长生命周期 的对象持有 短生命周期 对象的引用就很可能发生内存泄漏。

尽管短生命周期对象已经不再需要,但因为长生命周期依旧持有它的引用,故不能被回收而导致内存泄漏。

静态集合类引起的内存泄漏

如果仅仅释放引用本身(tO = null), ArrayList 依然在引用该对象,GC无法回收。

监听器

在Java应用中,通常会用到很多监听器,一般通过 addXXXXListener() 实现。但释放对象时通常会忘记删除监听器,从而增加内存泄漏的风险。

各种连接

如数据库连接、网络连接(Socket)和I/O连接。忘记显式调用 close() 方法引起的内存泄漏。

内部类和外部模块的引用

内部类的引用是很容易被遗忘的一种,一旦没有释放可能会导致一系列后续对象无法释放。此外还要小心外部模块不经意的引用,内部类是否提供相应的操作去除外部引用。

单例模式

由于单例的静态特性,使其生命周期与应用的生命周期一样长,一旦使用不恰当极易造成内存泄漏。如果单利持有外部引用,需要注意提供释放方式,否则当外部对象无法被正常回收时,会进而导致内存泄漏。

集合类泄漏

如集合的使用范围超过逻辑代码的范围,需要格外注意删除机制是否完善可靠。比如由静态属性 static 指向的集合。

单利泄漏

以下为简单逻辑代码,只为举例说明内存泄漏问题,不保证单利模式的可靠性。

AppManager 创建时需要传入一个 Context ,这个 Context 的生命周期长短至关重要。

1. 如果传入的是 Application 的 Context ,因为 Application 的生命周期等同于应用的生命周期,所以没有任何问题。

2. 如果传入的是 Activity 的 Context ,则需要考虑这个 Activity 是否在整个生命周期都不会被回收了,如果不是,则会造成内存泄漏。

非静态内部类创建静态实例造成的内存泄漏

应该将该内部类单独封装为一个单例来使用。

匿名内部类/异步线程

Runnable都使用了匿名内部类,将持有MyActivity的引用。如果任务在Activity销毁前未完成,将导致Activity的内存无法被回收,从而造成内存泄漏。

解决方法:将Runnable独立出来或使用静态内部类,可以避免因持有外部对象导致的内存泄漏。

Handler造成的内存泄漏

Handler属于TLS(Thread Local Storage)变量,生命周期与Activity是不一致的,容易导致持有的对象无法正确被释放

当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。

当主线程中实例化一个Handler对象后,它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用,所以Looper会据此回调Handle的handleMessage()方法来处理消息。只要MessageQueue中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。

另外,主线程的Looper对象会伴随该应用程序的整个生命周期。

在Java中,非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用,但是静态内部类却不会。

当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish() 掉的 Activity 就不会被回收了从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用,在这里就是指 SampleActivity)。

避免不必要的静态成员变量

对于BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等资源的使用,应在Activity销毁前及时关闭或注销。

不使用WebView对象时,应调用`destroy()`方法销毁。

Android集成微信分享功能采坑:分享不成功,一闪而过,无反应等情况

Android 微信分享遇到的问题:

1.APP_ID是否输入正确

2.官网申请时输入的签名和打包的签名是否一致(请用微信推荐的签名工具对比)

注:微信平台填写的签名是ce187ed67e05c2d8879bf66bbfdfc8b9

是apk的keystore的md5去掉冒号,大写换位小写字母形式

3.分享一闪而过

有可能的bug:签名错误,appid正确,但是申请的时候吧包名和签名写反了

微信缓存问题,重新安装微信多试几次

4.请用微信官方提供的签名获取工具

5.自己直接run到手机运行的apk包注意签名应该和申请时用的签名一致(即把debug的签名改为release的)

6.分享图片的缩略图太大,超过32k

7.换设备,重新尝试

8.保证所有配置没有问题的情况下,尝试重启手机即可……(我没有开玩笑)

利用 Android 系统原生 API 实现分享功能(2)

在之前的一篇文章 利用 Android 系统原生 API 实现分享功能 中主要说了下实现流程,但具体实施起来其实还是有许多坑要面对。那这篇文章就是提供一个封装好的 Share2 库供大家参考。

GitHub 项目地址:Share2

看过上一篇文章的同学应该知道,要调用 Android 系统内建的分享功能,主要有三步流程:

更多相关内容请参考上一篇,这里就不再重复赘述了。

知道大致的实现流程后,其实只要解决下面几个问题后就可以具体实施了。

这其实是直接决定了最终的实现形态,我们知道常见的使用场景中,只是为了在应用间分享图片和一些文件,那对于那些只是分享文本的产品而言,两者实现起来要考虑的问题完全不同。

所以为了解决这个问题,我们可以预先定好支持的分享内容类型,针对不同类型可以进行不同的处理。

在 Share2 中,一共定义了5种类别的分享内容,基本能覆盖常见的使用场景。在调用分享接口时可以直接指定内容类型,比如像文本、图片、音视频、已经其他各种类型文件。

对于不同类别的内容,可能会有不同的来源。比如文本可能就只是一个字符串对象,而对于分享图片或其他文件,我们需要一个 Uri 来标识一个资源。这其实就引出来具体实施时的一个大问题,如何获取要分享文件的 Uri,并且这个 Uri 要能被接收分享内容的应用处理才行 。

那么,如何获取要分享内容文件的 Uri?如果处理才能让接收方也能够根据 Uri 获取到文件?

我们把文件 Uri 的来源划分为下面三种类型:

常见场景 :通过文件选择器获取一个文件的 Uri

通过这种方式获取到的 Uri 是由系统 ContentProvider 返回的,在 Android 4.4 之前的版本和之后的版本有较大的区别,我们后面再说怎么处理。只要先记住这种系统返回给我们的 Uri 就行了。

比如调用系统相机进行拍照或录制音视频,要传入一个生成目标文件的 Uri ,从 7.0 开始我们需要用到 FileProvider 来实现。

如果用到了 FileProvider 就要注意跟系统 ContentProvider 返回 Uri 的区别,比如我们在 Manifest 中对 FileProvider 配置 android:authorities="com.xx.xxx.fileProvider" 属性,那这时系统返回的 Uri 格式就变成了 : content://com.xx.xxx.fileProvider... ,对于这种类型的 Uri 我们姑且叫 自定义 FileProvider 返回的 Uri ,后面一并说怎么处理。

我们调用 new File 时需要传入指定的文件路径,这个绝对路径通常是: /storage/emulated/0/... 这种样式,我们要想调用分享时也要变成 Uri 的形式才可以,那么如何把文件路径变成一个文件 Uri ?这个问题下面也一并进行回答。

前面提到了文件 Uri 的三种分类,对应不同类型处理方式也不同,不然你最先遇到的问题就是:

这是由于对系统返回的 Uri 缺失访问权限导致,所以要对应用进行临时访问 Uri 的授权才行,不然会提示权限缺失。

对于要分享系统返回的 Uri 我们可以这样进行处理:

需要注意的是对于自定义 FileProvider 返回 Uri 的处理,即使是设置临时访问权限,但是分享到第三方应用也会无法识别该 Uri

典型的场景就是,我们如果把自定义 FileProvider 的返回的 Uri 设置分享到微信或 QQ 之类的第三方应用,会提示文件不存在,这是因为他们无法识别该 Uri。

关于这个问题的处理其实跟下面要说的把文件路径变成系统返回的 Uri 一样,我们只需要把自定义 FileProvider 返回的 Uri 变成第三方应用可以识别系统返回的 Uri 就行了。

创建 FileProvider 时需要传入一个 File 对象,所以直接可以知道文件路径,那就把问题都转换成了: 如何通过文件路径获取系统返回的 Uri

下面是根据传入的 File 对象和类型来查询系统 ContentProvider 来获取相应的 Uri,已经按照不同文件类型在不同系统版本下的进行了适配。

其中 forceGetFileUri 方法是通过反射实现的,处理 7.0 以上系统的特殊情况下的兼容性,一般情况下不会调用到。Android 7.0 开始不允许 file:// Uri 的方式在不同的 App 间共享文件,但是如果换成 FileProvider 的方式依然是无效的,我们可以通过反射把该检测干掉。

通过 File Path 转成 Uri 的方式,我们最终统一了调用系统分享时传入内容 Uri 的三种不同场景,最终全部转换为传递系统返回的 Uri,让第三方应用能够正常的获取到分享内容。

Share2 按照上述方法进行了具体实施,可以通过下面的方式进行集成:

分享图片到指定界面,比如分享到微信朋友圈

GitHub 项目地址:Share2

Android 微信,qq分享文件

10M以下,建议进制算法为*1000以避免1024发生分享错误

将文件变成二进制数组,然后塞进去就OK了,我这边是直接传入的base64码省略了部分步骤

qq分享类似,不过是用intent来的

只能分享本地文件,我这边是js传的,可以将文件存到本地然后分享

File shareFileDir = StorageUtils.getExternalFileDirectory(activity.getApplicationContext(), StaticFinalUtil.SHARE_MEDIA);

                    File shareFile = new File(shareFileDir, shareMediaNew.title.concat(".").concat(shareMediaNew.fileType));

                    if (shareFile.exists()) {

                        Intent qqIntent = new Intent(Intent.ACTION_SEND);

                        Uri shareFileUri;

                        if (Build.VERSION.SDK_INT = Build.VERSION_CODES.N) {

                            //兼容7.0

                            shareFileUri = FileProvider.getUriForFile(activity.getApplicationContext(), "com.DaTong.InsuranceForAndroid.fileprovider", shareFile);

                            //添加权限 这一句表示对目标应用临时授权该Uri所代表的文件

                            qqIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

                            qqIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

                        }else {

                            shareFileUri = Uri.fromFile(shareFile);

                        }

                        qqIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

                        qqIntent.setType(getMimeType(shareFile.getPath()));

                        qqIntent.setClassName("com.tencent.mobileqq", "com.tencent.mobileqq.activity.JumpActivity");

                        qqIntent.putExtra(Intent.EXTRA_STREAM, shareFileUri);

                        activity.startActivity(qqIntent);

                        return;

                    }

Android 系统原生 API 实现分享功能

GitHub 项目地址:LocalShare-master

直接上图,这是一个典型的调用系统原生分享场景下的界面,相信大家应该都很熟悉。

那下面说一下遇到的一些问题,特别针对是 7.0 以后的系统,以及兼容一些主流 app 时遇到的坑。

前面说到分享文件时需要知道文件的类型,不然的指定类型为 / ,这样分享到某些 App 会因为无法判断文件类型而导致失败,所以最好先根据文件路径获取其文件类型。

使用这种方法获取文件类型,一定要注意 ContentResolver 获取返回为 null 的情况,不然空指针异常的崩溃率可能会让你笑不出来。实际测试中,发现在某些国产机型下,这个方法可以说直接是不可用,查询返回一直都是空,所以单纯依赖这一个方法会很不可靠。具体问题原因请看: What causes Android's ContentResolver.query() to return null?

下面按照第二条思路,按照文件头信息简单实现一个获取文件类型的例子:

// 获取文件Uri

要向在 MediaStore 中查询到文件,要不就是通知媒体库更新查询或则往里面插入一条新记录(会比较耗时)

可以参考我的另外一篇文章: Android 系统原生 API 实现分享功能(2)

参考:


新闻名称:android分享,android分享到微信朋友圈
文章路径:http://chengdu.cdxwcx.cn/article/phhjpg.html