20212419 2023-2024-2 《移动平台开发与实践》第三次作业


20212419 2023-2024-2 《移动平台开发与实践》第三次作业

1.实验内容

1.掌握Android四大组件(Activity、Service、BroadcastReceiver、ContentProvider)的基本概念和使用方法。

Activity(活动):用户界面的显示和交互管理者。
Service(服务):在后台执行长时间任务的组件。
BroadcastReceiver(广播接收器):接收系统或应用程序发送的广播消息。
ContentProvider(内容提供器):数据的安全共享和访问管理者。

2.通过实验深入了解四大组件的使用方法,加深对四大组件的理解。

2.实验过程

2.1Activity组件实践(注:完成3.3,实现从firstActivity跳转到secondActivity即可)

1.创建一个简单的Activity并添加UI元素:在res/layout目录下创建一个 XML 布局文件,例如activity_first.xml,并添加所需的 UI 元素。
然后在 FirstActivity.kt 中加载该布局。
核心代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FirstActivity">

    <Button
        android:id="@+id/btnNavigate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Go to Second Activity"
        android:layout_centerInParent="true"/>

</RelativeLayout>
// FirstActivity.kt
class FirstActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        
        val btnNavigate = findViewById<Button>(R.id.btnNavigate)
        btnNavigate.setOnClickListener {
            // Implement navigation to SecondActivity here
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
        }
    }
}

2.实现Activity之间的跳转与传值:
创建一个新的 Activity,例如 SecondActivity.kt。
在 FirstActivity 中使用 Intent 传递数据到 SecondActivity。

// FirstActivity.kt
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("key", "value") // Replace "key" and "value" with your data
startActivity(intent)
// SecondActivity.kt
val data = intent.getStringExtra("key") // Replace "key" with the key you used in FirstActivity

3.了解Activity的生命周期并编写代码验证:
在 FirstActivity 和 SecondActivity 的生命周期方法中添加日志打印。
在 Logcat 中查看输出,验证生命周期方法的调用顺序。

// FirstActivity.kt
override fun onStart() {
    super.onStart()
    Log.d("ActivityLifecycle", "FirstActivity onStart")
}
override fun onStop() {
    super.onStop()
    Log.d("ActivityLifecycle", "FirstActivity onStop")
}
// SecondActivity.kt
override fun onStart() {
    super.onStart()
    Log.d("ActivityLifecycle", "SecondActivity onStart")
}

activity跳转

2.2 Service组件实践(注:完成10.3,启动和停止Service即可)

1.创建一个Service,用于在后台执行长时间运行的任务。

package com.example.ex3_2
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log

class MyService : Service() {
    private val mBinder = DownloadBinder()
    class DownloadBinder : Binder() {
        fun startDownload() {
            Log.d("MyService", "startDownload executed")
        }
        fun getProgress(): Int {
            Log.d("MyService", "getProgress executed")
            return 0
        }
    }
    override fun onBind(intent: Intent): IBinder {
        return mBinder
    }
    override fun onCreate() {
        super.onCreate()
        Log.d("MyService", "onCreate executed")
    }
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.d("MyService", "onStartCommand executed")
        return super.onStartCommand(intent, flags, startId)
    }
    override fun onDestroy() {
        Log.d("MyService", "onDestroy executed")
        super.onDestroy()
    }
}

2.通过Intent启动和停止Service。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val startServiceBtn = findViewById<Button>(R.id.startServiceBtn)
    val stopServiceBtn = findViewById<Button>(R.id.stopServiceBtn)
    startServiceBtn.setOnClickListener {
        val intent = Intent(this, MyService::class.java)
        startService(intent) // 启动Service
    }
    stopServiceBtn.setOnClickListener {
        val intent = Intent(this, MyService::class.java)
        stopService(intent) // 停止Service
    }
    val bindServiceBtn = findViewById<Button>(R.id.bindServiceBtn)
    val unbindServiceBtn = findViewById<Button>(R.id.unbindServiceBtn)
    bindServiceBtn.setOnClickListener {
        val intent = Intent(this, MyService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE) // 绑定Service
    }
    unbindServiceBtn.setOnClickListener {
        unbindService(connection) // 解绑Service
    }

