Android开发笔记 |期末考试复习——【兰红 微课视频版第2版】

Android笔记总结了教材1到9章节的知识,可用于Android的学习或者期末复习,笔记字体有点小,建议放大阅读,欢迎读者提出建议或批评指正!!

1. Android入门

1.1 【开发基本介绍】

Android是一种基于Linux内核的自由及开放源代码的移动操作系统,由Google公司和开放手机联盟领导及开发。它最初由安迪·鲁宾开发,并于2005年被Google收购注资。Android操作系统最初主要支持手机,随后逐渐扩展到平板电脑、电视、数码相机、游戏机、智能手表等其他领域。

总的来说,Android系统以其开放性丰富的应用生态强大的多任务处理能力以及灵活的开发工具等特点,赢得了全球用户的广泛认可和喜爱,成为了移动设备领域的主流操作系统之一。

1.2 【Android系统架构】

  1. 应用程序层:这一层是用户直接交互的界面,包含了各种核心应用程序,如联系人、短信、浏览器等。这些应用程序可以是系统自带的,也可以是从Google Play等应用商店下载的。

  2. 应用程序框架层:这一层为应用程序的开发提供了大量的API(应用程序接口),使得开发者能够高效地构建各种应用。

  3. 核心类库:这一层包含了一些C/C++库,为Android系统提供核心功能支持,同时包含了Android运行环境(ART),负责Android应用程序的执行。

  4. Linux内核:Android系统基于Linux内核,这一层为Android设备提供了底层的硬件驱动和系统服务。

1.3 【Android项目结构】

  • src:存放项目用到的各种Activity,可以有多个不同的包
  • res:存放Android项目的各种资源文件,如布局layout文件,value目录下的文件
  • libs:存放Android项目开发的第三方JAR包
  • build:Android Studio自动生成的各种资源文件,R.java文件也放在该目录下

1.4 【AndroidMainfest.xml清单文件详解】

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!--    android:icon="@mipmap/ic_launcher" 应用程序的图标-->
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.RadionButtonAndCheckBox"
        tools:targetApi="31">
        <!-- 自定义的Activity-->
        <activity
            android:name=".SecondActivity"
            android:exported="false" />
        <!--应用程序的Activity-->
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
            <!--指定该Activity为程序的入口-->
                <action android:name="android.intent.action.MAIN" />
            <!--指定启动应用时运行该Activity-->
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

1.5【安卓四大组件】

  • Activity(活动)

  • Service(服务)

  • BroadcastReceiver(广播接收器)

  • ContentProvider(内容提供者)

2. Android应用界面

2.1【View概述】

  1. Android应用界面由View和ViewGroup对象构建,ViewGroup是View的子类,是View的扩展,可以容纳多个View
  2. View是AndroidUI的基本构建块所有UI组件都是View的子类,它包含的XML属性和方法是所有组件都可以使用的
  3. ViewGroup是View的扩展可以包含多个View,作为容器使用

在这里插入图片描述

2.2【布局管理器】

基本的宽高单位

px: 像素单位,同样100px,不同分辨率的手机显示的控件大小不一样;

pt: 磅数单位,一般作为字体单位使用,与 px相似,不同分辨率的手机显 示的字体大小不一样;

dp: 密度无关像素单位,无关屏幕分辨率,都能显示相同的大小,一般作 为宽高单位;

sp: 推荐的字体单位,代表可伸缩像素,与dp一样的设计理念,设置字体 大小使用

布局类型

  1. 线性布局:线性布局允许其子元素按照水平或垂直方向排列。

  2. 相对布局:相对布局允许子元素相对于彼此或父元素进行定位

  3. 表格布局:表格布局将子元素按照行列的方式排列,类似于HTML中的表格。

  4. 网格布局:网格布局将界面划分为行和列的网格,允许子元素在网格中的特定位置进行定位。

  5. 扁平化布局:Android的扁平化布局是近年来在Android应用界面设计中越来越受欢迎的一种风格。扁平化布局强调简洁、直观和去除复杂的视觉效果,使得界面更加清晰、现代和易于理解。

  6. 绝对布局:绝对布局允许开发者通过指定子元素的精确坐标(X,Y)来进行布局。这种方式提供了最大的灵活性,但也带来了最大的复杂性。

  7. 帧布局:帧布局是最简单的布局方式,它将所有的子元素放置在屏幕的左上角,并且后面的元素会覆盖前面的元素。

2.3【常用的Android UI控件】

TextView

  1. 直接继承了View,还是EditText和Button组件的父类

  2. 作用:在界面上显示文字

    gravity:字体的位置

    textColor:字体颜色

    textSize:字体大小

    <TextView
        android:id="@+id/textview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="这是一个TextView组件"
        android:textColor="@color/red"
        android:textSize="16sp" />
    

EditText

  1. 作用:接收用户输入

  2. hint:提示用户输入的内容

  3. inputType:设置输入的类型

    <EditText
        android:id="@+id/EditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:hint="请输入密码"
        android:inputType="number"
        android:textSize="16sp"
        android:textColor="@color/red"/>
    

Button

  1. 继承了TextView组件

  2. 作用:单击按钮,添加onClick事件

  3. background:设置按钮背景色

  4. textColor:文本颜色

      <Button
         android:id="@+id/btnGo"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="第一个aty,现在实现跳转"
         android:textSize="18sp"
         android:textColor="@color/blue"
         android:background="@color/red"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
    

ImageView

  1. 图像组件继承View组件,它还派生了ImageButton、ZoomButton等组件

  2. 作用:显示图片,ImageView和ImageButton都能添加单击事件

    <ImageView
            android:id="@+id/imageview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher"
            tools:layout_editor_absoluteX="33dp"
            tools:layout_editor_absoluteY="96dp" />
    <ImageButton
            android:id="@+id/iamgebtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher"
    

RadioButton

  1. 与HTML的单选按钮一样,搭配RadioGroup使用

  2. 代码:checked属性是否选中

      <RadioGroup
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <RadioButton
                android:id="@+id/radiobtn1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="true"
                android:text=""/>
        </RadioGroup>
    

CheckBox

  1. 与HTML的复选框一样

  2. 代码:

    <CheckBox
            android:id="@+id/checkbox1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:text="读书"/>
        <CheckBox
            android:id="@+id/checkbox2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:text="看电影"/>
    

ProgressBar

  1. 即进度条组件,派生了两个常用的组件:seekBarRatingBar

  2. 常用属性:

    android:max:设置进度条的最大值

    android:progress:设置进度条已完成值

    style:设置ProgressBar的风格

    <!--水平进度条-->
        <ProgressBar
     style="@style/Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="50" />
        <!--环形进度条-->
        <ProgressBar
            style="@style/TextAppearance.AppCompat.Inverse"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="70" />
    

SeekBar

  • 它继承了ProgressBar,因此ProgressBar的xml属性和方法也适用于SeekBar

    <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/gray"/>
    

2.4【AdapterView及其子类】

AdapterView具有以下特征:

  • AdapterView继承ViewGroup,本身是一个抽象基类,它的本质是容器
  • AdapterView包括多个“列表项”,以合适的方法显示出来
  • 它的列表项由Adapter提供,调用AdapterView的setAdapter(Adapter)方法设置Adapter即可
  • 它的子类ListView(列表视图)、Spinner(下拉列表)、GridView

2.4.1【ListView列表视图】

  1. ListView是一个垂直方向显示列表项的列表视图组件,用于生成列表视图,ListActivity直接继承自Activity,只包含一个组件即ListView

  2. 生成列表视图有两种方式:

    直接使用ListView

    写一个类继承ListActivity的Activity,再使用ListView

    ListView的细节:

    • ListView可以添加事件处理,可以应用于导航到新的Activity
    • ListView底层已经使用了setAdapter(…) 只是代码不用写了
    • 数据超出屏幕范围,ListView自动具有滚动的特性
ListView常用的xml属性
xml属性说明
android:divide设置分割条样式
android:entries指定一个数组资源
android:dividerHeight设置分割条高度
android:scrollbars=“vertical”|“horizontal”数据超出范围时设置滚动条
  • 代码

    <?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">
    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:divider="@color/blue"
        android:entries="@array/teacher_name"
        android:dividerHeight="1dp"/>
    </LinearLayout>
    
    strings.xml
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string-array name="teacher_name">
            <item>张三</item>
            <item>李四</item>
            <item>王五</item>
            <item>赵六</item>
        </string-array>
    </resources>
    

2.4.2【Spinner下拉列表】

  • 显示一个下拉的菜单,然后选择一个

  • 主要的xml属性:

    entries:指定遍历某个数组资源

    spinnerMode:dialog (页面中弹出) | dropdown(下拉列表的下方弹出)

  • 代码:

     <Spinner
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:entries="@array/teacher_name"
            android:spinnerMode="dropdown"/>
    

    Spinner还可以使用Adapter为其添加数据:

       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test_spinner);
            String[] spinnerArray = {"选项1", "选项2", "选项3"};
            Spinner spinner = findViewById(R.id.spinner);
            ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1, spinnerArray);
            spinner.setAdapter(adapter);
            
        }
    

