本文为《第一行代码——安卓》.pdf学习笔记,内容有所参照!!!
2019.08.15 周四 p26~p39
第一章
1.项目结构详解:
分为Android 和 project模式,具体再看…
2.两个gradle.property结构解析:
1)外层的gradle.property解析
2)app中的gradle.properties
3.日志打印
1)五个级别:
(ps:第二级别的简单打印MainActivity类中的**”某个“**内容
Log.d(“MainActivity”,“onCreate execute”);
2)android.util.Log与日常使用的system.out.println()区别:
1.Android studio不支持syso的快捷键
2.日志打印不可控
3.打印时间无法确定
4.不能添加过滤器
5.打印日志级别无法区分
(ps:在onCreate()方法外面,输入logt,回车,系统自动生成以当前的类名做为字符串的TAG常量
private static final String TAG = "MainActivity";)
3)级别控制
可详细探索,以便于排错。
2019.08.16 周五 p40~p50
第二章——活动探究
1.活动的定义及基本用法
2.手动创建活动
过程:
1)新建一个empty项目(我取名为ActivityTest)
2)改成project模式,在app/src/main/java/com.example.activitytest下右击新建一个empty activity(我取名为FirstActivity),
不要勾选Generate Layout File(避免AS自动生成一个对应的布局文件) 和Launcher Activity(避免AS将当前活动设置为项目的主活动)
3)创建布局文件
app/src/main/res下新建一个布局文件夹layout,在这个文件夹下new一个布局文件(我命名为first_layout);
加一个按钮作为测试(自己探索)
4)在活动中加载这个first_layout布局,在onCreate()中加入如下代码:setContentView(R.layout.first_layout);
5)在AndroidManifest文件中注册
主要目的就是为该项目配置主活动。
这是我的AndroidManifest的application标签内的内容
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".FirstActivity"
android:label="@string/first_lablename">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
自行了解标签和标签内的内容
3.在活动中使用Toast(Android中一种非常好的提醒方式)
1)通过findViewById()方法获取到布局文件中定义的元素组件,findViewById()方法返回的是一个View对象,所以一般需要**(向下)转型**成具体的组件对象。
2)为获取到的组建添加监听设置…
View.OnClickListener first_onclick = new View.OnClickListener() {
public void onClick(View view) {
Toast.makeText(FirstActivity.this,“Toast嘻嘻,被点击到了”,Toast.LENGTH_LONG).show();
}
};
protected void initView()
{
first_bttest = (Button)findViewById(R.id.first_testbut);
first_bttest.setOnClickListener(first_onclick);
}
3)在onCreate方法中调用initVIew()方法就OK了。
4.销毁活动
1)方法一:按下back键(可以理解为退出程序)
2)activity类提供了一个finish()方法
2019.08.17 周六 p51~p72
第二章——活动探究
1.使用Intent在活动间进行穿梭
1)简介:
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可用于启动服务、启动活动、发送广播等场景。
2)大致分类:显式Intent和隐式Intent
(i)显式intent用法演示:
public void onClick(View view) {
//显式的intent
Intent intent1 = new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent1);
}
先创建一个intent对象,参数有两个,FirstActivity.this作为上文,传入SecondActivity.class作为活动目标。
再在**startActivity()**方法里执行这个intent1
(ii)隐式intent用法演示:
隐式Intent含蓄了很多,它并不明确指出我们想要启动哪一个活动,而是指定一系列更为抽象的action和category,然后由系统去分析这个Intent,并帮我们找出合适的活动去启动。
分以下两个步骤:
1.修改AndroidManifest里的
<activity android:name=".SecondActivity"></activity>
改成如下形式:
(这不是绝对的)
2.修改onClick()函数里的内容:
如改成:
public void onClick(View view) {
//隐式的intent
Intent intent1 = new Intent("com.example.activitytest.ACTION_START");
intent1.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent1);
}
这里使用了Intent的一个构造函数,将action传进去了,表明我们先要启动能够响应com.example.activitytest.ACTION_START这个action的活动。(这里没有指定category,因为android.intent.category.DEFAULT是一种默认的category,在调用startActivity()方法时,会自动将category传到Intent中)
但也可以通过addCategory()这个方法来设置category信息,如我在onClick()函数里还加了intent1.addCategory(“com.example.activitytest.MY_CATEGORY”);这样一个语句,用于指定一个category。
如果直接运行,那么再点击按钮时,程序就会崩溃,原因如图:
信息提示我们没有一个活动可以响应我们的Intent,这是因为intent1.addCategory(“com.example.activitytest.MY_CATEGORY”);这个语句,设指定了category,所以我们也需要修改AndroidManifest的Second_layout的配置信息,改成如下形式:
加上后,运行OK,gift演示下:
3)更多Intent隐式用法:
使用隐式Intent,不仅可以启动自己程序内的活动,,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享成了可能。
演示下在该程序中打开网页:、
//跳转到网页的按钮设置
first_webbtn = (Button)findViewById(R.id.first_webbt);
first_webbtn.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
Intent intent_temp = new Intent(Intent.ACTION_VIEW);
intent_temp.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent_temp);
}
});
运行效果:
这里指定Inent的action是Intent.ACTION_VIEW,这是Android系统内置的动作,其常量值为android.intent.action.VIEW
可用于打开打电话的应用:
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(“tel:” + 545645));//指定一个号码
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
4)向下一个活动传递数据
Intent中提供了一系列putExtra()方法的重载,可以把我们想要传递的数据暂时存在Intent中,启动另一个活动后,就可以把数据从Intent中取出来。
**实例:**将FirstActivity中的字符串传到SecondActivity活动中
(i)对于FirstActivity
public void onClick(View view) {
//显式的intent
Intent intent1 = new Intent(FirstActivity.this,SecondActivity.class);
String data1 = "就是一条普通的传递过来的数据";
//将数据传入到下一个活动
intent1.putExtra("extra_data",data1);
startActivity(intent1);
}
}
(ii)对于SecondActivity
//(就认为是监听器吧)
View.OnClickListener onclick1 = new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent1 = new Intent();
intent1.putExtra("extra_return","活动结束,返回一个值");
setResult(RESULT_OK,intent1);
finish();
}
//为按钮添加监听器
public void init()
{
second_bt1 = (Button)findViewById(R.id.second_but1);
second_bt1.setOnClickListener(onclick1);
}
//个人理解下面这个是主函数
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
Intent second_intent1 = getIntent();
String data1 = second_intent1.getStringExtra("extra_data");
Log.d("SecondActivity",data1);//在日志中打印出传过来的字符串
init();
}
5)返回数据给上一个活动
(i)需要用到的thing:activity中的startActivityForResult()方法
这个方法期望在活动销毁时能够返回一个结果给上一个活动,这个方法需要两个参数,一个是Intent,另一个是请求码(requestCode),用于在之后的活动中判断数据的来源(还是很好理解的吧,即判断收到的数据来自哪个活动)
**(ii)A example:**将SecondActivity中的字符串传到FirstActivity活动中(在日志中打印显示)
对于SecondActivity活动
View.OnClickListener onclick1 = new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent1 = new Intent();
intent1.putExtra("extra_return","活动结束,返回一个值");
setResult(RESULT_OK,intent1);
finish();
}
};
上面setResult()方法接受了两个参数,第一个用于向上一个活动返回处理结果,一般只使用RESULT_OK,RESULT_CANCELED这两个值,第二个参数把带有数据的Intent传回去
对于FirstActivity活动
public void onClick(View view) {
Intent intent1 = new Intent(FirstActivity.this,SecondActivity.class);
//将数据传入到下一个活动
intent1.putExtra("extra_data",data1);
//这个0就是请求码(requestCode),下面的onActivityResult()就使用到了
startActivityForResult(intent1,0);
}
//重写onActivityResult方法,用于接收、处理数据
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
switch (requestCode)
{
case 0:
if(resultCode==RESULT_OK)
{
String return_data1 = data.getStringExtra("extra_return");
Log.d("FirstActivity",return_data1);
}
break;
default:
}
}
大功告成!!!
(iii)按下返回键,也可以返回数据
在SecondActivity活动中,重写onBackPressed()方法,使得按下返回键,也可以执行onClick()中的方法(其实就是将onClick()中的代码放到onBackPressed()方法里,尬笑.jpg 不然利用模块化思想,将需要复用的代码块写成一个函数)
public void onBackPressed() {
Intent intent1 = new Intent();
intent1.putExtra("extra_return","活动结束,返回一个值");
setResult(RESULT_OK,intent1);
finish();
}
6)活动的生命周期
(i)返回栈
Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈,也被称为返回栈(Back Stack)。想象一下栈后进先出的特性,再加上下面这幅图,就不难理解了。
(ii)活动状态
每个活动在其生命周期中最多可能会有4种状态。
1运行状态
当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
2暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域,你很快就会在后面看到这种活动。处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。
3停止状态
这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
4销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。
(iii)活动的生存期
Activity类中定义了7个回调方法,覆盖了活动生命周期的每-一个环节, 下面就来- -- 介绍这7个方法。
口onCreate()。这个方法你已经看到过很多次了,每个活动中我们都重写了这个方法,它
会在活动第--次被创建的时候调用。你应该在这个方法中完成活动的初始化操作,比如说加载布局、绑定事件等。
口onStart()。这个方法在活动由不可见变为可见的时候调用。
口onResume()。这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于
返回栈的栈顶,并且处于运行状态。
口onPause()。这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
口onStop()。这个方法在活动完全不可见的时候调用。它和onPause( )方法的主要区别在
于,如果启动的新活动是一个对话框式的活动,那么onPause( )方法会得到执行,而,onStop()方法并不会执行。
onDestroy()。 这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
onRestart()。 这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
以上7个方法中除了onRestart()方法, 其他都是两两相对的,从而又可以将活动分为3种生存期。
完整生存期。活动在onCreate()方法和onDestroy()方法之间所经历的,就是完整生存期。一般情况下,一个活动会在onCreate()方法中完成各种初始化操作,而在onDestroy()方法中完成释放内存的操作。
可见生存期。活动在onStart()方法和onStop()方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法,合理地管理那些对用户可见的资源。比如在onStart()方法中对资源进行加载,而在onStop( )方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
前台生存期。活动在onResume( )方法和onPause()方法之间所经历的就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的,我们平时看到和接触最多的也就是这个状态下的活动。
为了帮助你能够更好地理解,Android 官方提供了一张活动生命周期的示意图
(iv)体验一下活动的生命周期
创建了一个项目来详细的体验一下活动的生命周期:
创建一个empty project(我命名为project_testlife),系统会自动帮我们创建布局,如图红色画线(activity_main.xml)和活动(如图红色画线MainActivity),为了测试我们还需创建如图绿色对勾标记的布局和活动。
代码如下:
1)MainActivity
package com.example.project_testlife;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startNormalActivity = (Button) findViewById(R.id.but1);
Button startDialogActivity = (Button) findViewById(R.id.but2);
startNormalActivity.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent_temp = new Intent(MainActivity.this,NormalAcitivity.class);
startActivity(intent_temp);
}
});
startDialogActivity.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent_temp = new Intent(MainActivity.this,DialogAcitivity.class);
startActivity(intent_temp);
}
});
}
//几个......
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG,"onRestart");
}
}
2)activity_main.xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/but1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="jump to normalactivity"/>
<Button
android:id="@+id/but2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="jump to dialogactivity"/>
</LinearLayout>
- 4)NormalAcitivity和DialogAcitivity活动创建就好,不需要写什么代码
5)activity_normal_acitivity.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/nor_textView2"
android:layout_width="match_parent"
android:layout_height="61dp"
android:text="this is normal_activity" />
</LinearLayout>
6)activity_diaog_acitivity.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/dia_textView2"
android:layout_width="match_parent"
android:layout_height="61dp"
android:text="this is dialog_activity" />
</LinearLayout>
7)NormalAcitivity和DialogAcitivity活动对应的布局没有什么不同,NormalAcitivity是一个普通活动;把DialogAcitivity设置成对话框式的活动,这需要在AndroidManifest中完成。
重点关注下面这段语句:
<activity android:name=".DialogAcitivity"
android:theme="@style/Theme.AppCompat.Dialog"></activity>
完整的AndroidManifest代码。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.project_testlife">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".DialogAcitivity"
android:theme="@style/Theme.AppCompat.Dialog"></activity>
<activity android:name=".NormalAcitivity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
至此,该测试项目就完成了,让我们通过日志来观察下活动的生命历程吧!
1.运行项目,执行onCreate(),onStart(),on Resume()方法
2.点击第一个按钮,启动NormalAcitivity活动,由于NormalAcitivity把MainActivity完全遮住,所以MainActivity执行了onPause() , onStop()两个方法
3.按下back键返回MainActivity,因为之前MainActivity进入了停止状态,所以执行了onRestart(), onStart(), onResume()三个方法。
4.点击第二个按钮,启动DialogAcitivity,因为DialogAcitivity没有完全遮住MainActivity,所以MainActivity只是进入暂停状态,并没有停止,所以只执行onPause()
5.按下back键返回MainActivity,因为之前MainActivity进入了暂停状态,所以执行了onResume()方法。
6.最后按back退出程序,依次执行onPause(), onStart(), onDestroy(), 最终销毁程序(未配图)
2019.08.18周日 p72~p85
第二章
1.活动被回收后的恢复工作
1)onSaveInstanceState()方法
2.活动的启动模式(重要)
(i)分类:
启动模式一共有四种,分别是:standard,singleTop,singleTask,singleInstace
(ii)详解
1)standard是活动的默认启动模式,看图理解
2)singleTop
可能在有些情况下,你会觉得standard模式不太合理。活动明明已经在栈顶了,为什么再次启动的时候还要创建一个新的活动实例呢?别着急,这只是系统默认的一种启动模式而已,你完全可以根据自己的需要进行修改,比如说使用singleTop 模式。
当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
在AndroidManifest中标签里进行修改
<activity
android:name=".FirstActivity"
android:label="@string/first_lablename"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
3)singleTask
使用singleTop模式可以很好地解决重复创建栈顶活动的问题,但是正如你在上一节所看到的,如果该活动并没有处于栈顶的位置,还是可能会创建多个活动实例的。那么有没有什么办法可以让某个活动在整个应用程序的上下文中只存在一个实例呢?这就要借助singleTask模式来实现了。当活动的启动模式指定为singleTask, 每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
设置方法同singleTop一样。
4)singleInstance
它式四个启动模式中最特殊也是最复杂的了!
指定为singleInstance的活动会启动一个新的返回栈来管理这个活动。测试方法为:在三个活动中,将其中一个活动设为singleInstance,并在活动的onCreate()方法里设置相应的工作栈日志打印,来查看当前的活动所使用的栈。
(我讲的贼简单,尬笑.jpg…)
3.活动的最佳实践
(i)知晓当前是在哪一个活动
这个在阅读别人的代码时经常需要用到
在com.example.activitytest下创建一个类继承AppCompatActivity类,重写onCreate()f方法,然后让所有的活动都继承你刚刚写的这个类。(这个类的作用就是:在运行某个活动时,能够打印出当前活动的名字)
如下:
package com.example.activitytest;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//重点就是这个日志打印信息
Log.d("BaseActivity",getClass().getSimpleName());
}
}
(ii)随时随地退出程序
写一个专门的集合类对所有的活动进行管理(我创建的是ActivityCollector),然后在上一部分提到的BaseActivity中调用ActivityCollector的方法,对活动进行管理。
对于ActivityCollector
package com.example.activitytest;
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
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 finishAll()
{
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}
对于BaseActivity
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
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);
}
}
然后就可以设置按钮…等其他东西,调用ActivityCollector的finishAll()方法,退出程序。
当然你还可以在销毁所有活动的代码后面再加上杀掉当前进程的代码,以保证程序完全退出,杀掉进程的代码如下所示:
android.os. Process. killProcess (android. os . Process . myPid());
其中,killProcess()方法用于杀掉一个进程,它接收一个进程id参数,我们可以通过myPid()方法来获得当前程序的进程id。需要注意的是,killProcess()方法只能用于杀掉当前程序的进程,我们不能使用这个方法去杀掉其他程序。
(iii)启动活动的最佳写法
利用模块化思想