前言

  看标题你或许不知道是什么意思,我说一个场景你大约就理解了,比方在微信中收到了好友发过来的一个名为xxx.apk的文件,这是一个运用apk,而微信中收到后就是,xxx.apk.1。你点击这个文件接受之后,微信是无法直接翻开,这个时分会有一个其他运用翻开的按钮,你点击这个按钮会出现一个弹窗,里面会列举出可以翻开apk文件的运用。

Android 答应其他运用发动您的Activity

正文

  其实不光是微信,许多的交际软件都有这个其他运用翻开的功用,例如QQ、钉钉,介绍的很详细了。那么假如要让自己的运用出现在这个弹窗列表里,该怎样做呢?

  实践上这并不是一个新的知识点,只不过出现的不是很一再,而我也在实践开发中用过,因而这儿就写出来,做个笔记

一、创建项目

  仍是和曾经相同创建项目开端,这么做是为了让看的人了解每一步的通过,有的人喜爱看源码,有的人喜爱看过程和思路。两者统筹的话就是思路源码都要有,下面创建一个名为OpenOtherApps的项目,如下图所示:

Android 答应其他运用发动您的Activity

  创建项目之后,现在手机上工作一下,先确保一下你的项目没有问题,然后你可以弄一个微信打不开的文件,比方.hex文件,.apk文件。你可以试试看将文件放到微信上去,看看能不能通过其他运用翻开。

Android 答应其他运用发动您的Activity

很明显,是不可的,那么怎样让你的运用可以支撑翻开这个文件呢?

二、添加文件类型

  添加可翻开文件类型,这儿我们需求在非发动Activity中配备,我们方才创建的项目里面自带了一个MainActivity,我们发动程序时就会翻开这个Activity。

Android 答应其他运用发动您的Activity
这个Activity不能用,那么就要创建一个能用的,在com.llw.open包下新建一个FileActivity。然后翻开AndroidManifest.xml。

代码配备如下所示:

		<activity
            android:name=".FileActivity"
            android:exported="true"
            tools:ignore="AppLinkUrlError">
            <intent-filter >
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*"/>
                <data android:host="*" />
                <data android:scheme="file" />
                <data android:scheme="content" />
            </intent-filter>
        </activity>

这儿乍一看好像都知道,又好像不知道,下面说明一下:

我们在微信、QQ、钉钉中通过其他运用翻开文件,是不是就是Activity与Activity之间的交互呢?那么就会用到Intent,这儿的intent-filter就是起到过滤的效果,不能什么都能收到。它里面有三个数据,

  • action 表明目的。android.intent.action.VIEW,用于显示用户的数据。比较通用,会根据用户的数据类型翻开相应的Activity。
  • category 表明类别。android.intent.category.DEFAULT,设置Activity是否应该作为一个段数据实行的默认选项。
  • data 表明数据。mimeType,限制辨认的文件类型。这儿设置为<data android:mimeType="*/*"/>表明支撑一切数据类型。

这儿的mimeType还有许多文件类型的支撑,如下所示:

{".3gp", "video/3gpp"},
{".apk", "application/vnd.android.package-archive"},
{".asf", "video/x-ms-asf"},
{".avi", "video/x-msvideo"},
{".bin", "application/octet-stream"},
{".bmp", "image/bmp"},
{".c", "text/plain"},
{".class", "application/octet-stream"},
{".conf", "text/plain"},
{".cpp", "text/plain"},
{".doc", "application/msword"},
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{".xls", "application/vnd.ms-excel"},
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".exe", "application/octet-stream"},
{".gif", "image/gif"},
{".gtar", "application/x-gtar"},
{".gz", "application/x-gzip"},
{".h", "text/plain"},
{".htm", "text/html"},
{".html", "text/html"},
{".jar", "application/java-archive"},
{".java", "text/plain"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "application/x-javascript"},
{".log", "text/plain"},
{".m3u", "audio/x-mpegurl"},
{".m4a", "audio/mp4a-latm"},
{".m4b", "audio/mp4a-latm"},
{".m4p", "audio/mp4a-latm"},
{".m4u", "video/vnd.mpegurl"},
{".m4v", "video/x-m4v"},
{".mov", "video/quicktime"},
{".mp2", "audio/x-mpeg"},
{".mp3", "audio/x-mpeg"},
{".mp4", "video/mp4"},
{".mpc", "application/vnd.mpohun.certificate"},
{".mpe", "video/mpeg"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".mpg4", "video/mp4"},
{".mpga", "audio/mpeg"},
{".msg", "application/vnd.ms-outlook"},
{".ogg", "audio/ogg"},
{".pdf", "application/pdf"},
{".png", "image/png"},
{".pps", "application/vnd.ms-powerpoint"},
{".ppt", "application/vnd.ms-powerpoint"},
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{".prop", "text/plain"},
{".rc", "text/plain"},
{".rmvb", "audio/x-pn-realaudio"},
{".rtf", "application/rtf"},
{".sh", "text/plain"},
{".tar", "application/x-tar"},
{".tgz", "application/x-compressed"},
{".txt", "text/plain"},
{".wav", "audio/x-wav"},
{".wma", "audio/x-ms-wma"},
{".wmv", "audio/x-ms-wmv"},
{".wps", "application/vnd.ms-works"},
{".xml", "text/plain"},
{".z", "application/x-compress"},
{".zip", "application/x-zip-compressed"},
{"", "*/*"} 

  拿其间的apk格式来说,你就可以这样写:<data android:mimeType="application/vnd.android.package-archive"/>其他数据格式也是相同的,下面仍是用<data android:mimeType="*/*"/>,data中还有其他特点值,如下图所示:

Android 答应其他运用发动您的Activity

我们从一个Activity传递到另一个Activity的Uri,Uri的构成是 :]()<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

  • scheme:比方http、https、file、content。
  • host:主机。
  • port:端口号。
  • path:无缺的途径。
  • pathPattern:是断定无缺途径是否匹配用的正则表达式。
  • pathPrefix:也是正则表达式,它匹配的是途径的前缀信息。

工作一下:

Android 答应其他运用发动您的Activity

马到成功,现在我们的App就可以翻开这个hex文件了。

三、只翻开指定文件类型

  这儿还有一个问题,我现在的app可以翻开任何文件,可是这并不是最优的解决方法,由于我的文件类型是自定义的,mimeType无法匹配到,因而我们需求先翻开一切文件格式类型,然后通过匹配符只翻开指定的文件格式。需求修正一下AndroidManifest.xml中的代码,如下所示:

				<!--hex-->
                <data android:pathPattern="/.*\\.hex" />
                <data android:pathPattern="/.*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.hex" />
                <data android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.hex" />

我这儿设置翻开hex格式文件,代码添加方位如下所示:

Android 答应其他运用发动您的Activity

  这儿添加了许多的途径,由于要做文件夹匹配,现在你再工作一下,然后你通过微信收到的文件,点击其他运用翻开,你会发现假如不是hex格式文件,弹窗列表里面都不会有这个运用在里面。这就是要到达的效果,工作看看。

Android 答应其他运用发动您的Activity

四、获取文件的途径

  当我们通过这种方法翻开自己App的时分,在Activity中是会收到一个Uri的,我们可以通过Uir拿到文件的途径。下面简略写一些代码,首先在app的build.gradle中敞开viewBinding,代码如下:

	buildFeatures {
        viewBinding = true
    }

然后Sync Now。然后修正activity_file.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FileActivity">
    <TextView
        android:id="@+id/tv_path"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

然后修正FileActivity中的代码,如下所示:

class FileActivity : AppCompatActivity() {
    private lateinit var binding: ActivityFileBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityFileBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
    override fun onResume() {
        super.onResume()
        Log.d("FileActivity", "onResume: ${intent.data?.path}")
        binding.tvPath.text = intent.data?.path
    }
}

这就是十分简略的代码,没啥好说的,下面工作一下看看:

Android 答应其他运用发动您的Activity

你可以看到控制台也打印了途径:

Android 答应其他运用发动您的Activity

五、文件写入

  光是知道这个文件的途径仍是不行的,要想操作这个文件,我们需求将此文件从微信的运用文件夹中写入到自己的运用目录下,怎样做呢?代码如下:

	private fun uriToFile(uri: Uri?): String? {
        if (uri == null) {
            return null
        }
        //获得ContentResolver,用于拜访其它运用数据
        val resolver: ContentResolver = contentResolver
        //获得URI途径
        val pathUri = uri.path!!.lowercase(Locale.getDefault())
        //获取文件名称
        val fileName = pathUri.substring(pathUri.lastIndexOf("/") + 1)
        //新文件的途径
        val filePath = getExternalFilesDir(null)!!.absolutePath
        //创建文件
        val file = File(filePath, fileName)
        val parentFile = file.parentFile
        if (parentFile != null) {
            if (!parentFile.exists()) {
                parentFile.mkdirs()
            }
        }
        if (file.exists()) {
            return "文件已存在"
        }
        val inputStream: InputStream?
        return try {
            file.createNewFile()
            inputStream = resolver.openInputStream(uri)
            val outputStream = FileOutputStream(file.absolutePath)
            write(inputStream, outputStream)
            "文件已保存到本地。"
        } catch (e: IOException) {
            e.printStackTrace()
            "过错失常:" + e.message
        }
    }

  通过ContentResolver就可以拜访其他运用数据,这个是系统的,然后通过Uri的到此文件在微信运用中的途径和文件的名称。然后在自己的运用目录下创建文件,通过微信文件的输入流和当时运用文件的输出流,将数据从输入流写到输出流,这儿还有一个write()函数,代码如下:

	private fun write(inputStream: InputStream?, outputStream: OutputStream) {
        val buffer = ByteArray(1024 * 1024)
        while (true) {
            try {
                val len = inputStream!!.read(buffer)
                if (len < 0) break
                outputStream.write(buffer, 0, len)
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        try {
            outputStream.flush()
            inputStream!!.close()
            outputStream.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

写入完结之后,关闭输入流和输出流,即可,然后在onResume中调用。

	override fun onResume() {
        super.onResume()
        Log.d("FileActivity", "onResume: ${intent.data?.path}")
        binding.tvPath.text = intent.data?.path
        Toast.makeText(this,uriToFile(intent.data),Toast.LENGTH_SHORT).show()
    }

通过Toast来提示用户是否写入成功,下面工作一下看看效果。

Android 答应其他运用发动您的Activity

能仿制过来,这样做你可以不用任何权限,也不需求配备FileProvider。只不过你运用文件夹下的文件,当然的App被卸载掉时会铲除。

六、源码

假如对你有所帮忙的话,不妨Fork & Star

GitHub:OpenOtherApps