2.4.3【Adapter适配器接口】

  1. Adapter接口派生了ListAdapter、SpinnerAdaper两个子接口

  2. 常用的适配器实现类:

    ArrayAdapter:功能简单,只能展示一行文字,可以设置下拉框

    SimpleAdapter:可以在列表项中同时展示文字和图片

    BaseAdapter:适应性更强,可以在别的代码文件中编写操作代码,全能

ArrayAdapter数组适配器

示例:

ArrayAdapter adapter = new ArrayAdapter<>(参数1,参数2,参数3)

  • this:这整个安卓程序接口
  • 第二个参数是资源ID,代表一个ListView设置列表项的样式,可自定义
  • 第三个参数是列表项的来源
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //加载指定的布局文件
        setContentView(R.layout.listview_layout);
        ListView lv = findViewById(R.id.listview1);
        String[] arr = {"唱","跳","rap","篮球"};
        /**
         * this是这整个安卓程序的接口
         * 第二个参数是资源ID,代表一个TextView设置列表项的样式,可自定义
         * 第三个参数是列表项的来源
         */
        ArrayAdapter<String> adapter = new ArrayAdapter<>(
                this, android.R.layout.simple_expandable_list_item_1,arr
        );
        lv.setAdapter(adapter);
   <ListView
        android:id="@+id/listview1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
SimpleAdapter简单适配器

该适配器可以展示文字和图片

  • new SimpleAdapter()的后4个参数
    • list:指定一个集合对象
    • R.layout.list_item_layout:指定界面布局的id
    • new String[]:决定提取哪些内容显示在ListView的每一行
    • new int[]{R.id.name,R.id.icon,R.id.dexc}):决定显示哪些组件
  1. MainActivity.java
public class MainActivity extends AppCompatActivity {
    //定义名字数组
    private String[] name={"张三","王五","赵六"};
   //定义描述任务数组
    private String[] desc={"唱歌","跳舞","打球"};
        //定义头像数组
        private int[] icon=new int[]
        {R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher};

    @Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView listView =	   (ListView)findViewById(R.id.listview1);
        //创建一个list集合,list集合的元素是MAP
List<Map<String,Object>> list=new ArrayList<Map<String,Object>>();
    
  for(int i=0;i<name.length;i++){
  Map<String, Object> listitem=new HashMap<String, Object>();
            listitem.put("icon",icon[i]);
            listitem.put("name",name[i]);
            listitem.put("desc",desc[i]);
            list.add(listitem);
        }
        //创建一个SimpleAdapter
	SimpleAdapter adapter = new SimpleAdapter(this,list,R.layout.list_item_layout,
        new String[]{"name","icon","desc"},new int[]{R.id.name,R.id.icon,R.id.dexc});
    listView.setAdapter(adapter);

    }
}
  1. list_item_layout.xml
<ImageView
        android:id="@+id/icon"
        android:layout_width="60dp"
        android:layout_height="wrap_content"
         />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:textSize="16sp"
             />

        <TextView
            android:id="@+id/desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16sp"

             />
    </LinearLayout>

  1. activity_main.xml
 <ListView
        android:id="@+id/listview1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="#C4C4C4"
        android:dividerHeight="1dp">
    </ListView>
BaseAdapter基本适配器

从BaseAdapter派生的数据适配器主要实现下面5种方法。

  • 构造方法:指定适配器需要处理的数据集合。
  • getCount:获取列表项的个数。
  • getItem:获取列表项的数据。
  • getItemId:获取列表项的编号。
  • getView:获取每项的展示视图,并对每项的内部控件进行业务处理。

在这里插入图片描述

2.4.4【Dialog对话框的使用】

本次介绍AlertDialog 弹出对话框的使用

创建AlertDialog对话框的步骤如下:

  1. 创建AlertDialog.Builder对象
  2. setTitle:设置对话框的标题文本
  3. setMessage:设置对话框的内容文本
  4. setPositiveButton:设置肯定按钮的信息、setNegativeButton设置否定按钮的信息
  5. 调用AlertDialog.Builder对象的create( )方法创建AlertDialog对象,再show()方法显示出来
  6. 其他:setIcon:设置对话框的标题图标
   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_layout);
        AlertDialog.Builder builder 
            = new AlertDialog.Builder(this);
        //设置标题
        builder.setTitle("温馨提示");
        //设置对话框的消息内容
        builder.setMessage("是否退出");
        //设置确定按钮
        builder.setPositiveButton("确定", (dialog, which) -> {
            finish();
        });
        //设置取消按钮
        builder.setNegativeButton("取消",(dialig,which)->{
            dialig.cancel();
        });
        //创建AlertDialog实例
        AlertDialog alertDialog = builder.create();
        //结合按钮使用
        Button btn = findViewById(R.id.button1);
        btn.setOnClickListener(v -> {
            alertDialog.show();//显示对话框
        });
    }

2.4.5【Toast的使用】

  • 作用:短暂的显示一下消息

  • 掌握两个方法:

    • makeText():设置提示用户的文字,三个参数(上下文环境,显示的文字,显示时间的长短)
    • show():显示Toast
    public class MainActivity extends AppCompatActivity {
    
        @SuppressLint("MissingInflatedId")
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.dialog_layout);
            Button btn = findViewById(R.id.button1);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this,"你好啊~~~",Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    
    

3. Activity

  • 掌握Activity生命周期的作用,能够正确使用每个方法
  • 掌握Activity的创建、配置、启动和关闭的方式,能够完成创建、配置、启动和关闭Activity
  • 掌握IntentlntentFilter的用法,能够灵活使用lntent与lntentFilter

3.1【Activity基础】

Activity是一个负责与用户交互的组件,每个Android应用中都会用Activity来显示界面以及处理界面上一些控件的事件。每个APP中可以包含多个Activity,每个Activity负责管理一个用户界面。

Activity生命周期

  1. 基本介绍:Activity的生命周期指的是Activity从创建到销毁的整个过程,这个过程大致可以分为五种状态,分别是启动状态运行状态暂停状态停止状态销毁状态

在这里插入图片描述

  1. 回调方法(7个)

    • onCreate():Activity创建时调用,通常做一些初始化设置。
    • onStart():Activity即将可见时调用。
    • onResume():Activity获取焦点时调用。
    • onPause():当前Activity被其他Activity覆盖或屏幕锁屏时(失去焦点时)调用。
    • onStop:Activity对用户不可见时调用。
    • onRestart():Activity从停止状态到再次启动时调用。
    • onDestroy():Activity销毁时调用。
    package com.example.radionbuttonandcheckbox;
    
    //import.....
    
    public class MainActivity extends AppCompatActivity {
        private Button btn;
        private TextView tv;
        @SuppressLint("MissingInflatedId")
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //加载指定的布局文件
            setContentView(R.layout.activity_main);
             btn = findViewById(R.id.button1);
             btn.setOnClickListener(v -> {
                 //SecondActivity
                 Intent intent = new Intent(this, SecondActivity.class);
                 startActivity(intent);
             });
    
    
        }
        
        
       ==== 测试生命周期函数====
        //onStart
        @Override
        protected void onStart() {
            super.onStart();
            Log.i("MainActivity","onStart()");
        }
    
    ...........
    
    }
    
  2. 注意事项

    • 状态之间的转换不是线性的

      比如当在onPause()中,系统不保证接下来一定是onStop(),如果用户迅速回到这个Activity,系统会调用onResume()。

    • 系统不一定按顺序执行每个回调

      在异常情况下,如内存不足,系统可能会在调用onStop()之前直接调用onDestroy()

  3. 横竖屏切换

  • 当Activity对应的界面进行横竖屏切换时,程序首先会销毁Activity,然后再调用onCreate()方法重建Activity,如:用户竖屏填写信息,切换为横屏时,信息被清除。

  • 如果不希望界面进行横竖屏切换时Activity被销毁重建,在Android9.0或以下的版本可以通过configChanges属性进行设置,示例代码如下:

    <!--横竖屏切换时不销毁Activity-->
            <!--portrait在Activity中添加竖屏状态-->
            <!--landscape在Activity中添加横屏状态-->
            <activity
                android:name=".SecondActivity"
                android:exported="false"
                android:screenOrientation="landscape" />
    

Log日志的使用

5种方法打印日志信息:

两个参数:tag,msg

Log.v():打印繁琐且没什么用的信息

Log.d():打印调试信息

Log.i():打印主要的信息,且是你想看到的,帮助用户分析行为数据

