先来一个HelloWorld.
XML布局文件
<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World" />
</LinearLayout>
![]()
事件响应代码为将英文修改成中文。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置屏幕组件需要用的布局
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv);
// 修改文本控件的文字
tv.setText("你好,世界");
}
}
最后在手机上显示的画面如下
![]()
创建第二个页面
在res/layout文件夹下面新建一个xml文件
![]()
在res/values的strings.xml文件中添加内容
<resources>
<string name="app_name">OCR</string>
<string name="text2">Activity Main2</string>
</resources>
activity_main2.xml的布局内容如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text2" />
</LinearLayout>
在清单文件AndroidManifest.xml文件中添加activity_main2的配置。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.guanjian.ocr">
<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/Theme.OCR">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity2" />
</application>
</manifest>
在Activity1中添加一个按钮来跳转到Activity2。
<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转" />
</LinearLayout>
![]()
修改MainActivity的Java代码
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置屏幕组件需要用的布局
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv);
// 修改文本控件的文字
tv.setText("你好,世界");
Button button = findViewById(R.id.button);
// 给button设定点击事件的侦听
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 创建一个意图对象
Intent intent = new Intent();
intent.setClass(MainActivity.this,MainActivity2.class);
// 实现跳转
startActivity(intent);
}
});
}
}
在Java主目录中创建MainActivity2的响应类。
public class MainActivity2 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
}
在手机上的运行效果如下
![]()
![]()
Activity生命周期
![]()
上图的说明可以见以下代码
public class MainActivity extends AppCompatActivity {
private static final String TAG = "ning";
/**
* 在页面载入的时候最先触发
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"Activity onCreate");
// 设置屏幕组件需要用的布局
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv);
// 修改文本控件的文字
tv.setText("你好,世界");
Button button = findViewById(R.id.button);
// 给button设定点击事件的侦听
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 创建一个意图对象
Intent intent = new Intent();
intent.setClass(MainActivity.this,MainActivity2.class);
// 实现跳转
startActivity(intent);
}
});
}
/**
* 在页面载入的时候第二个触发
*/
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"Activity onStart");
}
/**
* 在页面载入的时候第三个触发,结束时页面可见
*/
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"Activity onResume");
}
/**
* 在页面跳转离开的时候触发
*/
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"Activity onPause");
}
/**
* 在页面完全消失的时候触发
*/
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"Activity onStop");
}
/**
* 从其他页面返回该页面时首次执行
* 然后执行onStart和onResume
*/
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG,"Activity onRestart");
}
/**
* 从主界面返回安卓桌面的时候触发
*/
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"Activity onDestroy");
}
}
当我们打开安卓app的时候会显示日志
D/ning: Activity onCreate
D/ning: Activity onStart
D/ning: Activity onResume
当我们点击跳转按钮会显示日志
D/ning: Activity onPause
D/ning: Activity onStop
当我们从跳转页面返回主界面时会显示日志
D/ning: Activity onRestart
D/ning: Activity onStart
D/ning: Activity onResume
当我们从主界面返回安卓桌面时会显示日志
D/ning: Activity onPause
D/ning: Activity onStop
D/ning: Activity onDestroy
当我们点击了跳转按钮立刻返回主界面时会显示日志
D/ning: Activity onPause
D/ning: Activity onResume
这里有一个值得说明的地方,当App隐藏于后台的时候,我们启动了非常占用内存的App,比如游戏,此时安卓系统会将该进程杀死释放内存给游戏使用。当我们再次进入该App的时候会显示D/ning: Activity onCreate而不是D/ning: Activity onRestart。这几个触发动作的具体应用如下
- onCreate:创建活动,把页面布局加载进内存,进入初始状态。
- onStart:开始活动,把活动页面显示在屏幕上,进入了就绪状态。
- onResume:恢复活动,活动页面进入活跃状态,能够与用户正常交互,例如允许响应用户的点击动作;允许用户输入文字等等。
- onPause:暂停活动,页面进入暂停状态,无法与用户进行正常交互。
- onStop:停止活动,页面将不在屏幕上显示。
- onDestroy:销毁活动,回收活动占用的系统资源,把页面从内存中清除。
- onRestart:重启活动,重新加载内存中的页面数据。
- onNewIntent:重用已有的活动实例。
Intent
Intent是各个组件之间信息沟通的桥梁,它用于Android各组件之间的通信,主要完成下列工作:
- 标明本次通信请求从哪里来,到哪里去,要怎么走。
- 发起方携带本次通信需要的数据内容,接收方从收到的意图中解析数据。
- 发起方若想判断接收方的处理结果,意图就要负责接收方传回应答的数据内容。
- 显式Intent:直接指定来源活动与目标活动,属于精确匹配。它有3种构建方式
- 在Intent的构造函数中指定。
- 调用意图对象的setClass方法指定。
- 调用意图对象的setComponent方法指定。
@Override
public void onClick(View view) {
// 创建一个意图对象,第一种方式
// Intent intent = new Intent(MainActivity.this,MainActivity2.class);
// 第二种方式
Intent intent = new Intent();
intent.setClass(MainActivity.this,MainActivity2.class);
// 第三种方式
// Intent intent = new Intent();
// 它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
// ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
// intent.setComponent(component);
// 实现跳转
startActivity(intent);
}
- 隐式Intent:没有明确指定要跳转的目标活动,只给出一个动作字符串让系统自动匹配,属于模糊匹配。
现在我们在Activity2的页面中添加如下的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text2" />
<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="点击以下按钮将向号码12345发起请求" />
<Button
android:id="@+id/btn_dial"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="跳到拨号页面" />
</LinearLayout>
![]()
在Activity2的Java代码中添加
public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
findViewById(R.id.btn_dial).setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_dial:
Intent intent = new Intent();
// 隐式跳转到拨号界面
intent.setAction(Intent.ACTION_DIAL);
Uri uri = Uri.parse("tel:12345");
intent.setData(uri);
startActivity(intent);
break;
default:
break;
}
}
}
运行结果
![]()
![]()
我们在Activity的主界面的布局文件中添加如下代码
<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转" />
<TextView
android:id="@+id/tv_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="今天天气很晴朗" />
<Button
android:id="@+id/button_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送信息" />
</LinearLayout>
在Activity的Java代码中添加
public class MainActivity extends AppCompatActivity {
private static final String TAG = "ning";
private TextView tvSend;
/**
* 在页面载入的时候最先触发
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"Activity onCreate");
// 设置屏幕组件需要用的布局
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv);
// 修改文本控件的文字
tv.setText("你好,世界");
Button button = findViewById(R.id.button);
// 给button设定点击事件的侦听
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 创建一个意图对象,第一种方式
// Intent intent = new Intent(MainActivity.this,MainActivity2.class);
// 第二种方式
Intent intent = new Intent();
intent.setClass(MainActivity.this,MainActivity2.class);
// 第三种方式
// Intent intent = new Intent();
// 它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
// ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
// intent.setComponent(component);
// 实现跳转
startActivity(intent);
}
});
tvSend = findViewById(R.id.tv_send);
findViewById(R.id.button_send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,MainActivity2.class);
// 创建一个包裹对象
Bundle bundle = new Bundle();
bundle.putString("request_time", new Date().toString());
bundle.putString("request_context",tvSend.getText().toString());
intent.putExtras(bundle);
startActivity(intent);
}
});
}
/**
* 在页面载入的时候第二个触发
*/
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"Activity onStart");
}
/**
* 在页面载入的时候第三个触发,结束时页面可见
*/
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"Activity onResume");
}
/**
* 在页面跳转离开的时候触发
*/
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"Activity onPause");
}
/**
* 在页面完全消失的时候触发
*/
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"Activity onStop");
}
/**
* 从其他页面返回该页面时首次执行
* 然后执行onStart和onResume
*/
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG,"Activity onRestart");
}
/**
* 从主界面返回安卓桌面的时候触发
*/
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"Activity onDestroy");
}
}
在Activity2中获取意图中的数据放入tv2中。
public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {
private TextView tv2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
findViewById(R.id.btn_dial).setOnClickListener(this);
tv2 = findViewById(R.id.tv2);
// 从上一个页面的意图中获取包裹
Bundle bundle = getIntent().getExtras();
String requestTime = bundle.getString("request_time");
String requestContext = bundle.getString("request_context");
String desc = String.format("收到请求消息:\n请求时间:%s\n请求内容:%s",
requestTime,requestContext);
tv2.setText(desc);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_dial:
Intent intent = new Intent();
// 隐式跳转到拨号界面
intent.setAction(Intent.ACTION_DIAL);
Uri uri = Uri.parse("tel:12345");
intent.setData(uri);
startActivity(intent);
break;
default:
break;
}
}
}
运行结果
![]()
![]()
现在我们要回一个信息给到主界面,说今天天气很热。
在Activity2中增加一个按钮,布局文件如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text2" />
<TextView
android:id="@+id/tv_response"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="待返回的消息为:今天天气很热" />
<Button
android:id="@+id/btn_response"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送返回信息" />
<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="点击以下按钮将向号码12345发起请求" />
<Button
android:id="@+id/btn_dial"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="跳到拨号页面" />
</LinearLayout>
现在我们在Activity的主界面的Java代码中就不仅仅是普通的跳转到Activity2了,而是需要注册一个可以等待返回的ActivityResult。跳转的方式也不再是startActivity(intent);注意,以下代码都改成了Lambda表达式的形式,关于lambda表达式的内容可以参考Java函数式编程整理 。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "ning";
private TextView tvSend;
private ActivityResultLauncher<Intent> register;
/**
* 在页面载入的时候最先触发
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"Activity onCreate");
// 设置屏幕组件需要用的布局
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv);
// 修改文本控件的文字
tv.setText("你好,世界");
Button button = findViewById(R.id.button);
// 给button设定点击事件的侦听
button.setOnClickListener(view -> {
// 创建一个意图对象,第一种方式
// Intent intent = new Intent(MainActivity.this,MainActivity2.class);
// 第二种方式
Intent intent = new Intent();
intent.setClass(MainActivity.this,MainActivity2.class);
// 第三种方式
// Intent intent = new Intent();
// 它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
// ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
// intent.setComponent(component);
// 实现跳转
startActivity(intent);
});
tvSend = findViewById(R.id.tv_send);
// 注册ActivityResult,并通过回调函数获取返回的信息
register = registerForActivityResult(new StartActivityForResult(), result -> {
if (result != null) {
Intent intent = result.getData();
if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
Bundle bundle = intent.getExtras();
String responseTime = bundle.getString("response_time");
String responseContext = bundle.getString("response_context");
String desc = String.format("收到返回消息:\n返回时间:%s\n返回内容:%s",
responseTime,responseContext);
tv.setText(desc);
}
}
});
findViewById(R.id.button_send).setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this,MainActivity2.class);
// 创建一个包裹对象
Bundle bundle = new Bundle();
bundle.putString("request_time", new Date().toString());
bundle.putString("request_context",tvSend.getText().toString());
intent.putExtras(bundle);
register.launch(intent);
});
}
/**
* 在页面载入的时候第二个触发
*/
@Override
protected void onStart() {
super.onStart();
Log.d(TAG,"Activity onStart");
}
/**
* 在页面载入的时候第三个触发,结束时页面可见
*/
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"Activity onResume");
}
/**
* 在页面跳转离开的时候触发
*/
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"Activity onPause");
}
/**
* 在页面完全消失的时候触发
*/
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"Activity onStop");
}
/**
* 从其他页面返回该页面时首次执行
* 然后执行onStart和onResume
*/
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG,"Activity onRestart");
}
/**
* 从主界面返回安卓桌面的时候触发
*/
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"Activity onDestroy");
}
}
Activity2中的返回信息的代码如下
public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {
private TextView tv2;
private final String msg = "今天天气很热";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
findViewById(R.id.btn_dial).setOnClickListener(this);
tv2 = findViewById(R.id.tv2);
// 从上一个页面的意图中获取包裹
Bundle bundle = getIntent().getExtras();
String requestTime = bundle.getString("request_time");
String requestContext = bundle.getString("request_context");
String desc = String.format("收到请求消息:\n请求时间:%s\n请求内容:%s",
requestTime,requestContext);
tv2.setText(desc);
findViewById(R.id.btn_response).setOnClickListener(this);
}
@Override
public void onClick(View view) {
Intent intent = new Intent();
switch (view.getId()) {
case R.id.btn_dial:
// 隐式跳转到拨号界面
intent.setAction(Intent.ACTION_DIAL);
Uri uri = Uri.parse("tel:12345");
intent.setData(uri);
startActivity(intent);
break;
case R.id.btn_response:
Bundle bundle = new Bundle();
bundle.putString("response_time",new Date().toString());
bundle.putString("response_context",msg);
intent.putExtras(bundle);
setResult(Activity.RESULT_OK,intent);
// 页面返回跳转
finish();
break;
default:
break;
}
}
}
运行结果
![]()
![]()
运行时动态申请权限
![]()
安卓系统在6.0之前,只需要在清单文件中去配置权限就可以使用例如手机联系人、短信、相册等需要申请权限的应用。用户在安装的时候会进行提示。在安卓6.0之后不仅仅需要在清单文件中配置这些权限,而且会进行系统弹窗的询问,要使用这些权限需要在用户允许的情况下才可以使用。
Lazy模式即懒汉式模式,当我们需要用到某个权限功能时才去请求权限。除此之外还有一个Hungry模式,即饿汉式模式,当我们打开App的时候,不管你有没有使用到某个权限功能,它都会对用户进行请求,让用户去一次性通过。
我们在Activity的布局文件中添加两个按钮去读取通讯录和发送短信。
<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转" />
<TextView
android:id="@+id/tv_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="今天天气很晴朗" />
<Button
android:id="@+id/button_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送信息" />
<Button
android:id="@+id/btn_contact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="读写通讯录" />
<Button
android:id="@+id/btn_sms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送短信" />
</LinearLayout>