河南理工大学Android开发课程:共40学时(24理论学时、16实践学时),学分2.5。
Android四层结构
应用层(System Apps) 系统内置的应用程序以及非系统级的应用程序 都属于应用层,负责与用户进行交互,一般使用Java或者Kotlin开发,也就是我们所说的应用层开发。
应用框架层(Java API Framework) 这一层为应用层开发提供所需要的Java API,也就是常说的Android系统的Java源码,这一层由Java代码编写,所以叫JavaFramework。
系统运行库层(Native) 从图中可以看出这里分为了两个部分,分别是C/C++程序库和Android运行时库。
C/C++库 这些C/C++库程序可以被应用框架层所使用,至于为什么Android系统要使用C/C++库,而不都用Java库,原因主要是C/C++代码执行效率更高,而且很多功能有成熟的C++代码,不用重新写
Android运行时库
分为核心库和ART。*(Android运行时环境(ART)自Android 4.4版本引入,并在后续版本中逐步取代了原有的Dalvik作为Android平台的默认运行时环境。)*其中核心库提供了Java语言核心库的大多数功能,这样开发者可以使用Java语言来编写Android应用。而ART则是专门为移动设备定制的Java虚拟机,在之前还有被淘汰的Dalvik虚拟机。ART和JVM还是有很大的区别,它是由C++编写,用来运行Java程序,所以非常关键。而且Android系统还允许同时允许多个ART实例,所以在Android系统中,每个进程都有一个虚拟机,一个APP发生崩溃不会影响其他APP。
Linux内核层(Linux Kernel) Android的核心服务是基于Linux内核,同时在该基础上添加了Android专用的驱动,比如Binder。由于Linux系统的优秀性,Android在基于Linux于安全性、内存管理、进程管理等都有很大优势。
Android四大组件
Activity(活动)
用户交互的界面,负责展示UI并处理用户操作(如点击、滑动)。
一个应用通常由多个Activity组成,通过栈管理(任务栈)。 通过Intent跳转或传递数据。 生命周期回调(如onCreate()、onDestroy())用于管理资源。
Service(服务)
在后台执行长时间运行的操作(如下载、播放音乐),无界面。
启动服务:通过startService()启动,需手动停止。 绑定服务:通过bindService()与组件绑定,可跨进程通信(IPC)。 典型场景:后台网络请求、定时任务等。
BroadcastReceiver(广播接收器)
可启用Activity、Notification通知
ContentProvider(内容提供者)
通过ContentResolver操作数据(增删改查)。 支持权限控制,保障数据安全。
知识点 Intent(意图):四大组件间通信的纽带,可显式/隐式指定目标组件。
AndroidManifest.xml:所有组件需在此文件中声明(注册),否则无法使用。
四大组件各司其职,共同构成Android应用的基石:Activity管界面、Service管后台、BroadcastReceiver管事件、ContentProvider管数据共享。
activity的生命周期
过程
全生命周期 函数的调用顺序:onCreate() →onStart()→onResume()→ onPause()→ onStop()→ onDestroy()
调用onCreate()函数分配资源、调用onStart()将Activity显示在屏幕上、调用onResume()获取屏幕焦点、调用onPause()、onStop()和onDestroy(),释放资源并销毁进程
onStart()(显示在屏幕上):仅表示 Activity 已可见,但尚未完全准备好接收用户交互(如界面元素可能还在加载); onResume()(获取屏幕焦点):是 Activity 可见性的进一步确认,此时界面元素已就绪,可真正响应用户操作; onPause()这里分两路,分为完全遮挡和不完全遮挡。
Log类 Android 的 Log 类提供了不同级别的日志输出方法,用于不同场景的调试和问题追踪。以下是它们的含义和典型使用场景:
1. Log.v() - Verbose(详细日志)
级别 :最低,输出最详细的日志信息
使用场景 :
开发阶段的详细流程跟踪
需要记录大量调试信息时(如循环、高频事件)
生产环境应关闭
示例 :1 2 Log.v("TAG" , "onCreate() called" );
2. Log.d() - Debug(调试日志)
级别 :调试信息
使用场景 :
开发时检查变量值、方法调用流程
临时调试问题(完成后建议删除)
生产环境建议关闭
示例 :1 2 Log.d("TAG" , "User ID: " + userId);
3. Log.i() - Info(信息日志)
级别 :重要运行时信息
使用场景 :
记录关键业务流程(如用户登录、支付成功)
需要长期保留的运行时状态
生产环境可保留
示例 :1 2 Log.i("TAG" , "App started, version: 1.2.0" );
4. Log.w() - Warning(警告日志)
级别 :潜在问题
使用场景 :
捕获非预期但可恢复的情况(如低内存、网络超时重试)
标记需要关注的边界条件
生产环境必须监控
示例 :1 2 Log.w("TAG" , "Low memory, cache cleared" );
5. Log.e() - Error(错误日志)
级别 :严重错误
使用场景 :
记录崩溃前的错误(如异常捕获)
关键功能失败(如数据库写入失败)
生产环境必须紧急处理
示例 :1 2 3 4 5 6 try { } catch (Exception e) { Log.e("TAG" , "Failed to save data" , e); }
创建一个跳转按钮 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Button btn=findViewById(R.id.btn); btn.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { Log.d(TAG, "btn" ); Intent intent = new Intent (MainActivity2.this ,MainActivity.class); startActivity(intent); } });
源码:
1 2 3 public interface OnClickListener { void onClick (View v) ; }
线性布局的权重 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <LinearLayout android:orientation ="vertical" android:layout_weight ="1" //该行为权重 android:layout_width ="match_parent" android:layout_height ="0dp" > //vertical时,高为0 <TextView android:layout_weight ="2" android:layout_width ="match_parent" android:layout_height ="0dp" > </TextView > <TextView android:layout_weight ="2" android:layout_width ="match_parent" android:layout_height ="0dp" > </TextView > <TextView android:layout_weight ="2" android:layout_width ="match_parent" android:layout_height ="0dp" > </TextView > <TextView android:layout_weight ="2" android:layout_width ="match_parent" android:layout_height ="0dp" > </TextView > </LinearLayout > <LinearLayout android:orientation ="horizontal" android:layout_weight ="1" android:layout_width ="match_parent" android:layout_height ="0dp" > <TextView android:layout_weight ="3" android:layout_width ="0dp" android:layout_height ="match_parent" > </TextView > <TextView android:layout_weight ="3" android:layout_width ="0dp" android:layout_height ="match_parent" > </TextView > <TextView android:layout_weight ="3" android:layout_width ="0dp" android:layout_height ="match_parent" > </TextView > <TextView android:layout_weight ="3" android:layout_width ="0dp" android:layout_height ="match_parent" > </TextView > <TextView android:autoLink ="phone" //可设置不同的值 ,对应的部分会成为超链接 android:text ="http://www.baidu.com 13312345678 test@163.com" android:layout_width ="match_parent" android:layout_height ="wrap_content" > </TextView > </LinearLayout >
做一个登录界面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class MainActivity extends AppCompatActivity { private EditText usernameEditText; private EditText passwordEditText; private Button loginButton; private Button registerButton; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); EdgeToEdge.enable(this ); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); usernameEditText = findViewById(R.id.usernameEditText); passwordEditText = findViewById(R.id.passwordEditText); loginButton = findViewById(R.id.loginButton); registerButton = findViewById(R.id.registerButton); loginButton.setOnClickListener(new View .OnClickListener(){ @Override public void onClick (View view) { if ("" .equals(usernameEditText.getText().toString().trim()) || "" .equals(passwordEditText.getText().toString().trim())){ Toast.makeText(MainActivity.this , "用户名/密码不能为空" , Toast.LENGTH_SHORT).show(); return ; } SharedPreferences sp = getSharedPreferences("user" ,MODE_PRIVATE); if (sp.getString("username" ,"1" ).equals(usernameEditText.getText().toString()) && sp.getString("password" ,"2" ).equals(passwordEditText.getText().toString())){ Toast.makeText(MainActivity.this , "登录成功" , Toast.LENGTH_SHORT).show(); } } }); registerButton.setOnClickListener(new View .OnClickListener(){ @Override public void onClick (View view) { Intent intent = new Intent (MainActivity.this ,MainActivity2.class); startActivity(intent); } }); } }
做一个注册界面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 public class MainActivity2 extends AppCompatActivity { private EditText registerUsernameEditText; private EditText registerPasswordEditText; private EditText confirmPasswordEditText; private Button registerSubmitButton; private static final String PREFS_NAME="user" ; private static final String USERNAME_KEY="username" ; private static final String PASSWORD_KEY="password" ; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); EdgeToEdge.enable(this ); setContentView(R.layout.activity_main2); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); registerUsernameEditText = findViewById(R.id.registerUsernameEditText); registerPasswordEditText = findViewById(R.id.registerPasswordEditText); confirmPasswordEditText = findViewById(R.id.confirmPasswordEditText); registerSubmitButton = findViewById(R.id.registerSubmitButton); registerSubmitButton.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View view) { String username = registerUsernameEditText.getText().toString(); String password = registerPasswordEditText.getText().toString(); String confirmPassword = confirmPasswordEditText.getText().toString(); if (username.isEmpty() || password.isEmpty() || !password.equals(confirmPassword)){ Toast.makeText(MainActivity2.this , "用户名与密码都不能为空,而且两次密码的输入都需要一致" , Toast.LENGTH_SHORT).show(); return ; } SharedPreferences sharedPreferences = getSharedPreferences(PREFS_NAME,MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(USERNAME_KEY,username); editor.putString(PASSWORD_KEY,password); editor.apply(); Toast.makeText(MainActivity2.this , "注册成功" , Toast.LENGTH_SHORT).show(); Intent intent = new Intent (MainActivity2.this ,MainActivity.class); startActivity(intent); finish(); } }); } }
做一个可进行明/暗文切换的输入框 1 2 3 4 5 6 7 8 9 10 11 12 <com.google.android.material.textfield.TextInputLayout android:layout_width ="match_parent" android:layout_height ="wrap_content" app:endIconMode ="password_toggle" > <com.google.android.material.textfield.TextInputEditText android:id ="@+id/passwordEditText" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:hint ="密码" android:inputType ="textPassword" /> </com.google.android.material.textfield.TextInputLayout >
密码格式的限制 1 2 3 4 if (!password.matches(".*\\d.*" ) || !password.matches(".*[a-zA-Z].*" ) || !password.matches(".*[^a-zA-Z0-9].*" )){ Toast.makeText(MainActivity2.this , "必须包含一个数字、一个字母、一个特殊字符" , Toast.LENGTH_SHORT).show(); return ; }
匿名内部类,以Handler为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private class MyHandler extends Handler { public MyHandler () { super (Looper.getMainLooper()); } @Override public void handleMessage (@NonNull Message msg) { textView.setText((String) msg.obj); } } private Handler handler;@Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.textView); handler = new MyHandler (); }
子线程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class MainActivity extends AppCompatActivity { private TextView textView; private Handler handler; private int counter = 0 ; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); EdgeToEdge.enable(this ); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); textView = findViewById(R.id.textView); handler = new Handler (Looper.getMainLooper()){ @Override public void handleMessage (@NonNull Message msg) { super .handleMessage(msg); String result = (String)msg.obj; textView.setText(result); } }; new Thread (new Runnable () { @Override public void run () { try { Thread.sleep(2000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } Message message= Message.obtain(); message.obj = "后台任务完成" ; handler.sendMessage(message); } }).start(); } }
布局、组件及其属性 LinearLayout(线性布局)
必备属性 android:orientation=”vertical”
RelativeLayout(相对布局) -
TextView(文本框)
android:text=”我是第二个页面”
android:textColor=”#FF0000”
android:background=”#FF0000”
android:layout_width=”match_parent” //和父容器一样
android:layout_height=”wrap_content” //合适,不大不小,默认规定的值
android:gravity=”right” //靠右显示
android:layout_width=”60dp” //单位为dp
android:layout_alignBaseline=”@id/etid”
android:layout_alignBaseline=”@id/etid” //是一个在 RelativeLayout 中使用的属性,用于将一个视图的基线(baseline)与另一个视图的基线对齐。这个属性通常用于对齐文本控件(如 TextView 或 EditText)的文本基线,使它们在视觉上对齐。$仅适用于 RelativeLayout
android:layout_toRightOf //用于将当前视图的左边缘放置在指定视图的右边缘的右侧。$仅适用于 RelativeLayout
android:layout_below=”@id/etid” //是一个在 RelativeLayout 中使用的属性,用于将当前视图的顶部放置在etid视图的底部的下方。这个属性通常用于控制视图之间的垂直布局关系。 $仅适用于 RelativeLayout 作用
android:layout_alignRight=”@id/etpwd” //是 Android 布局文件中的一种属性,用于指定当前视图的右边缘与另一个视图(这里是 etpwd)的右边缘对齐。$仅适用于 RelativeLayout
android:layout_toLeftOf=”@id/btncancel” //用于将一个视图的右边缘放置在另一个视图的左边缘的左侧 $仅适用于 RelativeLayout
android:id=”@+id/btn”
android:text=”跳转”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginRight=”10dp” //是一个用于设置视图(View)在父布局中右侧外边距的属性。它定义了视图与父布局右侧之间的空间距离。
EditText(文本编辑框) 小知识点 颜色表示 #RGB:分别表示红、绿、蓝三原色的值(该表示方法只支持0F这16级的颜色)来表示颜色。 #ARGB:分别表示透明度(只支持0F这16级的透明度)、红、绿、蓝的三原色的值该表示方法只支持0F这16级的颜色) #RRGGBB:分别表示红、绿、蓝三原色的值(该表示方法只支持00FF这256级的颜色)来表示颜色。 #AARRGGBB:分别表示透明度(只支持00FF这256级的透明度)、红、绿、蓝三原色的值(该表示方法只支持00FF这256级的颜色)来表示颜色。
dp与sp SP 是一种缩放独立的像素单位,主要用于定义字体大小
padding和layout_margin
属性
作用对象
效果
类比(CSS)
padding
视图内部的内容与边界之间的距离
缩小视图内部内容的显示区域
padding
layout_margin
视图外部与其他视图之间的距离
扩大视图占据的空间,推开周围视图
margin
先按照如图所示建立文件夹
编写option.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <item android:title ="保存" android:id ="@+id/save" app:showAsAction ="always" /> <item android:title ="设置" android:id ="@+id/setting" /> <item android:title ="更多操作" > <menu > <item android:title ="退出" android:id ="@+id/exit" /> <item android:title ="操作1" /> <item android:title ="操作2" /> </menu > </item >
让其在模拟器中显示,编写java文件
1 2 3 4 5 6 @Override public boolean onCreateOptionsMenu (Menu menu) { getMenuInflater().inflate(R.menu.option, menu); return true ; }
首选项
在Android中,首选项(Preferences) 是一种用于存储和管理应用程序配置或用户设置的机制,通常以键值对形式保存
SharedPreferences
轻量级键值存储,数据以XML文件形式保存在/data/data/<package_name>/shared_prefs/目录。
1 2 3 4 5 6 7 8 SharedPreferences prefs = getSharedPreferences("MyPrefs" , MODE_PRIVATE);SharedPreferences.Editor editor = prefs.edit(); editor.putString("username" , "John" ); editor.apply(); String name = prefs.getString("username" , "default" );
JSON
在Android开发中,JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
实例
1 2 3 4 5 6 { "name" : "Android" , "version" : 13 , "features" : [ "Jetpack" , "Kotlin" ] , "isOpenSource" : true }
JSON处理库
原生org.json。特点:Android内置,无需额外依赖,但功能较基础。
1 2 3 4 5 6 7 8 String jsonStr = "{\"name\":\"Android\"}" ;JSONObject json = new JSONObject (jsonStr);String name = json.getString("name" ); JSONObject newJson = new JSONObject ();newJson.put("version" , 13 );
工具
launcher:启动器
gradle:编译代码、处理资源、管理第三方库依赖、生成APK/AAB文件,以及配置不同构建变体(如Debug/Release)。
ctrl+O:快速添加重写方法
课程设计-基于GIS的Android地图开发 序章 基于位置的服务所围绕的核心就是要先确定出用户所在的位置。通常有两种技术方式可以实现:一种是通过GPS定位,一种是通过网络定位。 GPS定位的工作原理是基于手机内置的GPS硬件直接和卫星交互来获取当前的经纬度信息,这种定位方式精确度非常高,但缺点是只能在室外使用,室内基本无法接收到卫星的信号。网络定位的工作原理是根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,再通过三角定位确定出一个大概的位置,这种定位方式精确度一般,但优点是在室内室外都可以使用。
Android 对这两种定位方式都提供了相应的API支持,但是由于一些特殊原因,Google的网络服务在中国不可访问,从而导致网络定位方式的API失效。而GPS定位虽然不需要网络,但是必须要在室外才可以使用,因此在室内开发的时候很有可能会遇到不管使用哪种定位方式都无法成功定位的情况。
基于以上原因,放弃Android原生定位 API的用法了,而是使用一些国内第三方公司的 SDK。
SHA1
SHA1(Secure Hash Algorithm 1)是一种加密哈希函数 ,可将任意长度的数据生成唯一的160位(20字节)固定长度哈希值,常用于数据完整性校验和数字签名,但因其安全性漏洞(如碰撞攻击)已被逐步淘汰。
android studio如何连接夜神模拟器
在夜神的bin目录下D:\yeshen\data\Nox\bin 打开cmd,执行命令nox_adb.exe connect 127.0.0.1:62001
在Android Studio中访问谷歌虚拟手机(Android Emulator)的内部资源
打开Android Studio的终端或命令提示符
使用以下adb命令:adb shell
进入shell后,使用Linux命令浏览文件系统
如果 adb root 不可用,可以尝试临时修改权限:
1 2 3 adb shell su chmod 777 /data/data/应用包名
注意事项 Google Play 镜像默认无 root:如果你使用的是 “Google Play” 镜像,可能无法获取 root 权限,建议改用 “Google APIs” 镜像。
论文伴侣——NoteExpress的使用说明 点击这里
Word中的交叉引用 点击这里
百度学术 点击这里
随感 I prefer to mark a number of sentiment that will show itself underneath.
lay hands on approaches to dispose of errors:
there are little information useful on the internet nowadays,either one copy another,or opppsitely.Repeat oneself agine and agine only find you are reaing the same article which cite the same photograph.It is hard to change this despairing situation.Because of that , my ambition is to manufacture high-class academic article for example:$1what the problem is? $2what caused this problem? $3how to slove this problem.while texting,lay picture aside of it ,only words seems stupid and reads hardly.Compared to official or authorized technology files,I squint towards texts made by personal blogs while occupy approachable statement.
课设设计思路 // 1. 创建定位客户端 mLocationClient = new LocationClient(getApplicationContext());
// 2. 创建并配置定位选项 LocationClientOption option = new LocationClientOption(); option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); // 高精度模式 option.setScanSpan(5000); // 5秒更新一次 option.setIsNeedAddress(true); // 需要地址信息
// 3. 将配置应用到客户端(关键步骤) mLocationClient.setLocOption(option);
// 4. 注册位置监听器(监听定位结果) mLocationClient.registerLocationListener(myListener);
// 5. 启动定位 mLocationClient.start();