Log.w():打印警告信息,若打印出来,说明程序有潜在的风险

Log.e():打印程序的错误信息

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fimg-%E9%98%BF%E6%96%AF%E8%92%82%E8%8A%ACblog.csdnimg.cn%2Fdirect%2F20dd484187434054aac08fb4937e0031.png%23pic_center&pos_id=img-ZAvmRfAJ187434054aac08fb4978e0031.png#pic_center

创建Activity

两种方式:

  1. 按照"New"—>“Activity”—>“Empty view Activity”

  2. 通过创建Java类继承AppCompatActivity,然后在清单文件

    中进行相关配置即可。

     <activity android:name=".SecondActivity"/>
    
    public class NewActivity extends AppCompatActivity {
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
            super.onCreate(savedInstanceState, persistentState);
            setContentView(R.layout.activity_main);
        }
    }
    

Activity的启动和关闭

  • 启动:startActivity(intent);
  • 关闭:通常使用finish()方法关闭
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_layout);
        Intent intent = new Intent(MainActivity.this, MainActivity2.class);
        startActivity(intent);//启动Activity

        Button button = findViewById(R.id.button1);
        button.setOnClickListener(v -> {
                    finish();//关闭Activity
                }
        );
    }

3.2【Intent的使用】

Intent介绍

  • Intent是相同或者不同应用程序组件间通信的中介。在Android程序中,ActivityServiceBroadcastReceiver这三种核心组件都需要使用Intent进行操作,例如,如果用户需要从一个Activity切换到另一个Activity,则必须使用lntent来进行切换。本节将针对lntent的相关知识进行详细讲解。

  • Intent被称为意图,用于不同组件间的通信,分为显示Intent和隐式Intent

显式Intent

两种方式

  1. 显式lntent可以直接通过类的名称指定目标Activity
  2. 还可以指定目标组件的包名、全路径(完整包名+类名)指定开启的组件

方式一:

this指当前Activity,MainActivity2为要启动的Activity

Intent intent = new Intent(this, MainActivity2.class);
        startActivity(intent);//启动Activity

方式二:

使用setClassName(包名,全类名)

public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        Intent intent = new Intent();
        intent.setClassName("com.example.radionbuttonandcheckbox.testIntent",
                "com.example.radionbuttonandcheckbox.testIntent.TestActivity");
        startActivity(intent);
    }   
}

隐式Intent

  • 程序没有明确指定需要启动的Activity,Android系统会根据AndroidManifest.xml文件设置的动作(action)、类别(category)、数据(Uri和数据类型)来启动合适的组件
  • action、category、data三个属性匹配成功后才可以唤起相应的组件
<!-- 隐式Intent-->
      <activity
            android:name=".SecondActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
                <data android:scheme="http"/>
            </intent-filter>
        </activity>
//SecondActivity   
//隐式Intent
        Intent intent = new Intent();
        intent.setAction("android.intent.action.MAIN");
        startActivity(intent);

Android系统中常用的action常量

  1. android.intent.action.MAIN:Android程序的入口。
  2. android.intent.action.VIEW:显示指定数据。
  3. android.intent.action.EDIT:编辑指定数据。
  4. android.intent.action.DIAL:显示拨号面板。
  5. android.intent.action.CALL:直接呼叫指定的号码。
  6. android.intent.action.ANSWER:接听来电。
  7. android.intent.action.SEND:向其他程序发送数据,例如彩信或邮件等。
  8. android.intent.action.SENDTO:向他人发送短信。
  9. android.intent.action.SEARCH:执行搜索。
  10. android.intent.action.GET_CONTENT:让用户选择数据,并返回所选数据。

IntentFilter匹配规则

当发送一个隐式lntent后,Android系统会将它与程序中的每一个组件的过滤器进行匹配,匹配属性有actiondatacategory,需需要这3个属性都匹配成功才能唤起相应的组件

  1. action属性匹配规则

    action属性用来指定lntent对象的动作

    **注意:**在清单文件中为Activity添加标签时,必须添加action属性,否则隐式Intent无法开启该Activity。

  2. data属性匹配规则

    data属性用来指定数据的URI或者数据MIME类型

     <data android:scheme="http..." android:mimeType="vedio/mpeg"/>
    
  3. category属性匹配规则

    category属性用于为action添加额外信息,一个lntentFilter可以不声明category属性,也可以声明多个category属性。

     <intent-filter>
     <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category
    .BROWSABLE" />
     </intent-filter>
    

3.3【Activity之间的数据传递】

  • 一个Android程序通常会包含多个Activity,这些Activity之间可以互相跳转并传递数据

  • Android提供的Intent可以在界面跳转时传递数据。使用Intent传递数据两种方式

使用lntent的putExtra()方法传递数据

传递数据

//MainActivity
 Intent intent = new Intent(this, SecondActivity.class);

        intent.putExtra("name","琳琳");
        intent.putExtra("age",19);
        intent.putExtra("gender","女");
        intent.putExtra("isMan",true);
        startActivity(intent);

获取数据

//SecondAcitvity
Intent intent = this.getIntent();
String name = intent.getStringExtra("name");
int age = intent.getIntExtra("age",18);
boolean isMan = intent.getBooleanExtra("isMan",false);



/**
标准:
*/

        Intent intent = this.getIntent();
        if (intent != null) {
            String[] userinfo = intent.getStringArrayExtra("userInfo");
            if (userinfo != null && userinfo.length == 2) {
                String username = userinfo[0];
                String password = userinfo[1];
                // 使用用户名和密码
                String showInTv = "用户名:"+username+"\n"+"密码:"+password;
                tv.setText(showInTv);
            }
        }

使用Bundle类传递数据

  • 类似Java的Map集合

传递数据