Service组件

2.3 BroadcastReceiver组件实践

1.创建activity类管理。
要实现强制下线功能就需要先关闭所有的 Activity,然后回到登录界面。所以先创建一个ActivityCollector类用于管理所有的Activity,并创建 BaseActivity类作为所有 Activity的父类。

//ActivityCollector.kt
object ActivityCollector {
    private val activities = ArrayList<Activity>()
    fun addActivity(activity: Activity){
        activities.add(activity)
    }
    fun removeActivity(activity:Activity){
        activities.remove(activity)
    }
    fun finishAll(){
        for(activity in activities){
            if(!activity.isFinishing){
                activity.finish()
            }}
        activities.clear()
    }
}
//BaseActivity
open class BaseActivity : AppCompatActivity() {
    lateinit var receiver: ForceOfflineReceiver
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityCollector.addActivity(this)
    }
    override fun onResume() {
        super.onResume()
        val intentFilter = IntentFilter()
        intentFilter.addAction("com.example.broadcasttest.FORCE_OFFLINE")
        receiver = ForceOfflineReceiver()
        registerReceiver(receiver, intentFilter, RECEIVER_EXPORTED)
    }
    override fun onPause() {
        super.onPause()
        unregisterReceiver(receiver)
    }
    override fun onDestroy() {
        super.onDestroy()
        ActivityCollector.removeActivity(this)
    }
    inner class ForceOfflineReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            AlertDialog.Builder(context).apply {
                setTitle("System Warning")
                setMessage("20212318ysl are forced to be offline. Please try to login again.")
                setCancelable(false) // 将对话框设为不可取消,否则用户按一下 Back键就可以关闭对话框继续使用程序
                setPositiveButton("OK") { _, _ ->
                    ActivityCollector.finishAll() // 销毁所有Activity
                    val i = Intent(context, LoginActivity::class.java)
                    context.startActivity(i) // 重新启动LoginActivity
                }
                show()
            }
        }
    }
}

2.创建登录的界面,用于模拟用户登录,并在广播提示强制下线后跳转回到此页面。

//LoginActivity.kt
class LoginActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        val login: Button = findViewById(R.id.login)
        val accountEdit: EditText = findViewById(R.id.accountEdit)
        val passwordEdit: EditText = findViewById(R.id.passwordEdit)
        login.setOnClickListener {
            val account = accountEdit.text.toString()
            val password = passwordEdit.text.toString()
            // 如果账号是lf20212419且密码是20212419,就认为登录成功
            if (account == "lf20212419" && password == "20212419") {
                val intent = Intent(this, MainActivity::class.java)
                startActivity(intent)
                finish()
            } else {
                Toast.makeText(
                    this, "account or password is invalid",
                    Toast.LENGTH_SHORT
                ).show()
            }
        }
    }
//activity_login.xml
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Account:" />

        <EditText
            android:id="@+id/accountEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical" />
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Password:" />

        <EditText
            android:id="@+id/passwordEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical"
            android:inputType="textPassword" />
    </LinearLayout>

    <Button
        android:id="@+id/login"
        android:layout_width="200dp"
        android:layout_height="60dp"
        android:layout_gravity="center_horizontal"
        android:text="Login" />

</LinearLayout>

3.创建登录成功页面,此页面只需要一个按钮触发强制下线功能。

//MainActivity
class MainActivity : BaseActivity() {
    override fun onCreate(savedInstanceState:Bundle?){
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val forceOffline: Button = findViewById(R.id.forceOffline)
        forceOffline.setOnClickListener {
            val intent = Intent("com.example.broadcasttest.FORCE_OFFLINE")
            sendBroadcast(intent)
        }
    }
}
//activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/forceOffline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Force Logout"
        android:layout_centerInParent="true"/>

