《Android编程权威指南》第 23 章,本章将创建新应用啦,叫 NerdLauncher,然后技术点是关于隐式 intent 和 intent 过滤器。本章应用呢,将会展示设备上的其他应用,还可以启动其他应用。

一、创建 NerdLauncher 项目

创建项目,添加 RecyclerView 用于显示 App 列表。

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
class MainActivity : AppCompatActivity() {

    private lateinit var mBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        mBinding.recyclerView.layoutManager = LinearLayoutManager(this)
    }
}

二、解析隐式 intent

PackageManager 可用来获取所有可启动主 activity。可启动主 activity 都带有包含 MAIN 操作和 LAUNCHER 类别的 intent 过滤器。如下所示:

<intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

新增 setupAdapter() 函数,并在 onCreate() 中调用:

private fun setupAdapter(){
       val startupIntent = Intent(Intent.ACTION_MAIN).apply {
           addCategory(Intent.CATEGORY_LAUNCHER)
       }
       val activities = packageManager.queryIntentActivities(startupIntent,0)
       Log.i(TAG, "Found ${activities.size} activities")
   }

上述代码就有创建操作设为 ACTION_MAIN、类别为 CATEGORY_LAUNCHER 的隐式 intent。

PackageManager.queryIntentActivities(Intent, Int) 会返回包含所有activity(有匹配目标 intent 的过滤器)的 ResolveInfo 信息。这里参数 0 表示不打算修改查询结果。

android开发排行榜 android开发权威指南_android开发排行榜

接下来需要展示列表,使用 activity 标签名「应用名」。首先,使用ResolveInfo.loadLabel(PackageManager) 函数,对 ResolveInfo 对象中的 activity 标签按首字母排序。

在上述方法下面加入代码:

activities.sortWith(Comparator { a, b -> 
            String.CASE_INSENSITIVE_ORDER.compare(
                a.loadLabel(packageManager).toString(),
                b.loadLabel(packageManager).toString()
            )
        })

然后,定义一个 ViewHolder 用来显示 activity 标签名。使用成员变量存储 ResolveInfo 。

private class ActivityHolder(itemView: View):RecyclerView.ViewHolder(itemView){
        
        private val tvName = itemView as TextView
        private lateinit var resolveInfo:ResolveInfo
        
        fun bindActivity(resolveInfo: ResolveInfo){
            this.resolveInfo = resolveInfo
            val packageManager = itemView.context.packageManager
            val appName = resolveInfo.loadLabel(packageManager).toString()
            tvName.text = appName
        }
    }

接下来实现 RecyclerView.Adapter:

private class ActivityAdapter(val activities:List<ResolveInfo>):RecyclerView.Adapter<ActivityHolder>(){
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ActivityHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val view =layoutInflater.inflate(android.R.layout.simple_list_item_1,parent,false)
                 return ActivityHolder(view)
            }

        override fun onBindViewHolder(holder: ActivityHolder, position: Int) {
            val resolveInfo = activities[position]
            holder.bindActivity(resolveInfo)
        }

        override fun getItemCount(): Int {
            return activities.size
        }
    }

最后,把 adapter 实例配置给 RecyclerView ,在 setupAdapter 末尾处添加:

mBinding.recyclerView.adapter = ActivityAdapter(activities)

运行结果:

android开发排行榜 android开发权威指南_学习_02

三、在运行时创建显式intent

接下来做点击列表,用显示 intent 启动对应的 activity 啦。

要创建启动 activity 的显式 intent,就需要从 ResolveInfo 对象中获取 activity 的包名与类名。

更新 ActivityHolder 类实现一个点击监听器,并从 activityInfo 中获取必要信息,创建一个显示 intent 去启动目标 activity。

private class ActivityHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
        View.OnClickListener {
       ...
        init {
            tvName.setOnClickListener(this)
        }
       ...
        override fun onClick(v: View) {
            val activityInfo = resolveInfo.activityInfo
            val intent = Intent(Intent.ACTION_MAIN).apply {
                setClassName(activityInfo.applicationInfo.packageName, activityInfo.name)
            }
            val context = v.context
            context.startActivity(intent)
        }
    }

然后运行项目,点击列表 item,就可以跳转到相应的 App 了。