//MainActivity
Intent intent1 = new Intent(this,SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("school","斯坦福大学");
bundle.putInt("sno",12345);
intent1.putExtras(bundle);//数据封装到Bundle对象中

获取数据

//SecondAcitvity
//通过bundle对象获取数据
        Bundle bundle = getIntent().getExtras();
        String sch = bundle.getString("school");
        int sno = bundle.getInt("sno");

putExtra()和putExtras()

putExtra

  • 使用putExtra()时,如果有多个数据要传递,需要对每个数据单独调用一次putExtra(),使用putExtra()允许一次添加一个额外的数据(键值对)

putExtras

  • 使用putExtras()时,可以先将所有数据放入一个Bundle,然后一次性全部添加到IntentputExtras()允许一次添加一个Bundle对象,这个对象可以包含多个键值对。putExtras()更适用于需要一次性传递大量的数据

使用Serializable序列化接口传递对象

步骤:

  1. 假如写个Person类,必须实现序列化接口

  2. MainActivity:

    Person person = new Person(name,gender,age);
    Intent intent = new Intent(this,B.class);
    intent.putExtra("personObject",person);
    startActivity(intent);
    
  3. SerializableActivity:

    Person person=(Person)getIntent().getSerializableExtar("personObject");
    
    然后就操作这个对象获取信息了
    
    

3.4【Activity之间的数据回传】

【介绍】

  • 当我们从MainActivity界面跳转到SecondActivity界面时,可以在SecondActivity界面上进行一些操作。当关闭SecondActivity界面时,想要从该界面返回一些数据到MainActivity界面,Android系统为我们提供了一些方法用于Activity之间数据的回传。

旧的数据回传方式

  1. Android 10版本的数据回传方式:

在这里插入图片描述

​ Activity数据回传流程图

  1. Activity之间进行数据回传时包含3个方法

    • startActivityForResult()

    • setResult()

    • onActivityResult()

startActivityForResult()

  • 作用:用于开启一个Activity,当开启的Activity销毁时,会从销毁的Activity中返回数据。

  • 代码:

    第一个参数为Intent对象,第二个参数为requestCode请求码

    startActivityForResult(intent, 1);
    

setResult()

  • 作用:用于携带数据进行回传

  • 代码:

    第一个参数:结果码resultCode

    setResult(1,intent);
    

onActivityResult()

  • 作用:用于接收回传的数据

  • 代码:

    //从第二个Activity获取数据
        @Override
    protected void onActivityResult(int requestCode, 
        int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
            tv = findViewById(R.id.textView);
            if (requestCode == 1 && resultCode == 1) {
                String studentName = 	       data.getStringExtra("studentName");
                tv.setText(studentName);
         }
    }
    
  1. 小案例:

步骤:

  1. 创建MainActivity;
  2. 创建SecondActivity;
  3. 在MainActivity中点击按钮跳转到SecondActivity;
  4. 在SecondActivity中点击按钮将数据返回到MainActivity;
//MainActivity
private Button btn;
private TextView tv;

//OnCreate()方法
btn = findViewById(R.id.button);
        btn.setOnClickListener(v -> {
            //Android 10版本以下的api
            Intent intent = new Intent(this, SecondActivity.class);
            //请求返回结果
            startActivityForResult(intent, 1);

        });

    @Override
protected void onActivityResult(int requestCode, 
    int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
        tv = findViewById(R.id.textView);
        if (requestCode == 1 && resultCode == 1) {
            String studentName = 	       data.getStringExtra("studentName");
            tv.setText(studentName);
     }
}

//SecondActivity
  btn = findViewById(R.id.button2);
        //点击按钮数据回传
        btn.setOnClickListener(v -> {
            Intent intent = new Intent();
            intent.putExtra("studentName","张三");
            //将结果返回给第一个Activity
            setResult(1,intent);
            finish();//一定要关闭
        });

新版本数据回传方式

  • 推荐使用ActivityResultContractsActivityResultLauncher。这种方法比旧版本更为简洁和模块化
  • 使用Android10或以上版本这种新方式,不需要再处理请求码,这使得代码更加简洁和易于维护减少了Activity间耦合,使得代码结构更清晰。

在这里插入图片描述

步骤:

  1. 在Activity中接收回传的数据
  2. 使用launch()方法启动目标Activity
  3. 在被启动的Activity中设置返回结果

3.5【 Android中的任务栈】

  • Android系统采用任务栈的方式来管理Activity实例
  • 任务栈:一种用来存放Activity实例的容器
  • 特点:“先进后出”
  • 操作:压栈和出栈
  • 一个应用程序有一个任务栈
  • 每启动一个Activity都会将其加入栈中,放在栈顶位置
  • 用户操作的界面永远都是栈顶的Activity
  • 每启动一个新的Activity都会创建新的实例并覆盖在原Activity之上
  • 当单击返回按钮,最上面的Activity会被销毁,下面的Activity会重新显示。

在这里插入图片描述

3.6【Activity的启动模式】

  • 在开发中,还可以为每个Activity指定恰当的启动模式,来复用 Activity实例优化 App 的效率和资源占用
  • 掌握Activity的启动模式,能够归纳Activity的4种启动模式的作用

1. Activity启动模式有四种:

standard模式

  • standard模式是Activity的默认启动方式,每启动一个Activity就会在栈顶创建一个新的实例。

singleTop模式

  • singleTop模式会判断要启动的Activity实例是否位于栈顶,如果位于栈顶直接复用否则创建新的实例

singleTask模式

  • singleTask模式下每次启动该Activity时,系统首先会检查栈中是否存在当前Activity实例,如果存在则直接使用,并把当前Activity之上的所有实例全部出栈

singleInstance模式

  • singlelnstance模式会启动一个新的任务栈来管理Activity实例,无论从哪个任务栈中启动该Activity,该实例在整个系统中只有一个。

在这里插入图片描述

开启使用singlelnstance模式的Activity的两种情况

  1. 要开启的Activity实例在栈中不存在,则系统会先创建一个新的任务栈,然后再压入Activity实例
  2. 要启动的Activity已存在,系统会把Activity所在的任务栈转移到前台,从而使Activity显示。

4. Android事件处理

  • 掌握基于回调机制的事件处理方法,能够处理按下、弹起、触摸等事件
  • 掌握基于监听接口机制的事件处理方法,能够处理点击、长按等事件
  • 掌握Handler消息机制原理,能够使用Handler进行线程间通信

Android事件处理分为两种:

  • 键盘事件
  • 触摸事件

4.1【基于回调机制的事件处理】

  • 当用户与UI控件发生某个事件(如按下事件、滑动事件、双击事件)时,程序会调用控件自己特定的方法处理该事件,这个处理过程就是基于回调机制的事件处理。
方法说明
boolean onKeyDown()按下键盘时触发
boolean onKeyUp()松开键盘时触发
boolean onTouchEvent()触摸控件时触发
boolean onFocusChanged()焦点发生改变时触发

注意:对于基于回调机制的事件传播而言,某控件上所发生的事件:

  • 不仅触发该控件的回调方法
  • 如果事件传播到Activity中,也会触发该控件所在的Activity的回调方法。

键盘事件

onKeyDown()方法

 @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode==KeyEvent.KEYCODE_BACK){
            Toast.makeText(this,"你点击了后退键",Toast.LENGTH_LONG).show();
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
            Toast.makeText(this,"你点击了音量键+",Toast.LENGTH_LONG).show();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

触摸事件

onTouchEvent()

  • 方法处理的事件分为三种
  1. 屏幕被按下;
  2. 屏幕弹起;
  3. 在屏幕中滑动;
  • 示例:

    /**
         * 触摸事件
         *
         * @param event The touch screen event being processed.
         * @return
         */
        @Override
    public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Toast.makeText(MainActivity.this,
                            "按下了按钮", Toast.LENGTH_SHORT).show();
                    break;
                case MotionEvent.ACTION_UP:
                    Toast.makeText(MainActivity.this,
                            "按钮被弹起", Toast.LENGTH_SHORT).show();
                    break;
                case MotionEvent.ACTION_MOVE:
                    Toast.makeText(MainActivity.this,
                            "在按钮上进行移动", Toast.LENGTH_SHORT).show();
                    break;
            }
            return super.onTouchEvent(event);
        }
    

onFocusChanged()

  • onFocusChanged()方法是焦点改变的回调方法,只能在View中重写。当某个控件重写了该方法后,焦点发生变化时,会自动调用该方法来处理焦点改变的事件,其定义方式如下:

    public class MyButton extends androidx.appcompat.widget.AppCompatButton {
        public MyButton(Context context) {
            super(context);
        }
    
        @Override
        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
            super.onFocusChanged(focused, direction, previouslyFocusedRect);
        }
    }
    
    

    三个参数:

    1. focused:触发该事件的View是否获得了焦点;
    2. direction:焦点移动的方向;
    3. previouslyFocusedRect:在触发事件的View坐标系中,前一个获得焦点的矩形区域

自定义Button

/**
 * 自定义的 Button
 */
public class MyButton extends androidx.appcompat.widget.AppCompatButton {
    public MyButton(Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("测试btn","我是Button,你触碰了我:"+event.getAction());
        Toast.makeText(getContext(),"我是Button,你触碰了我"+event.getAction(),Toast.LENGTH_SHORT).show();
        return true;
    }


}
//布局文件中使用Button  
<com.example.radionbuttonandcheckbox.MyButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试一下onTouchEvent"/>

4.2【基于监听接口的事件处理】

  • 为Android界面组件绑定特定的事件监听器

    如:setOnClickListener等等

  • 基于监听接口机制的事件处理是一种“面向对象”的事件处理,在事件监听的处理模型中主要涉及三个对象,这三个对象的具体介绍如下:

EventSource

  • 事件发生的场所

Event

  • 封装了界面组件发生的特定事情

EventListener

  • 负责监听事件源所发生的事件

  • 流程图

    在这里插入图片描述

监听器接口

  • 在基于监听的事件处理模型中,事件监听器必须实现事件监听器接口font>,Android系统为不同的界面组件提供了不同的监听器接口,这些接口通常以内部类的形式存在。

在这里插入图片描述

4.3【Handleri消息机制】

引入】:

  • 当应用程序启动时,Android首先会开启一个Ul线程(主线程),UI线程负责管理UI界面中的控件,并进行事件分发

  • 在Android中,更新Ui界面只能在主线程中完成,其他线程是无法直接对主线程进行操作的。

  • 为了解决以上问题,Android中提供了一种异步回调机制Handler,由Handler来负责与子线程进行通信。

介绍】:

Handler是一种异步回调机制主要负责与子线程进行通信,主要包括4个关键对象

  1. Message:线程之间传递的消息
  2. Handler:发送消息和处理消息
  3. MessageQueen:存放发送消息
  4. Looper:loop()方法进入循环

在这里插入图片描述

4.4【AnsyncTask异步任务】

  • AnsyncTask异步任务可以简化操作更轻量级

使用AnsyncTask的步骤如下:

  1. 创建AsyncTask的子类,并指定参数类型;

  2. 实现AsyncTask的方法:

    dolnBackground(Params…):后台线程将要完成的功能

    onPostExecute(Resultresult):一般负责更新UI线程等操作

  3. 调用AsyncTask子类的实例的execute(Params…params)方法执行耗时操作。

5.Fragment基础

5.1【Fragment简介】

  • Fragment(碎片)是Android3.0后引入的一个新的API。

    它是一种可以嵌入在活动中的Ul片段,可以将其看成一个小型Activity,它又被称作Activity片段。

  • 一个Activity中可以包含多个Fragment

  • 一个Fragment也可以在多个Activity中使用

  • 手机或平板竖屏:Fragment1需要嵌入到Activity1中,Fragment2需要嵌入到Activity2中;

  • 平板横屏:两个Fragment可同时嵌入到Activity1中

在这里插入图片描述