</RelativeLayout>

BroadcastReceiver组件

2.4 ContentProvider组件实践(注:完成8.3,P329-P333的实践)

1.创建一个ContentProvider,用于共享数据给其他应用程序。
请添加图片描述

2.在另一个应用程序中访问该ContentProvider,实现数据操作。

//MainActivity
package com.example.ex3_4

import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
import android.provider.ContactsContract
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat


class MainActivity : AppCompatActivity() {

    private val contactsList = ArrayList<String>()
    private lateinit var adapter: ArrayAdapter<String>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, contactsList)
        val contactsView: ListView = findViewById(R.id.contactsView)
        contactsView.adapter = adapter
        if (ContextCompat.checkSelfPermission(
                this,
                android.Manifest.permission.READ_CONTACTS
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this, arrayOf(android.Manifest.permission.READ_CONTACTS), 1
            )
        } else {
            readContacts()
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            1 -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    readContacts()
                } else {
                    Toast.makeText(
                        this, "You denied the permission", Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }
    }

    @SuppressLint("Range")
    private fun readContacts() {
// 查询联系人数据
        contentResolver.query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null
        )?.apply {
            while (moveToNext()) {
// 获取联系人姓名
                val displayName = getString(
                    getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
                )
// 获取联系人手机号
                val number = getString(
                    getColumnIndex(
                        ContactsContract.CommonDataKinds.Phone.NUMBER
                    )
                )
                contactsList.add("$displayName\n$number")
            }
            adapter.notifyDataSetChanged()
            close()
        }
    }
}

请添加图片描述

3.学习中遇到的问题及解决

  • 问题1:在测试Activity组件时,点击“Go to Second Activity”按钮后,应用退出到桌面,然后无反应。

  • 问题1解决方案:查找资料发现可能出现的错误导致该问题出现的有

  • 1.Activity定义错误: 确保在 AndroidManifest.xml 文件中正确声明了 FirstActivity 和 SecondActivity。确保它们的 部分正确设置,以确保系统可以正确识别它们。

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

2.传递数据异常: 确保在 FirstActivity.kt 中正确传递数据到 SecondActivity.kt,并且在 SecondActivity.kt 中正确接收该数据。

3.Activity生命周期异常: 检查在 FirstActivity.kt 和 SecondActivity.kt 中是否有异常导致应用程序崩溃。确保生命周期方法(如 onCreate、onStart 等)中的代码没有导致异常。

  • 问题1解决方案:修改manifest文件中的代码, 在 AndroidManifest.xml 文件中正确声明了 FirstActivity 和 SecondActivity即可。

  • 问题2:在进行ContentProvider组件测试时出现报错:Unresolved reference: contactsView。

  • 问题分析:缺少对contactsView的定义。

  • 问题2解决方案:定义contactsView。

  • val contactsView: ListView = findViewById(R.id.contactsView)

4.学习感悟、思考等)

在学习和实践了Activity组件、Service组件、BroadcastReceiver组件和ContentProvider组件后,我对Android应用程序的核心组件有了更深入的理解。通过对Activity、Service、BroadcastReceiver和ContentProvider组件的学习和实践,我不仅掌握了它们的基本用法和原理,还深入理解了它们在Android应用程序中的作用和意义。这些知识和经验对我今后的Android开发工作将起到重要的指导作用,帮助我更好地设计和开发高质量的Android应用程序。
尤其是ContentProvider组件的用法。ContentProvider用于在不同的应用程序之间共享数据,它提供了一种标准化的接口来访问应用程序的数据。通过实践,我创建了一个简单的ContentProvider,并在另一个应用程序中访问了该ContentProvider,成功实现了数据操作。这让我意识到了ContentProvider的重要性和灵活性,以及如何通过ContentProvider来实现数据共享和访问。

参考资料

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值