温故而知新
这一点主要是在比较不忙的时间,巩固一下android的基础知识。
第一章 开始启程
- 在res目录文件关于mipmap中的多种文件,例如hdpi,xhdpi,xxhdpi等等不同类型的文件名。
可以参考res中文件夹名的意义
摘取其中的一些内容:
QHD 960*540
720p 1280*720 标清
1080p 1920*1080 高清
"QHD hdpi "
"720P xhdpi"
"1080P xxhdpi "
- 在build.gradle 文件plugins的认识
plugins {
id 'com.android.application'
}
plugins {
id 'com.android.library'
}
两者对应的东西是不一样的:前者应用模块直接就可以运行,后者库模块需要依附应用模块。
3. android最大的闭包:以下我截取我之前项目中的一些配置代码并在上面做相应的解释
android {
compileSdkVersion 30 // 编译版本 表示SDK的
buildToolsVersion "30.0.2" // 构建工具的版本
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
packagingOptions {
pickFirst 'META-INF/*'
}
defaultConfig {//默认配置
applicationId "com.example.zegoimmultitest" // 包名,这是唯一的,前面构建项目的时候写了,修改的地方就是这里
minSdkVersion 21 //最低兼容的版本
targetSdkVersion 30 // 充分测试过的版本
versionCode 1 // 该项目的版本号
versionName "1.0" // 项目的版本名,这两个version在后面的生成安装文件中起到至关重要的
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {//正式安装的版本,其中还有一个debug的,但是这个一般可忽略
minifyEnabled false //代码混淆开启
//混淆的文件规则
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
下面是dependencies
闭包,主要是对项目的第三方导入的依赖,例如本地依赖以及库依赖,远程依赖:
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0' // 远程依赖
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.3.0'
implementation project(path:':Scenes')。//库依赖
implementation project(path: ':CommonFeatures')
implementation project(path: ':AdvancedVideoProcessing')
implementation project(path:':AdvancedStreaming')
implementation project(path: ':Quickstart')
implementation project(path: ':Others')
implementation project(path: ':DebugAndConfig')
implementation project(path: ':AdvancedAudioProcessing')
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.android.volley:volley:1.1.1'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.tencent.bugly:crashreport:3.2.422'
implementation 'com.tencent.bugly:nativecrashreport:3.7.500'
implementation files('libs/ZegoExpressEngine.jar') // 本地依赖
}
第二章 探究活动
- 学习使用menu菜单;首先在res中构建一个menu然后在activity进行展示
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
选择好菜单之后,相应地需要对这些item进行监听实现相应的功能。这里就得使用到如下的代码进行设计:
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.add_item:
Toast.makeText(this,"add",Toast.LENGTH_SHORT).show();
return true;
case R.id.remove_item:
Toast.makeText(this,"remove",Toast.LENGTH_SHORT).show();
return true;
default:
return true;
}
- 销毁一个活动
finish()
方法即可实现。 - intent的使用
这里主要是两个方向,第一是显式;第二是隐式;
显式:
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
ComponentName componentName = new ComponentName(this, SecondActivity.class);
// 或 ComponentName componentName = new ComponentName(this, "com.example.app.SecondActivity");
// 或 ComponentName componentName = new ComponentName(this.getPackageName(), "com.example.app.SecondActivity");
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
// 或 intent.setClassName(this, "com.example.app.SecondActivity");
// 或 intent.setClassName(this.getPackageName(),"com.example.app.SecondActivity");
startActivity(intent);
隐式比较含蓄:主要是使用如下的:
<activity
android:name="com.example.app.SecondActivity">
<intent-filter>
<action android:name="mark"/>
<category android:name="android.intent.category.DEFAULT"/> //默认的一个category
</intent-filter>
</activity>
其中的filter过滤器的作用比较重要点。
一个activity可以有多个category。
这部分,有些文章写的很好,可以参考!
其中有内置的一些可以调用的其他程序的活动,例如网页解析
// 打开百度主页
Uri uri = Uri.parse("https://www.baidu.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
这样就直接跳转过去了。当然是在os中的浏览器中进行展现的。
Data:表示与动作要操纵的数据
一个URI对象是一个引用的data的表现形式,或是data的MIME类型;data的类型由Intent的action决定
- intent 的数据传递
这里使用的intent.putExtra()
方法可以将活动与活动之间的数据进行传递。而需要接受的数据的intent可以采用getXX()
方法进行获取,其中的XX
指的是特定的数据类型,数值的传递是通过kv对进行的匹配的。
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
//数据通过putExtra()以键值对形式传递数据 use putExtra() by key-value transmit values
intent.putExtra("hello",str);
startActivity(intent);
那么返回数据给上一个活动应该采用什么的方法呢?
Intent intent = new Intent(ActivityA.this, ActivityB.class);
//第一个参数是intent,第二个参数是请求码,随便定义,保证是唯一的就行
startActivityForResult(intent, 1);
//重点是在本活动activity中override如下的方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//这里要通过请求码来判断数据的来源
switch (requestCode) {
case 1:
//判断请求的结果是否成功,resultCode == RESULT_OK,代表成功了
if (resultCode == RESULT_OK) {
String getData = data.getStringExtra("data");
Toast.makeText(ActivityA.this, getData, Toast.LENGTH_LONG).show();
}
break;
/**这里可以有多个requestcode**/
default:
}
}
负责回传数据的活动需要setResult
进行返回
Intent intent = new Intent();
//将要传给activityA的数据存放在intent中
intent.putExtra("data", "你好,time");
//第一个参数用于向上一个活动返回结果,一般有RESULT_OK和RESULT_CANCEL两个值,第二个参数把带有数据的intent传回去
setResult(RESULT_OK, intent);
小强调一下activity中存在onBackPresses()
方法,该方法是用户点击返回键(os自带的)会触发的方法。这个回传数据的实现也可以在这个方法中进行实现。
返回栈——activity的关键之一
任务和返回栈是activity 中重要的概念,在这一部分可以参考谷歌开发者的介绍
任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。
您可以通过启动模式定义 Activity 的新实例如何与当前任务关联。您可以通过两种方式定义不同的启动模式:
- 使用清单文件
当您在清单文件中声明 Activity 时,您可以指定该 Activity 在启动时如何与任务关联。这个是我个人比较常用的一个方法。
在清单文件即manifest中可以将进行如下操作:
android:launchMode="singleInstance"
android:launchMode="singleTask"
android:launchMode="singleTop"
android:launchMode="standard"
//一个activity只能对应这四个指定mode的一个
这四个模式的区别可以参考[开发者文档](https://developer.android.google.cn/guide/components/activities/tasks-and-back-stack#ManagingTasks )进行学习。
standard:默认值,在新的activity上再构建一个new的activity
singleTop:看中文大意即单例置顶;可以这样理解——例如,
假设任务的返回堆栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈为 A-B-C-D;D 位于顶部)。收到以 D 类型 Activity 为目标的 intent。如果 D 采用默认的 “standard” 启动模式,则会启动该类的新实例,并且堆栈将变为 A-B-C-D-D。但是,如果 D 的启动模式为 “singleTop”,则 D 的现有实例会通过 onNewIntent() 接收 intent,因为它位于堆栈顶部,堆栈仍为 A-B-C-D。但是,如果收到以 B 类型 Activity 为目标的 intent,则会在堆栈中添加 B 的新实例,即使其启动模式为 “singleTop” 也是如此。
singleTask:这一部分在官方文档上看的模棱两可,迷迷糊糊的。所以比较推荐其他的文章参考;主要的业务意思是:标记为singleTask的Activity,在启动的时候如果当前没有实例,就和standard模式一样,直接压栈到当前activity上;如果已经有实例了,就把该目标Activity实例上面的activity都清除,从而将目标Activity暴露到最上面,调用了其onNewIntent方法。
singleInstance:与 “singleTask” 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。
- 使用 Intent 标记
这一部分的内容和知识点可以参考文章。这里主要是通过实际的业务逻辑对于intent下的activity的启动模式进行设计。
当您调用 startActivity() 时,可以在 Intent 中添加一个标记,用于声明新 Activity 如何(或是否)与当前任务相关联。
例如:
Intent it = new Intent(SecondActivity.this, SecondActivity.class);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(it);
activity的生命周期
这一个知识点基本上只要面试android的工作岗位基本上都会过一遍的。可以说是安卓开发的“九九乘法表”!这个知识点郭大神已经讲的比较清晰了,当然,这种东西自己再去官网专门针对activity的生命周期的文章读一遍更佳。
- 生命周期详见此图。
- 阅读官方文档的时候,关于onPause的部分我觉得讲得很不错,故特将其copy过来。
Activity 进入此状态的原因有很多。例如:
如 onResume() 部分所述,某个事件会中断应用执行。这是最常见的情况。在 Android 7.0(API 级别 24)或更高版本中,有多个应用在多窗口模式下运行。无论何时,都只有一个应用(窗口)可以拥有焦点,因此系统会暂停所有其他应用。有新的半透明 Activity(例如对话框)处于开启状态。只要 Activity 仍然部分可见但并未处于焦点之中,它便会一直暂停。
同时,在这个activity的生命周期的时刻,这里是不利于储存数据的时间节点,因为时间不一定足够。
onPause() 执行非常简单,而且不一定要有足够的时间来执行保存操作。因此,您不应使用 onPause() 来保存应用或用户数据、进行网络调用或执行数据库事务。
巧用方法,提高认识和开发activity的能力
- 确定activity的位置
在开发过程中因为activity的类型多,且不知道运行的是哪一个activity,虽然可以进行debug进行判断。但是这里提供一个新的思路,使用父类的思想。
例如:构建一个myActivity类继承父类在onCreate中log日志打印当前的类名。然后所有的activity都可以继承这个类,这个也不影响整体的代码逻辑。
public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
//我们在onCreate()方法中获取了当前实例的类名,并通过Log 打印了出来。
}
- 直接退出整个程序
首先构造一个工具类
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity){
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishyAll(){
for(Activity activity:activities){ // 遍历
if(!activity.isFinishing()){
activity.finish();
}
}
activities.clear();
}
}
在上面所示的BaseActivity中进行改写,每一个活动都继承这个BaseActivity对象即可。在通过一些业务逻辑或者组件触发这个finishAll
方法即可
public class BaseActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
- 最佳启动activity的方式
主要是通过静态方法进行活动的启动
public static void actionStart(Context context, String account, String password){
Intent intent = new Intent(context,FirstActivity.class);
intent.putExtra("account",account);
intent.putExtra("password",password);
context.startActivity(intent);
}
以上的参数可以自己进行修改,主要的context这个对象是不能缺少的!接着在相关的地方进行调用这个静态方法即可!
FirstActivity.actionStart(MainActivity.this,account,password);