5.2【Fragment生命周期】

  1. onAttach():Fragment和Activity建立关联时调用
  2. onCreateView():Fragment加载布局时调用
  3. onActivityCreated():Fragment关联的Activity创建完成时调用
  4. onDestoryView():Fragment关联的视图被移除时调用
  5. onDetach():Fragment和Activity解除关联时调用

在这里插入图片描述

Fragment生命周期状态直接受其所属Activity的生命周期状态影响:

在这里插入图片描述

5.3【创建Fragment】

  • 第一个参数:表示Fragment对应的布局资源ID
  • 第二个参数:表示存放Fragment视图的父视图
  • 第三个参数:表示是否将生成的视图添加一个父视图

public class LeftFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_left,container,false);

    }
}

5.4【在Activity中添加Fragment】

两种方式

  1. 在布局文件中静态添加Fragment:

    在Activity布局文件中:

    使用fragment标签

    必须指定android:name属性,其属性值为Fragment的全路径名称

        <fragment
            android:id="@+id/left_fragment"
            android:name="com.example.radionbuttonandcheckbox.LeftFragment"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />
    //weight表示在Activity中的权重,即占多少份
    
  2. 在Activity中动态加载Fragment:

    步骤:

    1. 创建指定Fragment的实例对象
    2. 获取FragmentManager的实例
    3. 开启FragmentTransaction事务
    4. 向Activity的布局容器中添加Fragment
    5. 通过commit0方法提交事务

    注意:

    getSupportFragmentManager() 是新版本的用法,getFragmentManager()是旧版本的用法。

    public class MainActivity extends AppCompatActivity {
        Button button;
        @SuppressLint("MissingInflatedId")
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button=(Button)findViewById(R.id.button);
            button.setOnClickListener(v -> {
                
                
                //创建fragment实例
                SecondFragment secondFragment = new SecondFragment();
                //获取fragment示例
                FragmentManager fm = getSupportFragmentManager();
                //开启事务
                FragmentTransaction transaction = fm.beginTransaction();
                //将right_layout对应的fragment替换成secondFragment
                transaction.replace(R.id.right_layout,secondFragment);
                //将此事务添加到返回栈,以便用户可以按返回键返回到上一个Fragment
                transaction.addToBackStack(null);
                //提交事务
                transaction.commit();
            });
        }
    
    }
    

6. Android数据存储

6.1【数据存储方式】

  • 多种数据存储方式

在这里插入图片描述

6.2【文件存储】

  • 文件存储是Android中最基本的一种数据存储方式通过I/O流的形式把数据直接存储到文件中适合存储少量数据
  • 分为内部存储外部存储

在这里插入图片描述

6.2.1【文件的内部存储】

1. 写入文件
  • 使用ContextopenFileOutput方法来创建或打开一个文件时,这个文件默认会被创建在应用的内部存储中的私有目录下。不要直接指定目录,因为这个方法是自动处理的。

openFileOutput

  • 用于在私有目录下创建或打开一个文件

