文章目录
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来实现数据共享和访问。