两个参数:

  • name(String):指的是文件名,可以不用加扩展名,因为可以识别

  • mode(int):文件的操作模式,默认Private

  • mode的取值:

    • MODE_PRIVATE(默认取值,该文件只能被当前程序读写)

    • MODE_APPEND(该文件的内容可以追加)

    • MODE_WORLD_WRITEABLE:该文件的内容可以被其他程序读取

    • MODE_WORLD_READABLE:该文件的内容可以被其他程序写入

 /**
         * 写入文件
         */
        button1.setOnClickListener(v -> {
			//文件名称
            String fileName="data.txt";
            //这里写入一个字符串
            String writeContent = "helloWorld";
        
            try {
                fos = openFileOutput(fileName,MODE_PRIVATE);
                fos.write(writeContent.getBytes());
                //刷新流,确保文件被完整写入
                fos.flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }finally {
                try {
                    assert fos != null;
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
2. 读取文件

openFileInput

  • 用于打开并且读取应用程序的私有文件目录下已存在的文件
        /**
         * 读取文件
         */
        button2.setOnClickListener(v -> {
            String readContent = "";
            FileInputStream fis = null;
            try {
                fis = openFileInput("data.txt");
                //available()方法用于返回可以从此输入流中读取(或跳过)的字节数的估计值
                byte[] buffer = new byte[fis.available()];
                fis.read(buffer);
                readContent = new String(buffer);
                //显示读取的数据
                tv.setText(readContent);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }finally {
                try {
                    assert fis!=null;
                    fis.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });

6.2.2【文件的外部存储】

写入文件

getExternalFilesDir().getAbsolutePath();

  • 这个方法返回的是应用私有的外部存储目录的路径。这个目录是专门为当前应用保留的,其他应用通常无法直接访问这个目录。
  • 用途:这个目录通常用于存储应用生成的文件,这些文件是应用特有的,不需要与其他应用共享。

Environment.getExternalStorageDirectory().getPath();

  • 这个方法返回的是外部存储的根目录路径,也就是传统意义上的SD卡路径(尽管现在很多设备并没有物理的SD卡)。这个目录是公共的,所有应用都可以访问(如果有相应的权限)。
  • 用途:这个目录通常用于存储需要与其他应用或用户共享的文件,例如照片、视频、音乐等。
  button1.setOnClickListener(v -> {
            //获取外部设备状态
            String state = Environment.getExternalStorageState();
            //判断设备是否可用
            if (state.equals(Environment.MEDIA_MOUNTED)){
                String SDPath;
                //如果当前Android版本的高于29(Android 10)
                if(Build.VERSION.SDK_INT>29){

                    SDPath = getExternalFilesDir(null).getAbsolutePath();
                }else {
                 
                    SDPath = Environment.getExternalStorageDirectory().getPath();
                }
                File file = new File(SDPath, "data.txt");
                String data = "Hello World 外部存储";
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(file);
                    fos.write(data.getBytes());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                finally {
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
读取文件
button2.setOnClickListener(v -> {
            //获取外部存储路径
            File SDPath = Environment.getExternalStorageDirectory();
            //找到或创建该路径下名为“data.txt”的文件
            File file = new File(SDPath, "data.txt");
            FileInputStream fis;
            BufferedReader br;
            try {
                //创建文件输入流,准备读取这个文件
                fis = new FileInputStream(file);
                //创建字符输入缓冲流对象
                br = new BufferedReader(new InputStreamReader(fis));
                //按行读取数据
                String data = br.readLine();
                tv.setText(data);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });

6.2.3【缓冲字符输入输出流】

在Java的IO(输入/输出)体系中,字符流和字节流是两种基本的流类型,它们分别用于处理字符数据和字节数据。

字符流

字符流用于处理字符数据。在Java中,字符流以ReaderWriter为基类。这些流按照16位Unicode字符进行操作,它们更适合处理文本文件。

  • Reader: 所有字符输入流的超类。
  • Writer: 所有字符输出流的超类。

常见的字符流子类包括:

  • FileReaderFileWriter:用于文件操作。
  • BufferedReaderBufferedWriter:带有缓冲区的字符流,用于提高读写效率。
  • InputStreamReaderOutputStreamWriter:桥接类,用于将字节流转换为字符流,或将字符流转换为字节流。

字节流

字节流用于处理字节数据。在Java中,字节流以InputStreamOutputStream为基类。这些流按照字节进行操作,它们更适合处理二进制文件。

  • InputStream: 所有字节输入流的超类。
  • OutputStream: 所有字节输出流的超类。

常见的字节流子类包括:

  • FileInputStreamFileOutputStream:用于文件操作。
  • BufferedInputStreamBufferedOutputStream:带有缓冲区的字节流,用于提高读写效率。
  • ObjectInputStreamObjectOutputStream:用于序列化和反序列化对象。

桥接流

InputStreamReaderOutputStreamWriter 是特殊的桥接流,它们允许你将字节流转换为字符流,或将字符流转换为字节流。这在需要处理文本文件但文件是以字节形式存储时非常有用。

总结

  • 字符流:以ReaderWriter为基类,用于处理字符数据,更适合处理文本文件。
  • 字节流:以InputStreamOutputStream为基类,用于处理字节数据,更适合处理二进制文件。

在选择使用字符流还是字节流时,主要取决于你处理的数据类型:如果是文本数据,通常使用字符流;如果是二进制数据,通常使用字节流。

BufferedWriter
  • 缓冲字符输出流,用于高效写入
  • 参数:Write(字符流),因此需要使用桥接器OutputStreamWriter将字节流FileOutputStream转换为字符流,实现写入字符,效率高。
 FileOutputStream out;
out = new openFileOut("data");
 BufferedWriter bufferedwriter = null;
 bufferedwriter = new BufferedWriter(new OutputStreamWriter(out));
BufferedReader
  FileInputStream in;
  BufferedReader reader = null;
  in = openFileInput("data");
  reader = new BufferedReader(new InputStreamReader(in));       

Try-catch的注意点

下面这段代码的意图是:

  • 首先检查writer对象是否为null。这是因为如果在try块中初始化writer时发生异常,writer可能不会被赋值,因此直接调用writer.close()会抛出NullPointerException
  • 如果writer不是null,则尝试关闭它。关闭文件流可能会抛出IOException,因此我们需要在finally块内部再嵌套一个try-catch块来捕获这个异常。
  • 如果在关闭writer时发生了IOException,我们将这个异常包装成一个新的RuntimeException并重新抛出。这是因为finally块本身不应该抛出被外部try-catch块捕获的异常类型,所以通过转换为运行时异常来确保异常能被上层代码捕获并处理。
finally {  
    if (writer != null) {  
        try {  
            writer.close();  
        } catch (IOException e) {  
            throw new RuntimeException(e);  
        }  
    }  
    if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
     customToast("关闭文件输出流时发生错误:" +e.getMessage());
                        e.printStackTrace();
                    }
                }
}

6.3【SharedPreferences】

  • SharedPreferences是一个轻量级的存储类
  • 当程序中有一些少量数据需要持久化存储时,使用SharedPreferences类存储

数据存入

步骤:

  1. 调用getSharedPreferences()方法获取实例对象,该对象本身只能获取数据
  2. 调用SharedPreferences类的edit()方法获取可编辑的Editor对象
  3. 通过该对象的putXxx()方法存储数据。

commit()apply()的区别与使用场景:

  • apply()在后台异步执行,不会阻塞主线程,从而提供更好的用户体验。

  • 只有在需要确保写入成功(比如处理可能的失败情况)时,才应该使用 commit() 方法。在大多数情况下,apply() 是更好的选择。

getSharedPreferences()

  • 用途:获取实例对象,该对象本身只能获取数据
  • 第一个参数:文件名是 “data”。Android 系统会使用这个名称来创建或检索一个特定的 SharedPreferences 文件。
  • 第二个参数:操作模式,这个模式确保了数据的安全性,防止其他应用程序访问您的私有数据。
  //获取实例对象
        SharedPreferences sp = getSharedPreferences("data", MODE_PRIVATE);
        //获取编辑器
        SharedPreferences.Editor edit = sp.edit();
        //以key-value的形式保存数据
        edit.putString("name","张三");
        edit.putInt("age",18);
        //提交数据,比commit()更好,不会阻塞线程
        edit.apply();

注意:

key/value(键值对)的形式保存数据,value值只能是float、int、long、boolean、String、Set类型数据

读取与删除数据

读取SharedPreferences中的数据:

//读取数据  
        SharedPreferences sp1 = getSharedPreferences("data", MODE_PRIVATE);
        String data = sp1.getString("name", "");

删除SharedPreferences中的数据:

 edit.remove("name");//删除key值为name的数据
        edit.clear();//删除所有数据

注意:

  • 获取数据的key值与存入数据的key值的数据类型要一致,否则查找不到数据
  • 保存SharedPreferences的key值时可以用静态变量保存,以免存储、删除时写错。如: private final String key = “itcast”

6.4【SQLite数据库】

  • 适合存储大量数据
  • 对数据进行管理和维护

###6.4.1【SQLite数据库的类和接口】

SQLiteOpenHelper类
  • 它是SQLiteDatabse的一个帮助类,用来管理数据额产和版本的更新

  • 它有两个回调方法:

    onCreate

    初次生成数据库时才会被调用,用于生成数据库表结构及添加一些应用使用到的初始化数据。

      @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
            String sql = "create table user(id integer primary key autoincrement," +
                    "username varchar(20),paswd varchar(20),sex varchar(20),age integer)";
            sqLiteDatabase.execSQL(sql);
            Log.i("SQLite","execSQL(sql)");
        }
    
    

    execSQL()方法执行SQL语句

    onUpgrade

    ​ 在数据库的版本发生变化时会被调用,一般在软件升级时才需

    改变版本号,而数据库的版本是由程序员控制的。

SQLiteDatabase类

常用方法:

  • insert

  • update

  • delete

  • query

  • exceSQL

  • close

Cursor接口
  • Cursor是一个游标接口,在数据库中作为返回值,相当于结果集ResultSet。

常用方法:

moveToFirst() :移动光标到第一行

moveToLast() :移动光标到最后一行

moveToNext() :移动光标到下一行

moveToPosition(int position) :移动光标到一个绝对的位置

moveToPrevious() :移动光标到上一行

getColumnCount():返回所有列的总数。

getColumnlndex(String columnName):返回指定列的名称,如果不存在返 回-1。

getColumnName(int columnlndex):从给定的索引返回列名 。

getColumnNames():返回一个字符串数组的列名。

getCount():返回Cursor中的行数。

6.4.2【SQLite数据库的创建与操作】

创建SQLite数据库
  • Android系统在使用SQLite数据库时, 一般使用SQLiteOpenHelper的子类创建SQLite数据库,因此需要创建一个类继承自 SQLiteOpenHelper,然后重写oncreate()方法.

public class TestSQLite extends SQLiteOpenHelper {
    public TestSQLite(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, "user_db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table user(id integer primary key autoincrement," +
                "username varchar(20),paswd varchar(20),sex varchar(20),age integer)";
        db.execSQL(sql);
        Log.i("SQLite", "execSQL(sql)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        
    }

}
创建好数据库后的使用方法

示例:

  • 在MainActivity类中调用即可。
 //dbHelper = new MyDataBaseHelper(MainActivity.this, "BookStore.db", null, 1);
        dbHelper = new MyDataBaseHelper(MainActivity.this, "BookStore.db",
                null, 2);


        //点击按钮后获取可读写SqliteDatabase对象
        createDatabase.setOnClickListener(v -> {
            dbHelper.getWritableDatabase();
        });
添加数据
  1. 需要获取SQLiteDatabase对象;
  2. 把数据放入values中(类似集合);
  3. 调用insert()方法插入数据

insert()方法的三个参数:

  • table(String):表名,为哪张表插入数据;

  • nullColumnHack(String):

    • 如果你需要主键自增,可以设置为null,即如果表中有字段可以为Null,那么SQLite自动插入值,自动帮你处理它;但是如果表中所有字段都是Not Null,那么这个参数就不能为Null,必须提供一个可以为空的列名。
  • values(ContentsValues):

   public void addData(SQLiteDatabase db){
        ContentValues values = new ContentValues();
        values.put("name","xialin");
        values.put("gender","女");
        values.put("age",18);
        db.insert("user_db",null,values);
        db.close();
    }


或者:
        /**
         * 添加数据
         */
        addBtn.setOnClickListener(v -> {
            SQLiteDatabase db = dbHelper.getWritableDatabase();
            ContentValues values = new ContentValues();
            //插入第一条数据
            values.put("name", "Computer book");
            values.put("author", "Tom");
            values.put("pages", 443);
            values.put("price", 16.5);
            db.insert("book", null, values);


修改数据
  1. 还是需要一个ContentValues对象;
  2. 把要修改的内容放进values;
  3. 使用update()方法更新。

update的三个参数:

  • 第一个参数"user_db"是要更新的表的名称。
  • 第二个参数values是一个ContentValues对象,包含了要更新的列和值。
  • 第三个参数"name=?"是一个WHERE子句,用于指定哪些行应该被更新。问号?是一个占位符,用于后面的参数数组。
  • 第四个参数new String[]{"xialin"}是一个字符串数组,用于替换WHERE子句中的占位符。在这个例子中,它只有一个元素"xialin",这意味着只有name字段值为xialin的行会被更新。
   public void updateData(SQLiteDatabase db){
        ContentValues values = new ContentValues();
        //修改name=xialin的年龄
        values.put("age",21);
        db.update("user_db",values,"name=?",new String[]{"xialin"});
        db.close();
    }
删除数据

delete的三个参数:

  • 第一个参数"user_db"是要删除记录的表的名称。
  • 第二个参数"name=?"是一个WHERE子句,用于指定哪些行应该被删除。问号?是一个占位符,用于后面的参数数组。
  • 第三个参数new String[]{"xialin"}是一个字符串数组,用于替换WHERE子句中的占位符。在这个例子中,它只有一个元素"xialin",这意味着只有name字段值为xialin的行会被删除。
 public void deleteData(SQLiteDatabase db){
        db.delete("user_db","name=?",new String[]{"xialin"});
        db.close();
    }
查询数据

两种方式:

  • 使用query方法
  • 使用原始的SQL查询语句配合rawQuery方法来完成
  • 两种方式都需要遍历返回的Cursor对象来获取查询结果
使用query方法

query方法的参数:

  • columns数组定义了要查询的列,这里查询nameage两列。
  • selection字符串定义了查询条件,使用占位符?代替具体的值。
  • selectionArgs数组用于替换selection中的占位符,这里只有一个值"xialin"
  • query方法的第一个参数是表名,后面跟着的是列名、查询条件、查询条件的参数等。
  • 查询结果通过Cursor对象返回,你可以遍历这个Cursor对象来获取查询到的每一行数据。
  public Cursor queryData(SQLiteDatabase db) {
        // 定义要查询的列
        String[] columns = new String[]{"name", "age"};

        // 定义查询条件,这里查询name为xialin的记录
        String selection = "name=?";
        String[] selectionArgs = new String[]{"xialin"};

        // 使用query方法执行查询
        Cursor cursor = db.query("user_db", columns, selection, selectionArgs,
                null, null, null);

        // 返回查询结果的Cursor对象
        return cursor;
    }

使用rawQuery方法

rawQuery的参数:

  • sql字符串包含了原始的SQL查询语句。
  • selectionArgs数组用于替换SQL语句中的占位符。
  • rawQuery方法接受SQL语句和占位符对应的值作为参数,并返回查询结果的Cursor对象。
public Cursor rawQueryData(SQLiteDatabase db) {  
    // 定义原始的SQL查询语句,其中?是占位符  
    String sql = "SELECT name, age FROM user_db WHERE name=?";  
  
    // 创建包含占位符对应值的数组  
    String[] selectionArgs = new String[]{"xialin"};  
  
    // 使用rawQuery方法执行查询  
    Cursor cursor = db.rawQuery(sql, selectionArgs);  
  
    // 返回查询结果的Cursor对象  
    return cursor;  
}
遍历Cursor对象

getWritableDatabase():

  • SQLiteOpenHelper 类中的一个方法,用于获取一个可写的数据库对象。这个方法会尝试打开一个数据库。用于插入、删除、修改。

getReadableDatabase():

  • 尝试打开一个可写的数据库连接,用于读取数据库文件。
 queryBtn.setOnClickListener(v -> {
            SQLiteDatabase db = dbHelper.getWritableDatabase();
            //查询所有数据
            Cursor cursor = db.query("book", null, null, null,
                    null, null, null);

            try {
                if (cursor!=null&&cursor.moveToFirst()){
                    do {
                        @SuppressLint("Range")
     String name = cursor.getString(cursor.getColumnIndex("name"));
     String name1 = getString(cursor.getColumnIndexOrThrow("name"));
                        @SuppressLint("Range")
     String author = cursor.getString(cursor.getColumnIndex("author"));
                        @SuppressLint("Range")
     int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                        @SuppressLint("Range")
     double price = cursor.getDouble(cursor.getColumnIndex("price"));
      Log. e("queryTagMsg:","book name is"+name);
                        .......


                    } while (cursor.moveToNext());
                }
                cursor.moveToNext();
            } catch (IllegalArgumentException e) {
                throw new RuntimeException(e);
            } finally {
                if (cursor!=null){
                    cursor.close();
                }
            }
        });

注意:

一、cursor.getColumnIndex(“name”)和cursor.getColumnIndexOrThrow(“name”);

  • 前者需要搭配@SuppressLint("Range")阻止报错警告,因为本身可以正常运行。
  • 两者都可以用来从 Cursor 中获取 “name” 列的值,但 getColumnIndexOrThrow 提供了更直接的错误处理机制(通过抛出异常),而 getColumnIndex 则允许你在稍后的点处理潜在的索引问题(如果发生的话)。使用哪个取决于你的具体需求和错误处理策略。–此处引用文心一言

6.5【Android中JSON数据以及解析】

  • 在安卓创建JSON文件并且读取json数据
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InputStreamReader reader;
        try {
            //getAssets读取asset文件夹下名为"test.json"的文件
            reader = new InputStreamReader(getAssets().open("test.json"));
            BufferedReader br = new BufferedReader(reader);
            String line = "";
            StringBuilder sb= new StringBuilder();
            while ((line = br.readLine())!=null){
                sb.append(line);
            }

            //JSONObject获取每个json数据,需要指定key
            JSONObject root = new JSONObject(sb.toString());
            String cat = (String) root.get("cat");
            Log.i("cat", cat);

            //如果JSON数据是一个数组,则需要使用JSONArray
            JSONArray array = root.getJSONArray("language");
            for (int i = 0; i < array.length(); i++) {

                //再使用getxxx方法获取里面的每一个JSON
                JSONObject job = array.getJSONObject(i);
                String id = job.getString("id");
                String name = job.getString("name");
                String gender = job.getString("gender");
                Log.i("id", id);
                Log.i("name", name);
                Log.i("gender", gender);
            }
        } catch (IOException | JSONException e) {
            throw new RuntimeException(e);
        }

7. ContentProvider

背景:

  • Android数据持久化技术:文件存储、SharedPreferences存储以及数据库存储,其所保存的数据都只能在当前应用程序中访问
  • 在Android开发中,有时也会访问其他应用程序的数据。实现这种跨程序共享数据的功能,Android系统提供了一个组件ContentProvider(内容提供者)
  • 为了观察程序中数据的变化,Android系统提供了一个内容观察者ContentObserver

7.1【ContentProvider概述】

  • ContentProvider(内容提供者)是不同应用程序之间进行数据共享的标准API。

  • 它以uri的形式对提供数据,允许其他应用操作本应用程序的数据,其他应用通过ContentResolver类提供的Uri操作指定的数据。

  • 它最重要的两个东西:数据模型与Uri

  • ContentResolver的工作原理:

在这里插入图片描述

A程序通过ContentProvider将数据暴露出来,供其他应用程序使用;

B程序通过ContentResolver接口操作暴露的数据;

总体:A程序通过ContentProvider将数据返回到ContentResolver,然后ContentResolver把数据返回到B程序。

比喻:

​ A仆人(A程序)用盘子(ContentProvider)装食物(数据),然后放到桌子上(暴露数据)。B(B程序)从桌子拿起食物享用(通过ContentResolver访问数据)。

​ 如果B想要改变食物(修改数据),它会告诉A仆人(通过ContentResolver向ContentProvider发出请求),A仆人根据B的要求改变食物(修改数据)。当不再需要盘子时,A仆人负责清洗和存放盘子(管理ContentProvider的生命周期和数据清理)。

数据模型

  • ContentProvider使用基于数据库模型的简单表格来提供需要共享的数据
  • 在该表格中,每一行表示一条记录,而每一列代表特定类型和含义的数据
  • 每一条数据记录都包含一个名为“__ID”的字段标识每条数据

Uri

  • ContentResolver提供一系列增、删、改、查的方法对数据进行操作,以Uri的形式对外提供数据。

  • 其实就是一个操作方法,没有太复杂的含义,直接拿来用即可。

  • Uri为ContentProvider中的数据建立了唯一标识符主要由三部分组成,scheme、authorities、path

  • 使用parse()方法解析Uri,比如Uri.parse(“tel:10086”)

    示例:

    content://com.example.mycontentprovider/person

    scheme:*content://*是一个标准的前缀

    authorities:com.example.mycontentprovider是authorities属性值,通常采用程序包名的方式命名。

    path:/person代表资源或者数据,可以改变。

7.2【创建ContentProvider】

在这里插入图片描述

  • 内容提供者创建完成后,AndroidStudio会自动AndroidManifest.xml中对内容提供者进行注册

示例代码:

  • 根据传入的Uri进行相关操作

public class MyContentProvider extends ContentProvider {
    public MyContentProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

7.3【ContentResolver】

  • B程序通过ContentResolver来对暴露的数据进行操作。

步骤:

  1. 通过parse()方法解析Uri

    Uri uri=Uri.parse("content://cn.itcast.
    mycontentprovider/person");
    
  2. 通过query()方法查询数据

在这里插入图片描述

参数:

  • uri:表示查询其他程序的数据需要的Uri
  • projection:表示要查询的内容
  • selection:表示设置查询的条件
  • selectionArgs:需要配合参数selection使用
  • sortOrder:表示查询的数据按照什么顺序进行排序
  1. 通过while()循环语句遍历查询到的数据
while(cursor.moveToNext()){
    String address = cursor.getString(0);
    long date = cursor.getLong(1);
    int type = cursor.getlnt(2);
}
  cursor.close();//关闭cursor

7.4【ContentObserver】

概述

  • 帮助应用程序实时监听ContentProvider共享的数据是否发生变化

  • 当ContentObserver观察到指定Uri代表的数据发生变化时,就会触发onChange()方法

在这里插入图片描述

创建ContentObserver

  • 在自定义的ContentProvider类里面定义该类即可。
 private class MyObserver extends ContentObserver {
        
        public MyObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
        }
    }

注册ContentObserver

示例

ContentResolver cr = getContentResolver();
        Uri uri  = Uri.parse("content://xxxx");
        cr.registerContentObserver(uri,true,new MyContentProvider.MyObserver(new Handler()));

取消注册ContentProvider

在这里插入图片描述


在这里插入图片描述

8. Broadcast

8.1【概述】

  • 作用:在Android系统中,广播是一种运用在组件之间传递消息的机制

  • BroadcastReceiver(广播接收者)是Android四大组件之一,接收并过滤广播中的消息

  • 广播接收者可以监听系统中的广播消息,实现在不同组件之间的通信

  • 广播机制使用了观察者模式,基于消息的发布–订阅模式

    • 消息发送者是广播机制中的广播发送者
    • 消息订阅者是广播机制中的广播接收者

8.2【广播机制实现流程】

  1. 开发者在AMS中注册广播接收者
  2. 广播发送者向AMS发生广播
  3. AMS寻找合适的接收者并且转发到相应的消息循环队列
  4. 执行消息循环时接收者获取到此广播

在这里插入图片描述

注意:广播发送者与广播接收者的执行是异步的:

发出去的广播不会关心有无接收者接收,也不确定接收者到底何时才能接收到。

8.3【广播的使用场景】

  • 同一App内同一组件的通信(能使用广播,但是增加性能开销和复杂性,现实开发这种场景不会用广播)
  • 同一App内不同组件的通信
  • 同一App内具有多个进程的不同组件的通信
  • 不同App的组件之间的通信
  • 特定情况下与不同App之间的通信

8.4【广播接收者】

作用

  • 为了监听系统或应用程序的广播事件,Android系统提供了BroadcastReceiver(广播接收者)组件。

创建广播接收者

  1. 两种注册方法:静态和动态注册

  2. 静态注册:在清单文件中注册

  3. 动态注册:

    public class MainActivity extends AppCompatActivity {
        MyReceiver receiver;
        @SuppressLint("NonConstantResourceId")
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            receiver = new MyReceiver();
            String action = "android.provider.Telephony.SMS.RECEIVED";
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(action);
            //注册广播
            registerReceiver(receiver,intentFilter);
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //Activity被销毁时,取消注册receiver
            unregisterReceiver(receiver);
        }
    
    

    广播接收器:

    public class MyReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // an Intent broadcast.
            throw new UnsupportedOperationException("Not yet implemented");
        }
          Log.e("DyTag","动态注册:收到广播了!");
    }
    
  4. 静态注册(Android 8.0后,静态注册已经接收不到广播)

    需要在清单文件中添加:

    <receiver
        android:name=".MyReceiverTest"
        android:enabled="true"
        android:exported="true">
    </receiver>
    

    注意:静态注册的方式则在Activity中设置action的必须要与的一样

8.5【自定义广播】

为什么要自定义广播?

  • 当系统提供的广播不能满足实际需求时,可以自定义广播,同时需要编写对应的广播接收者。

整体流程

  • 当自定义广播发送消息时,会储存到公共消息区中公共消息区中
  • 如存在对应的广播接收者,就会及时的接收这条信息

在这里插入图片描述

广播的类型

  • 两种广播类型
  1. 无序广播:发送效率高,但是无法被拦截,所有接收者都会收到该广播
  2. 有序广播:发送效率低,有先后顺序,可以被拦截

广播接收者优先级

  • 指的是有序广播。

  • 在动态注册注册广播时,可以使用如下方法设置优先级,值越大,优先级越高

  • 如果两个广播接收者的优先级相同,则先注册的广播接收者优先级高,,即先安装的程序优先接收

intentFilter.setPriority(1000);

9. Service

9.1【概述】

  • Service服务是安卓四大组件之一
  • 具有较长的时间运行特性,可以在后台长时间执行操作不提供用户界面的应用程序组件
  • 一般是Activity启动,但是不依赖于Activity

9.2【主要应用场景】

如:下载文件、播放音乐

  • 后台运行

    在后台长时间进行操作而不用提供界面信息,只有系统要回收内存资源时,才会被销毁,否则Service会一直在后台运行

  • 跨进程访问

    当Service被其他应用组件启动时,即使用户切换到其他应用服务仍将在后台继续运行。

    注意:

    1. Service总是在后台运行,其运行并不是在子线程中,而是在主线程中进行的
    2. 处理的耗时操作时开启子线程进行处理,否则出现ANR异常

9.3【创建服务】

  1. AS中,New->Service->Service;

  2. 或者自行创建java类继承Service类即可;

  3. 必须在清单文件中注册Service

    //类名
    //能否实例化
    //能否被调用
    <service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true" />
    

9.4【服务的生命周期】

  • 不同启动方式的生命周期略有差异

在这里插入图片描述

9.5【服务的启动方式】

startService()启动

  • 通过startService()方法启动的服务,会长期在后台运行
  • 启动服务的组件(如:Activity)与服务之间没有关联,即使启动服务的组件被销毁,服务依旧会运行

示例

//可以搞几个按钮试试
@SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.startService:
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.stopService:
                Intent intent1 = new Intent(this,MyService.class);
                stopService(intent1);
                break;
            default:break;
        }
    }
public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("Service~~~", "启动!!!");
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("Service~~~", "onStartCommand: 来力");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("Service~~~", "卸载!!!: ");
    }
}

bindService()启动

  • 这种启动方式需要与Activity建立连接(connection)进行通信

  • 通过bindService()方法启动服务时,服务会与组件绑定当调用onUnbind()方法时,这个服务就会被销毁

  • 绑定服务(调用onBind方法)时,我们可以进行一下操作,如调用一个方法sum()进行求和等等。

  • bindService(a,b,c)方法的三个参数

    a=指定启动的Service b=用于监听调用者与Servicei之间的连接状态

    c=用于指定绑定时是否自动创建Service

     if (myConn == null) {
              myConn = new MyConn();
      }
       intent intent = new Intent(this, MyService2.class);
                    bindService(intent, myConn, BIND_AUTO_CREATE);
    

    BIND_AUTO_CREATE的含义是一旦Activity与Service建立连接,Service就会被自动创建出来

9.6【服务的通信】

本地服务通信

  • 本地服务通信是指应用程序内部的通信需要使用IBinder对象进行本地服务通信

在这里插入图片描述

远程服务通信

  • 远程服务通信是指两个应用程序之间的通信,远程服务通信是通过AIDL实现的。

  • AIDL定义接口的源代码必须以.aidl结尾

  • AIDL接口中用到的数据类型,除了基本数据类型String、List、Map、CharSequence之外,其他类型全部都需要导入包,即使它们在同一个包中

进行处理,否则出现ANR异常

9.3【创建服务】

  1. AS中,New->Service->Service;

  2. 或者自行创建java类继承Service类即可;

  3. 必须在清单文件中注册Service

    //类名
    //能否实例化
    //能否被调用
    <service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true" />
    

9.4【服务的生命周期】

  • 不同启动方式的生命周期略有差异

在这里插入图片描述

9.5【服务的启动方式】

startService()启动

  • 通过startService()方法启动的服务,会长期在后台运行
  • 启动服务的组件(如:Activity)与服务之间没有关联,即使启动服务的组件被销毁,服务依旧会运行

示例

//可以搞几个按钮试试
@SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.startService:
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.stopService:
                Intent intent1 = new Intent(this,MyService.class);
                stopService(intent1);
                break;
            default:break;
        }
    }
public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("Service~~~", "启动!!!");
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("Service~~~", "onStartCommand: 来力");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("Service~~~", "卸载!!!: ");
    }
}

bindService()启动

  • 这种启动方式需要与Activity建立连接(connection)进行通信

  • 通过bindService()方法启动服务时,服务会与组件绑定当调用onUnbind()方法时,这个服务就会被销毁

  • 绑定服务(调用onBind方法)时,我们可以进行一下操作,如调用一个方法sum()进行求和等等。

  • bindService(a,b,c)方法的三个参数

    a=指定启动的Service
    b=用于监听调用者与Servicei之间的连接状态
    c=用于指定绑定时是否自动创建Service

     if (myConn == null) {
              myConn = new MyConn();
      }
       intent intent = new Intent(this, MyService2.class);
                    bindService(intent, myConn, BIND_AUTO_CREATE);
    

    BIND_AUTO_CREATE的含义是一旦Activity与Service建立连接,Service就会被自动创建出来

9.6【服务的通信】

本地服务通信

  • 本地服务通信是指应用程序内部的通信需要使用IBinder对象进行本地服务通信

在这里插入图片描述

远程服务通信

  • 远程服务通信是指两个应用程序之间的通信,远程服务通信是通过AIDL实现的。
    在这里插入图片描述

  • AIDL定义接口的源代码必须以.aidl结尾

  • AIDL接口中用到的数据类型,除了基本数据类型String、List、Map、CharSequence之外,其他类型全部都需要导入包,即使它们在同一个包中

  • 28
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值