Android 开发笔记整理,比较全_安卓开发笔记-程序员宅基地

技术标签: android  移动开发  

四层架构体系

  1. 第一层 Linux内核层

    安卓是 Linux 内核的,这一层为Android设备的各种硬件提供了底层的驱动,如显示驱动、音频驱动、照相机驱动、蓝牙驱动、Wi-Fi驱动、电源管理等。

  2. 第二层 系统运行库层

    这一层通过C/C++库来为安卓系统提供了主要的特性支持。如SQLite库提供了数据库的支持,OpenGL|ES库提供了3D绘图的支持,Webkit库提供了浏览器内核的支持等。

    这一层还有Android运行时库提供了一些核心库,能让开发者使用Java来写Android,Android运行时库中还包含了Dalvik虚拟机(5.0系统之后改为 ART运行环境 ,就是 Android Runtime ),它使得每一个Android应用都能运行在独立的进程当中,并且拥有一个自己的Dalvik虚拟机实例。相较于Java虚拟机,Dalvik是专门为移动设备定制的,针对手机内存、CPU性能有限等情况做了优化处理。

    Dalvik模式的特点: 运行内存大、占用空间小、加载慢、省电、兼容性好。Art相反。

  3. 第三层 应用框架层
    提供了构建应用程序时用的API, Android自带的一些核心应用就是用这些API,开发者也可以通过使用这些API来构建自己的应用程序。

  4. 第四层 应用层
    所有安装在手机上的应用程序都是属于这一层的。

manifest文件

整个应用的全局描述文件,作为一个前端的理解,manifest文件相当于 package.json的作用。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.project" android:versionCode="1"
				android:versionName="1.0">
// 权限和application是同级的				
<uses-permission android:name="android.permission.CAMERA" />
    
// 这里的android是设置app的名字,就是在手机桌面上的名字
<application android:label="@string/app_name" android:icon="@drawable/icon">
    // 这里的label设置的是活动的标题
     <activity android:name=".MyActivity" android:label="@string/app_name"> 
            <intent-filter>
              <action android:name="android.intent.action.MAIN" /> 
              <category android:name="android.intent.category.LAUNCHER" />
              </intent-filter>
           </activity>
       </application>
     
	    </manifest>

项目目录

主项目目录

/app 项目源代码

/build 它主要包含了一些在编译时自动生成的文件

/gradle 包含了gradle wrapper 的配置文件,使用gradle wrapper的方式不需要提前将gradle下载好,而是会自动根据本地的缓存情况决定是否需要联网下载gradle

/build.gradle 项目全局的gradle构建脚本

gradle.properties 全局的gradle配置文件,这里配置的属性会影响到项目中所有gradle编译脚本

/gradlew, /gradlew.bat 这两个文件是用来在命令行界面中执行gradle命令的,其中 gradlew 是在Linux或Mac系统中使用的,gradlew.bat 是在Windows系统中使用的

/local.properties 用于指定本机中的Android SDK路径,通常内容都是自动生成的,并不需要修改

/settings.gradle 用于指定项目中所有引入的模块。由于一般项目中就只有一个app模块,因此该文件中也就只引入了app这一个模块

app项目目录

/app/build 这个目录和外层的build目录类似,主要也是包含了一些在编译时自动生成的文件

/app/libs 如果你的项目中使用到了第三方jar包,就需要把这些jar包都放在libs目录下,放在这个目录下的jar包都会被自动添加到构建路径里去

/app/AndroidManifest.xml 这是你整个Android项目的配置文件,你在程序中定义的所有四大组件都需要在这个文件里注册,另外还可以在这个文件中给应用程序添加权限声明

/app/build.gradle 这是app模块的gradle构建脚本

/app/res/drawable*都是用来放图片

/app/res/mipmap* 都是用来放应用图标的

/app/res/values 用来放字符串、样式、颜色等配置的

/app/res/layout 文件夹是用来放布局文件

app/build.gradle 详解

// 首先第一行应用了一个插件,
// 一般有两种值可选:com.android.application 表示这是一个应用程序模块,
// com.android.library表示这是一个库模块
// 应用程序模块和库模块的最大区别在于,一个是可以直接运行的,一个只能作为代码库依附于别的应用程序模块来运行
apply plugin: 'com.android.application'
android {
    
    // 指定项目的编译版本
    compileSdkVersion 29

    // buildToolsVersion用于指定项目构建工具的版本
    buildToolsVersion "29.0.3"

    defaultConfig {
    

        // 指定项目的包名
        applicationId "com.example.myapplication1"

        // 指定项目最低兼容的Android系统版本
        minSdkVersion 28

        // targetSdkVersion指定的值表示你在该目标版本上已经做过了充分的测试,系统将会为你的应用程序启用一些最新的功能和特性
        targetSdkVersion 29

        // versionCode用于指定项目的版本号
        versionCode 1

        // versionName用于指定项目的版本名
        versionName "1.0.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    // 指定生成安装文件的相关配置
    buildTypes {
    
        // 指定生成正式版安装文件的配置
        release {
    
            // 是否对项目的代码进行混淆
            minifyEnabled true
            // proguardFiles用于指定混淆时使用的规则文件,这里指定了两个文件,
            // 第一个proguard-android.txt是在Android SDK目录下的,里面是所有项目通用的混淆规则,
            // 第二个proguard-rules.pro是在当前项目的根目录下的,里面可以编写当前项目特有的混淆规则
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    
    // Android Studio项目一共有3种依赖方式:本地依赖、库依赖和远程依赖
    // 添加一个库依赖  implementation project(':helper')
    // implementation fileTree是一个本地依赖声明,
    // 它表示将libs目录下所有.jar后缀的文件都添加到项目的构建路径当中
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    // 远程依赖声明,androidx.appcompat:appcompat:1.2.0就是一个标准的远程依赖库格式,
    // 其中androidx.appcompat是域名部分,用于和其他公司的库做区分;
    // appcompat是组名称,用于和同一个公司中不同的库做区分;
    // 1.2.0是版本号,用于和同一个库不同的版本做区分
    // 加上这句声明后,Gradle在构建项目时会首先检查一下本地是否已经有这个库的缓存,
    // 如果没有的话则会去自动联网下载,然后再添加到项目的构建路径当中
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.material:material:1.0.0'
    // testImplementation 是用于声明测试用例库
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

四大组件

  1. Activicy
    活动,标识一个具有用户界面的单一屏幕。例如,一个邮件应用程序可以包含一个活动用于显示新邮件列表,另一个活动用来编写邮件,再一个活动来阅读邮件。当应用程序拥有多个活动,其中一个会被标记为主活动,在app启动时显示。

  2. Service
    在主线程,运行在后台执行长时间操作的组件。举个例子,服务可以是用户在使用不同的程序时在后台播放音乐,或者在活动中通过网络获取数据但不阻塞用户交互。

  3. Broadcast
    广播接收器简单地响应从其他应用程序或者系统发来的广播消息。举个例子,应用程序可以发起广播来让其他应用程序知道一些数据已经被下载到设备,并且可以供他们使用。因此广播接收器会拦截这些通信并采取适当的行动。广播接收器是BroadcastReceiver类的一个子类,每个消息以Intent对象的形式来广播。

  4. Content Providers
    内容提供者组件通过请求从一个应用程序到另一个应用程序提供数据。这些请求由ContentResolver类的方法来处理。这些数据可以是存储在文件系统、数据库或者其他其他地方。内容提供者是ContentProvider类的子类,并实现一套标准的API,以便其他应用程序来执行事务


Intent

Android 应用程序架构包括组件(使用 Intent 通信)、资源(通常在用户界面上下文中使用)、
manifest(描述了应用程序组件等)、应用程序包(用于存储组件、资源和manifest内容),Android 使用 Intent 对象与 Activity、服务和广播接收器进行通信。

Intent 对象是 android.content.intent 类的实例,可以分为显式或隐式声明。
显式 Intent 明确指定了组件的名称,隐式 Intent 则不指定目标名称(组件名没有赋值)。
隐式 Intent 通常用于启动其他应用中的组件。
如果组件没有 Intent 过滤器,那就只能接受显式 Intent,有过滤器的组件则既能接收显式 Intent 也能接收隐式 Intent。Android 在过滤 Intent 时,会参考 Intent 对象的 ActionCatejoryDataType,而不会参考 Extra 和 Flag。

Android 提供了发送电子邮件的 Intent。你的app发送此 Intent 即可以激活标准邮件应用程序,或者也可以注册一个 Activity 来响应该 Intent 对象,从而使用该 Activity 取代标准的邮件应用程序这种面向组件的架构允许一个应用程序重用其他应用程序的组件,只要其他应用程序允许重用自身的组件。组件重用减少内存占用率对于有限存储空间的设备尤其重要。

当应用程序的任何组成部分需要执行,Android会启动一个进程并实例化该组成部分的 Java 对象。这就是为什么 Android 的应用程序没有单一入口点 (例如没有 C 程序中的 main函数)的原因。因为应用程序会根据需要使用实例化的组件并运行。

安卓基础

基础知识

  • 通常Android Studio项目一共有3种依赖方式:本地依赖、库依赖和远程依赖
  • 本地依赖可以对本地的Jar包或目录添加依赖关系,
  • 库依赖可以对项目中的库模块添加依赖关系,
  • 远程依赖则可以对jcenter库上的开源项目添加依赖关系
  • Android程序设计讲究逻辑和视图分离,一般不在活动中写view,而是在布局文件中编写界面然后在活动中引入
  • 在代码中通过 R.( string | drawable | layout | id ).name 可以获得该字符串的引用。
  • 在XML中通过@string/app_name可以获得该字符串的引用, layout文件里 @+id/id_name 这种语法 是定义一个id
  • app/build.gradle 比如说 Android 6.0 系统中引入了运行时权限这个功能,如果你将targetSdkVersion指定成23或者更高,那么系统就会为你的程序启用运行时权限功能
  • 如果应用程序中没有声明主活动,这个程序仍然可以正常安装,只是无法在启动器中看到或者打开这个程序。这种程序一般都是作为第三方服务供其他应用在内部进行调用的,如支付宝快捷支付服务

像素单位

  • dp(dip): device independent pixels(设备独立像素). 和设备硬件有关不同设备有不同的显示效果。一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。
  • px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。
  • pt: point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用;
  • sp: scaled pixels(放大像素). 主要用于字体显示, textView 的字号最好使用 sp 做单位,而且查看TextView的源码可知 Android 默认使用 sp 作为字号单位。

日志

Android中的日志工具类是Log(android.util.Log), 在logcat下边看日志,还可以根据标签过滤。不要使用System.out 打印日志!

  1. Log.v()。用于打印那些最为琐碎的、意义最小的日志信息。对应级别verbose,是Android日志里面级别最低的一种。

  2. Log.d()。用于打印一些调试信息,这些信息对你调试程序和分析问题应该是有帮助的。对应级别debug,比verbose高一级。

  3. Log.i()。用于打印一些比较重要的数据,这些数据应该是你非常想看到的、可以帮你分析用户行为数据。对应级别info,比debug高一级。

  4. Log.w()。用于打印一些警告信息,提示程序在这个地方可能会有潜在的风险,最好去修复一下这些出现警告的地方。对应级别warn,比info高一级。

  5. Log.e()。用于打印程序中的错误信息,比如程序进入到了catch语句当中。当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复。对应级别error,比warn高一级。

布局

基础

  1. android:gravity 可以指定布局的对齐方式,是左对齐还是水平对齐,还是上下左右居中。

  2. setVisibility 修改View的可见性,View可用性如下三种状态:View.VISIBLE 可见,View.INVISIBLE 不可见,但仍然占据可见时的大小和位置。View.GONE 不可见,且不占据空间。

  3. android:ems=“10” 设置TextView或EditText为10个字符的宽度,超出部分不显示。android:ellipsize="end" android:lines="3" 多行超出显示省略号。

LinearLayout

线性布局,就是按照vertical或者horizontal平铺的布局,新手必备。

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="2dp"
    tools:context=".Main4Activity">
	
	// android:layout_height为0,切android:layout_weight为1,
	//表示高度不是height决定,而是vertical方向 flex 1 的效果
     <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:orientation="horizontal"
        android:layout_weight="1" >

        <Button
            android:id="@+id/btn11"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="haha1" />

        <Button
            android:id="@+id/btn12"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="haha2" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:orientation="horizontal"
        android:layout_weight="2" >

        <Button
            android:id="@+id/btn13"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="haha11" />

        <Button
            android:id="@+id/btn14"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="haha22" />

    </LinearLayout>

</LinearLayout>

RelativeLayout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".RelativeLayoutDemoActivity">

    <!-- relative_btn1 对其父元素中的左边和Top  -->

    <Button
        android:id="@+id/relative_btn1"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="relative_btn1" />

    <!-- relative_btn1 对其父元素中的右边和Top  -->
    <Button
        android:id="@+id/relative_btn2"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:text="relative_btn2" />

    <!-- relative_btn3 控件在父元素中的中间位置  -->
    <Button
        android:id="@+id/relative_btn3"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:text="relative_btn3" />


    <!-- relative_btn4 相当于 relative_btn3 的上边 ,并且在 relative_btn3 的左边-->
    <Button
        android:id="@+id/relative_btn4"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_above="@id/relative_btn3"
        android:layout_toLeftOf="@id/relative_btn3"
        android:text="relative_btn4" />

    <!-- relative_btn5 相当于 relative_btn3 的上边 ,并且在 relative_btn3 的右边-->
    <Button
        android:id="@+id/relative_btn5"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_above="@id/relative_btn3"
        android:layout_toRightOf="@id/relative_btn3"
        android:text="relative_btn5" />

    <!-- relative_btn5 相当于 relative_btn3 的下边 ,并且在 relative_btn3 的右边-->
    <Button
        android:id="@+id/relative_btn6"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_below="@id/relative_btn3"
        android:layout_toRightOf="@id/relative_btn3"
        android:text="relative_btn6" />

    <!-- relative_btn7 相当于 relative_btn3 的下边 ,并且和relative_btn4 的左边对齐 -->
    <Button
        android:id="@+id/relative_btn7"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:layout_below="@id/relative_btn3"
        android:layout_alignLeft="@id/relative_btn4"
        android:text="relative_btn7" />

    <!-- relative_btn8 和 relative_btn3 的top 对齐-->
    <Button
        android:id="@+id/relative_btn8"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:background="@color/blue"
        android:layout_alignTop="@id/relative_btn3"
        android:text="b8" />


</RelativeLayout>

FrameLayout

又称作帧布局,它相比于前面两种布局就简单太多了,因此它的应用场景也少了很多。这种布局没有方便的定位方式,所有的控件都会默认摆放在布局的左上角。

ConstraintLayout

约束布局号称一层布局实现所有效果,约束布局 ConstraintLayout 是一个 ViewGroup,可以在Api9以上的Android系统使用它,它的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整小部件。嵌套布局是布局内的布局,会增加绘制界面所需的时间

添加依赖 app/build.gradle 中添加 implementation 'com.android.support.constraint:constraint-layout:1.1.3',由于约束布局并不是内置在系统SDK当中的,所以需要把完整的包路径写出来。


元素宽度100%

<Button
 android:layout_width="fill_parent"
 android:layout_marginStart="0dp"
 >

元素宽度怎么设置百分比

<Button 
  app:layout_constraintWidth_default="percent"
  app:layout_constraintWidth_percent="0.6" 
>

元素怎么左右居中,怎么垂直也居中

设置左右两个约束,在正中间那么设置4个约束。


margin-left 怎么做

如果是 marginLeft 是确定的就是 android:layout_marginStart="156dp"app:layout_constraintHorizontal_bias 可以实现左右margin是百分比的效果, 就是marginLeft/marginLeft + marginRight,如果是0.5代表水平居中, app:layout_constraintVertical_bias 原理相同。


一个按钮固定宽度,一个填满剩余

当多个控件除了头和尾之外彼此之间互相约束,他们之间就形成了一条链,由头和尾决定链的约束位置。

<Button
    android:id="@+id/button6"
    android:layout_width="140dp"
    android:layout_height="wrap_content"
    android:layout_marginTop="64dp"
    android:text="button6"
    app:layout_constraintEnd_toStartOf="@+id/button7"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintHorizontal_chainStyle="spread"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<Button
    android:id="@+id/button7"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginTop="64dp"
    android:layout_weight='1'
    android:text="button77"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.8"
    app:layout_constraintStart_toEndOf="@+id/button6"
    app:layout_constraintTop_toTopOf="parent" />

百分比布局

LinearLayout本身已经支持按比例指定控件的大小了,因此百分比布局只为Frame-Layout和RelativeLayout进行了功能扩展。

LinearLayout支持百分比不是直接写 50%这样,而是 android:layout_weight="1"

添加依赖 app/build.gradle 中添加 implementation 'com.android.support:percent:24.2.1'

CoordinatorLayout

此布局是一个调度者,中心指挥者,在它布局下的所有的直接子View可以通过Behavior来和CoordinatorLayout中心指挥者通信。中心指挥者就可以根据Behavior来为子View指定依赖的同级View、嵌套滑动、事件拦截。

Activity

它是一种可以包含用户界面的组件,主要用于和用户进行交互。Activity是一个Android应用程序组件,它提供一个屏幕,用户可以通过该屏幕进行交互以执行某些操作,例如拍照。每个活动都有一个窗口,用于绘制其用户界面。窗口通常填满屏幕也可能比屏幕小,并漂浮在其他窗口的顶部。

Android中的活动是可以层叠的。我们每启动一个新的活动,就会覆盖在原活动之上,然后点击Back键会销毁最上面的活动,下面的一个活动就会重新显示出来。

Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。在默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。

定义一个Activity

使用Activity时,在manifest中声明此Activity,并设置为主活动。如果没设置,无法在启动器中看到这应用程序,这种程序通常作为第三方服务供其它应用在内部进行调用。

<activity android:name=".FirstActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

java代码中的活动

// 基本活动定义
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.os.Bundle;

public class MainActivity2 extends AppCompatActivity {
    

    // 在Activity创建时被系统调用
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        // Bundle类型的数据与Map类型的数据相似,都是以key-value的形式存储数据的
        // savedInstanceState 也就是保存Activity的状态
        super.onCreate(savedInstanceState);

        // 加载布局文件
        setContentView(R.layout.activity_main2);
    }

    @Override
    protected void onStart() {
    
        Log.v("start", "act start");
        super.onStart();
    }
}

点击事件

import android.view.View;
import android.widget.Button;
import android.widget.Toast;

 Button button1 = findViewById(R.id.demo1);
    button1.setOnClickListener(new View.OnClickListener() {
    
        @Override
        public void onClick(View v) {
    
            Toast toast=Toast.makeText(getApplicationContext(), "默认的Toast", Toast.LENGTH_SHORT);
            toast.show();
        }
    });

活动跳转

// 跳转活动
Intent intent = new Intent(MainActivity.this,Main2Activity.class);
startActivity(intent);

生命周期

安卓活动周期Activity 的前台生命周期就是调用 onResume()到调用 onPause()之间的时间段。在这 段时间内,Activity 处于屏幕上的最前台,正处于跟用户交互的状态。当设备休眠或有新 Activity 启动时 调用 onPause()。

当用户按下返回键时,当前的 Activity 就被弹出栈(Activity 被销毁),前 一个 Activity 重新进入运行状态,恢复操作(之前的用户界面状态也会还原)。

Activity 的用户界面是基于视图(用户界面组件Widget)、ViewGroup(由相关视图构成的集合)和事件监听器(用来监听视图或 ViewGroup 事件的对象)的,android.widget包中包 括各种 View 的子类,如 Button等,android.widget包中包括了不同的ViewGroup的子类,如 LinearLayout。



      @Override
      public void onCreate(Bundle savedInstanceState)
      {
    
          super.onCreate(savedInstanceState);
          LinearLayout layout = new LinearLayout(this);
          final EditText et = new EditText(this);
          et.setEms(10);
          layout.addView(et);
          Button btnOK = new Button(this);
          btnOK.setText("OK");
          layout.addView(btnOK);
          View.OnClickListener ocl;
          ocl = new View.OnClickListener()
          {
    
              @Override
              public void onClick(View v)
              {
    
                  Toast.makeText(class.this, et.getText(),
                                Toast.LENGTH_SHORT).show();
} };
          btnOK.setOnClickListener(ocl);
          setContentView(layout);
      }


活动状态

  1. 运行状态当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验

  2. 暂停状态当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域,你很快就会在后面看到这种活动。处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。

  3. 停止状态当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。

  4. 销毁状态当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。

活动被回收

应用中有一个活动A,用户在活动A的基础上启动了活动B,活动A就进入了停止状态,这个时候由于系统内存不足,将活动A回收掉了,然后用户按下Back键返回活动A,会出现什么情况呢?
其实还是会正常显示活动A的,只不过这时并不会执行onRestart()方法,而是会执行活动A的onCreate()方法,因为活动A在这种情况下会被重新创建一次。

生命周期方法

关于 savedInstanceState,来保存状态

    // savedInstanceState 是保存的状态,这里可以直接拿过来
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        // savedInstanceState 也就是保存Activity的状态
        super.onCreate(savedInstanceState);
        // 加载布局文件
        setContentView(R.layout.activity_main2);
    }

    // 在这个活动结束之前,会调用次方法,来保存状态
    @Override
    public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
    
        super.onSaveInstanceState(outState, outPersistentState);
    }

启动模式

启动模式分别是standard、singleTop、singleTask和singleInstance,可以在AndroidManifest.xml中通过给标签指定android:launchMode属性来选择启动模式

  1. standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。

  2. singleTop 在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例

  3. singleTask 每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例

  4. SingleInstance 这种模式是最特殊的模式,这种模式是为了让不同的app之间可以共享同一个活动,如果你的app想让别的app调用你的某一个界面,就可以用这种模式,这种模式会为你想共享的界面单独创造出一个单独使用的返回栈,不会与别的返回栈共同使用

scheme跳转协议

通过注册scheme可在App页面内跳转,从H5页面跳转到App,从一个App跳转到另一个App

Fragment

使用Fragment能实现一个界面的多次使用,能加快效率。Fragment可以被认为是Activity界面的一个布局,其依赖于Activity,但是拥有自己的活动事件与生命周期。可以通过替换Activity中的Fragment实现界面的优化处理。
Fragment相比Activity更节省内存,UI切换更舒适。

Activity 和 Fragment的区别

Activity里面可以没有 Fragment,但是Fragment不能脱离Activity而存在。

Activity 间接继承 Context,Fragment 继承Object。

设备屏幕一层只一个Activity, 但是Activity里面可以有多个Fragment。

Activity需要在AndroidManifest.xml中注册,可以作为接收intent的载体。使用Fragment不需要在AndroidManifest.xml文件中注册,Fragment也不能作为intent跳转的对象。

生命周期不同: Fragment相对Activity,增加了5个生命周期方法: onAttach():当fragment和activity建立关联的时候调用 onCreateView():为fragment创建视图时调用 onActivityCreated():与fragment相关联的activity已经创建完毕 onDestroyView():当与fragment关联的视图被移除的时候调用 onDetach():当fragment与activity解除关联时调用。

androidx

为了让Android兼容平板,3.0系统(API 11)中加入了Fragment功能。以前的老系统中也想使用这个功能该怎么办?于是Android团队推出了一个鼎鼎大名的 Android Support Library,用于提供向下兼容的功能。比如我们每个人都熟知的support-v4库,appcompat-v7库都是属于Android Support Library的。AndroidX本质上其实就是对Android Support Library进行的一次升级。

ViewBinding

  • 2019年Google I/O大会上公布的一款Android视图绑定工具:ViewBinding。使用方式类似DataBinding,但相比DataBinding,ViewBinding是一个更轻量级、更纯粹的findViewById的替代方案。它具有如下优点:

  • 类型安全: ViewBinding会基于布局中的View生成类型正确的属性。比如,在布局中放入了一个 TextView ,视图绑定就会暴露出一个 TextView 类型的属性供开发中使用。

  • 空安全:ViewBinding会检测某个视图是否只在某些配置下存在,并依据结果生成带有 @Nullable 注解的属性,所以即使在多种配置下定义的布局文件,视图绑定依然能够保证空安全。

  • ViewBinding生成的绑定类是一个Java类,并且添加了Kotlin注解,可以很好地支持 Java 和 Kotlin 两种编程语言。

  • 项目开启,在build.gradle 下

android {
    
    ...
    buildFeatures {
    
        viewBinding = true
    }
}

  • 活动中使用
class MainActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        // 之前是 setContentView(R.layout.activity_main) 
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.textView1.text = "textView1"
    }
}

  • 访问布局文件中元素时,使用 binding.textView1

navigation跳转

首选,添加依赖。

implementation 'androidx.navigation:navigation-fragment:2.4.1'
implementation 'androidx.navigation:navigation-ui:2.4.1'

构成的三个要素。

  1. Navigation graph,一个包含所有导航相关信息的 XML。
  2. NavHostFragment,是一种特殊的Fragment,用于承载导航内容的容器。
  3. NavController,管理应用导航的对象,实现Fragment之间的跳转等操作。

Application

和Activity一样,都是Android框架的一个系统组件,activity是UI界面的抽象,而application是应用程序的抽象。

应用程序每次启动时,系统会为其创建一个application对象且只有一个(单例类),用来存储系统的一些信息,相当于一个容器。

启动application时,系统会创建一个PID(进程ID),所有的activity都在这个进程上运行,在application创建时会初始化一个全局变量,同一个应用的activity,都可以获取到这个变量,也就是说,某一个activity中改变了这个变量,其他activity里也会改变。

Service

Service(服务)是运行在后台的组件,其运行不限时间,且不提供用户界面,Service是运行在主线程中的,它必须创建另一个线程以执行耗时操作,例如服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进。服务有两种状态。使用service也需要在manifest中声明。

<service
   android:name=".MyService"
   android:enabled="true"
   android:exported="true">
</service>

Service 分为本地 Service远程 Service 两类:本地 Service 与应用程序其他部分运行在相同的进程中,这种 Service 很容易执行 后台任务。远程 Service 运行在独立的进程中,这种 Service 可以用来执行进程间的相互通信。

比如用户可利用 Activity 来选择播放歌曲,并启动一个服务来响应用户选择。服务会在另一个线程中进行音乐播放,防止应用程序出现 ANR(Application Not Responding)

在调用 stopService(Intent)stopSelf()stopSelfResult(int)方法之后,Android 会调用 onDestroy(),让 Service 执行清理任务


public class MyService extends Service {
    

    // onCreate()方法不会被反复调用
    @Override
    public void onCreate() {
    
        super.onCreate();
    }

    // intent 是传递给 startService(Intent)的 Intent 对象
    // flags 用于提供关于启动请求的其他信息,但通常设置为 0。
    // startId 是用于描述该启动请求的唯一整数编号,可以将这个值传递给Service的
    // boolean stopSelfResult(int startId)来将自己停止。
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
        return super.onStartCommand(intent, flags, startId);
    }

    // onDestroy()方法是一定会被调用的,来释放资源
    @Override
    public void onDestroy() {
    
        super.onDestroy();
    }

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

启动状态
当应用组件通过调用 startService 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。

// 启动service的代码
Intent intent = new Intent(MainActivity.this, MyService.class);
MainActivity.this.startService(intent);

绑定状态
当应用组件通过调用 bindService() 绑定到服务时,服务处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

远程service

远程Service是通过Context类的 boolean bindService(Intentservice, ServiceConnection conn, int flags) 方法来启动的,它会连接到一个正在运行的服务(如果需要,则创建该服务), 如果成功连接返回true。
调用 bindService(Intent, ServiceConnection, int) 会导致调用 onCreate()方法随后会调用 onBind(Intent)函数,该函数会返回一个用于跟 Service 交互的通信通道(实现 android.os.IBinder 接口的类的实例)。

// 服务
public class MyBinder extends Binder {
    
        public BindService getService() {
    
            return BindService.this;
        }
    }
    private MyBinder myBinder = new MyBinder();
    public void MyMethod(){
    
        Log.i("BindService", "BindService-->MyMethod()");
    }
    @Override
    public IBinder onBind(Intent intent) {
    
        // TODO: Return the communication channel to the service.
        return myBinder;
    }
}

// 活动
public class MainActivity extends AppCompatActivity {
    
    private ActivityMainBinding binding;
    private ServiceConnection conn = new ServiceConnection() {
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
            // TODO Auto-generated method stub
            BindService.MyBinder binder = (BindService.MyBinder)service;
            BindService bindService = binder.getService();
            bindService.MyMethod();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
    
            // TODO Auto-generated method stub
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.fab.setOnClickListener(new View.OnClickListener() {
    
            @Override
            public void onClick(View view) {
    
                Intent intent = new Intent(MainActivity.this, MyService.class);
                bindService(intent, conn, Context.BIND_AUTO_CREATE);
            }
        });
    }

    private void unBind(){
    
        unbindService(conn);
    }

}

Intent

Intent是Android程序中各组件之间进行交互的一种重要方式,
可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。
Intent一般可被用于启动活动、启动服务以及发送广播等场景

// 都是在活动的onCreate方法,传递数据
intent.putExtra("name", "intent-data1");

// 接收数据
Intent intent = getIntent();
String name = intent.getStringExtra("name");

返回上级 数据带回去

// 活动1 
// 接收返回结果
startActivityForResult(intent, 1);

// 活动1 重写方法
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    

  if(requestCode == 1){
    
      if(resultCode == RESULT_OK){
    
          String returnData = data.getStringExtra("data-return");
          Log.i("main", returnData);
      }
  }

  super.onActivityResult(requestCode, resultCode, data);
}

// 活动2

// 点击返回 和按一下Back键就可以销毁当前的活动一样
button2.setOnClickListener(new View.OnClickListener() {
    
    @Override
    public void onClick(View v) {
    
        Intent intent = new Intent();
        intent.putExtra("data-return", "data-return");
        setResult(RESULT_OK, intent);
        finish();
    }
});

// 重写back键
public void onBackPressed() {
    
   Intent intent = new Intent();
   intent.putExtra("data-return", "data-return");
   setResult(RESULT_OK, intent);
   finish();
   super.onBackPressed();
}

HTTP

http 是无状态的请求响应的协议,http2 使用二进制格式,而不是文本格式 多路复用 多重请求响应
HttpUrlConnect是java标准类,用于安卓网络请求。

控件

所有控件都是直接或间接继承自View的,所有布局都是直接或间接继承自ViewGroup的。

自定义控件,实现一个公用的头部。

// 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@color/blue"
    android:layout_width="match_parent"
    android:layout_height="36dp">

    <Button
        android:id="@+id/title_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:text="Back"
        android:textColor="#fff"
        android:textSize="14sp"
        android:background="@color/blue"/>

    <TextView
        android:id="@+id/title_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:gravity="center"
        android:text="Title Text"
        android:textColor="#fff"
        android:textSize="16sp"
        />

    <Button
        android:id="@+id/title_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:text="edit"
        android:textSize="14sp"
        android:textColor="#fff"
        android:background="@color/blue"/>

</LinearLayout>

// 布局主文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ConstraintLayoutDemo2Activity">

    <com.hua.huade001android.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="36dp">
    </com.hua.huade001android.TitleLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

// java代码
public class TitleLayout extends LinearLayout {
    
    public TitleLayout(Context context, @Nullable AttributeSet attrs) {
    
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.title, this);
        Button back = findViewById(R.id.title_back);
        back.setOnClickListener(new View.OnClickListener(){
    
            @Override
            public void onClick(View v) {
    
                ((Activity)getContext()).finish();
            }
        });
    }
}

Listview 缺点,性能不好,只能纵向滚动,所以以后要使用 RecyclerView
RecyclerView是Android 5.0推出的

广播

广播是一种可以跨进程的通信方式。

标准广播是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

有序广播是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。

接收系统广播

Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。如手机开机完成后会发出一条广播、电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播。

注册广播的方式一般有两种,在代码中注册被称为动态注册,缺点是必须要在程序启动之后才能接收到广播,在AndroidManifest.xml中注册被称为静态注册

简单使用

// 活动中发送广播
Intent intent = new Intent(SimpleActivity.this, SimpleBroadcastReceiver.class); 
intent.putExtra("message", "Hello, broadcast receiver!"); SimpleActivity.this.sendBroadcast(intent);

// 接受广播
public class SimpleBroadcastReceiver extends BroadcastReceiver {
    
	@Override
       public void onReceive(Context context, Intent intent){
    
          System.out.println("onReceive(Context, Intent) called");
	} 
}

动态系统广播实现

// 1 实现网络接收器 
// 网络接收器,每当网络变化,系统发送一个广播,都会执行onReceive方法
public class NetworkReceiver extends BroadcastReceiver {
    
    @Override
    public void onReceive(Context context, Intent intent) {
    
        Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
        boolean success = false;
        //获得网络连接服务
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);
        NetworkInfo info = connectivityManager.getActiveNetworkInfo();
        //获取wifi连接状态
        NetworkInfo.State state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
        //判断是否正在使用wifi网络
        if (state == NetworkInfo.State.CONNECTED) {
    
            Toast.makeText(context, "using wifi", Toast.LENGTH_SHORT).show();
            success = true;
        }
        //获取GPRS状态
        state = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
        //判断是否在使用GPRS网络
        if (state == NetworkInfo.State.CONNECTED) {
    
            success = true;
        }
        //如果没有连接成功
        if(!success){
    
            Toast.makeText(context,"当前网络无连接",Toast.LENGTH_SHORT).show();
        }

    }
}

// 2 在活动中使用
@Override
protected void onCreate(Bundle savedInstanceState) {
    
    super.onCreate(savedInstanceState);
     String type = "android.net.conn.CONNECTIVITY_CHANGE";
    intentFilter = new IntentFilter();
    intentFilter.addAction(type);

    networkReceiver = new NetworkReceiver();
    registerReceiver(networkReceiver, intentFilter);
}

 @Override
 protected void onDestroy() {
    
     super.onDestroy();
     // 销毁活动,要结束广播接收
     unregisterReceiver(networkReceiver);
 }
     

静态广播

在 android studio 中 新建一个广播,勾选 exported 和 enabled ,Exported属性表示是否允许这个广播接收器接收本程序以外的广播,Enabled属性表示是否启用这个广播接收器,并会自动在manifest文件中添加声明。

<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">
    <receiver
        android:name=".broadcast.BootReceiver"
        android:enabled="true"
        android:exported="true"></receiver>
</application>

自定义发送标准广播

// 1 创建接收者
public class MyReceiver extends BroadcastReceiver {
    
    @Override
    public void onReceive(Context context, Intent intent) {
    
        Toast.makeText(context, "MyReceiver get", Toast.LENGTH_SHORT).show();
    }
}

// 2 配置到 android manifest
<receiver
   android:name=".broadcast.MyReceiver"
   android:enabled="true"
   android:exported="true">
   <intent-filter>
       <action android:name="com.tecent.chat.MY_BROADCAST" />
   </intent-filter>
</receiver>

// 3. 活动中,加一个按钮点击事件,触发广播
// 点击按钮,发送广播
Button btn = findViewById(R.id.btn_b);
btn.setOnClickListener(new View.OnClickListener(){
    
  @Override
  public void onClick(View v) {
    
      Intent intent = new Intent("com.tecent.chat.MY_BROADCAST");
      sendBroadcast(intent);
  }
});

发送有序广播

sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是Intent,第二个参数是一个与权限相关的字符串,这里传入null就行了
为了处理顺序,可以加优先级字段

<receiver
    android:name=".broadcast.MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter android:priority="100">
        <action android:name="com.tecent.chat.MY_BROADCAST" />
    </intent-filter>
</receiver>

本地广播

导包 implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

1.发送本地广播比发送系统全局广播将会更加高效
2.本地广播无法静态广播
3.本地广播不会数据泄露安全问题

数据持久化

文件存储

Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。方法接收两个参数,
第一个是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data//files/目录下的。

第二个参数是文件的操作模式,主要有两种模式可选,MODE_PRIVATE和MODE_APPEND。其中MODE_PRIVATE是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,而MODE_APPEND则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件

// 保存数据到本地文件
public void save(){
    
    FileOutputStream out = null;
    BufferedWriter writer = null;
    try {
    
        out = openFileOutput("data", Context.MODE_PRIVATE);
        writer = new BufferedWriter(new OutputStreamWriter(out));
        writer.write("huahuadavids");

    } catch (FileNotFoundException e) {
    
        e.printStackTrace();
    } catch (IOException e) {
    
        e.printStackTrace();
    } finally {
    
        if(writer != null){
    
            try {
    
                writer.close();
                Log.v("main", "write end");
            } catch (IOException e) {
    
                e.printStackTrace();
            }
        }
    }
}

SharedPreferences存储

SharedPreferences是使用键值对的方式来存储数据的
SharedPreferences文件都是存放在/data/data//shared_prefs/目录下的

// 将数据存储到SharedPreferences
public void save1(){
    
   // getShared-Preferences()方法指定SharedPreferences的文件名为data,
   // 生产文件的路径为  /shared_prefs/data.xml
   // 并得到了SharedPreferences.Editor对象
   SharedPreferences.Editor editor = getSharedPreferences("data", 0).edit();
   editor.putString("name", "huahuadavids");
   editor.putInt("age", 34);
   editor.putBoolean("newbee", true);
   editor.apply();
   Toast.makeText(LoginActivity.this, "success!!", Toast.LENGTH_SHORT).show();
}

// 从SharedPreferences中取数据
public void get1(){
    
   SharedPreferences share = getSharedPreferences("data", 0);
   String name = share.getString("name", "");
   Log.v("main", name);
}

SQLite数据库存储

SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。Android 运行时环境包含了完整的 SQLite。JDBC 会消耗太多的系统资源,所以 JDBC 对于手机这种内存受限设备来说不合适。数据库文件会存放在/data/data/package name/databases/目录下。

SQLiteOpenHelper是一个抽象类,使用它需要创建一个自己的帮助类去继承它。有两个抽象方法onCreate()和onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。

getReadableDatabase()和getWritableDatabase()。这两个实例方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则将出现异常。

可以直接使用 execSQLrawQuery 方法执行sql

public class DatabaseHelper extends SQLiteOpenHelper {
    

  //创建数据库sql语句 并 执行
  public static final String sql = "create table user(id integer primary key, name text)";

  private Context mcontext;

  //带全部参数的构造函数,此构造函数必不可少
  public DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
    
      super(context, name, factory, version);
      mcontext = context;
  }
  
  @Override
  public void onCreate(SQLiteDatabase db) {
    
      db.execSQL(sql);
      Toast.makeText(mcontext, "create success!!", Toast.LENGTH_SHORT).show();
  }

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

public class DemoActivity extends BaseActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
		dbHelper = new DatabaseHelper(this, "db-hua", null, 1);

        // 多次点,不会重复提示,数据库创建成功,因为oncreate只会执行一次
        sql.setOnClickListener(new View.OnClickListener() {
    
            @Override
            public void onClick(View v) {
    
                // 返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了。
                SQLiteDatabase db = dbHelper.getWritableDatabase();
      			ContentValues val = new ContentValues();
                val.put("id", 1);
                val.put("name", "nina");
                db.insert("user", null, val);
                val.clear();
                
                val.put("id", 2);
                val.put("name", "tina");
                db.insert("user", null, val);
                Toast.makeText(LoginActivity.this, "插入数据成功", Toast.LENGTH_SHORT).show();


				// 更新数据 
				ContentValues val = new ContentValues();
                val.put("name", "sasa");
                db.update("user", val, "id = ?", new String[]{
    "1"});
				// 删除数据
				db.delete("user", "id = ?", new String[]{
    "1"});

				// 查询数据
			    Cursor cursor = db.rawQuery("select * from user", null);
                if(cursor.moveToFirst()){
    
                    do {
    
                        String name = cursor.getString(cursor.getColumnIndex("name"));
                        Log.v("main", name);
                    }while (cursor.moveToNext());
                }
                cursor.close();
            }
        });
        
    }
}

LitePal

是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式

dependencies {
    
    implementation 'org.litepal.guolindev:core:3.2.1'
}

权限

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ptrprograms.androidpermissionsdemo" >
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />


public class MainActivity extends Activity {
    

    private final int REQUEST_CODE_SOME_FEATURES_PERMISSIONS = 0;
    private final int REQUEST_CODE_ALL = 42;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int hasLocationPermission = checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION );
        int hasSMSPermission = checkSelfPermission( Manifest.permission.SEND_SMS );
        List<String> permissions = new ArrayList<String>();
        if( hasLocationPermission != PackageManager.PERMISSION_GRANTED ) {
    
            permissions.add( Manifest.permission.ACCESS_FINE_LOCATION );
        }

        if( hasSMSPermission != PackageManager.PERMISSION_GRANTED ) {
    
            permissions.add( Manifest.permission.SEND_SMS );
        }

        if( !permissions.isEmpty() ) {
    
            requestPermissions( permissions.toArray( new String[permissions.size()] ), REQUEST_CODE_SOME_FEATURES_PERMISSIONS );
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    
        switch ( requestCode ) {
    
            case REQUEST_CODE_SOME_FEATURES_PERMISSIONS: {
    
                for( int i = 0; i < permissions.length; i++ ) {
    
                    if( grantResults[i] == PackageManager.PERMISSION_GRANTED ) {
    
                        Log.d( "Permissions", "Permission Granted: " + permissions[i] );
                    } else if( grantResults[i] == PackageManager.PERMISSION_DENIED ) {
    
                        Log.d( "Permissions", "Permission Denied: " + permissions[i] );
                    }
                }
            }
            break;
            default: {
    
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }

}

运行时权限

public class contentActivity extends AppCompatActivity {
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content);
        Button btn1 = findViewById(R.id.rper);
        bindClick1(btn1);
    }

    public void bindClick1(Button btn) {
    
        btn.setOnClickListener(new View.OnClickListener() {
    
            @Override
            public void onClick(View v) {
    
                if (ContextCompat.checkSelfPermission(contentActivity.this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
    
                    call();
                }else {
    
                    ActivityCompat.requestPermissions(contentActivity.this, new String[]{
    
                            Manifest.permission.CALL_PHONE
                    }, 1);
                }
            }
        });
    }

    private void call() {
    
        Toast.makeText(contentActivity.this, "xiaohua", Toast.LENGTH_SHORT).show();
        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel:10086"));
        startActivity(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode){
    
            case 1:
               if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    
                    call();
               }else {
    
                   Toast.makeText(contentActivity.this, "您拒绝了权限哦", Toast.LENGTH_SHORT).show();
               }
        }
    }
}

内容提供器

内容提供器用于不同app之间实现数据共享,允许一个程序访问另一个的数据,还能保证被访数据的安全性。数据可以存储在 Android 的文件系统、SQLite 数据库中或其他方式进行存储。

使用内容提供器是Android实现跨程序共享数据的标准方式。只需要获取到该应用程序的内容URI,然后借助ContentResolver进行CRUD操作就可以了。

一个标准的URI,比如 content://com.demo.app.provider/table1
content://com.demo.app.provider/* 匹配任意表
content://com.demo.app.provider/table1/# 匹配任意表任意一行数据

Bundle

Bundle类型的数据与Map类型的数据相似,都是以key-value的形式存储数据的

Bundle data = new Bundle();
data.putString("Data", "data from TestBundle");
    
// bundle 放入 intent
Intent intent = new Intent();
intent.setClass(TestBundle.this, Target.class);
intent.putExtras(mBundle);

主题

主题(Theme)就是一种应用到整个应用程序或某个 Activity 的外观风格,在没有任何自定义时,Android 应用程序的 Activity 会加载默认的系统主题,application和activity的android:theme="THEME_NAME" 属性,都可以指定主题。

自定义主题

在 res/values/styles.xml 文件中,声明两个主题

<resources>
   <style name="MyTheme.One" parent="@android:style/Theme">
       <item name="android:windowNoTitle">true</item>
       <item name="android:windowContentOverlay">@null</item>
   </style>
   <style name="MyTheme.Two" parent="@android:style/Theme">
       <item name="android:windowBackground">@drawable/window_bg</item>
       <item name="android:windowFullscreen">true</item>
   </style>
</resources>

工具库

  • ButterKnife
    是一个专注于Android系统的View注入框架,以前总是要写很多findViewById来找到View对象,有了ButterKnife可以很轻松的省去这些步骤。
    使用ButterKnife对性能基本没有损失,因为ButterKnife用到的注解并不是在运行时反射的,而是在编译的时候生成新的class。

常用术语


SDK

SDK (Software Development Kit),Android SDK 就是 Android 专属的软件开发工具包。

AVD

android virtual device manager 安卓虚拟设备管理器

NDK

NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。NDK的发布,使“Java+C”的开发方式成为官方支持的。使用NDK,我们可以将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。使用NDK,我们可以将需要保密的应用逻辑使用C开发。毕竟,Java包都是可以反编译的。

ADB

Android Debug Bridge,是 Android SDK 里的一个是一个多功能命令行工具,就是一个调试桥,用这个工具可直接操作管理Android模拟器、虚拟机或者真实的Android设备。

它是一个客户端 - 服务器程序,包括三个组件:

  1. 客户端:您可以通过发出adb命令从shell调用客户端。 其他Android工具(如ADT插件和DDMS)也可以创建adb客户端。
  2. 服务器:在开发计算机上作为后台进程运行。 服务器管理客户端与在仿真器或设备上运行的adb守护程序之间的通信。
  3. 后台程序:在每个模拟器或设备实例上作为后台进程运行。

主要作用有:

  1. 在设备上运行Shell命令; 将本地APK软件安装至模拟器或Android设备;
  2. 管理设备或手机模拟器上的预定端口;
  3. 在设备或手机模拟器上复制或粘贴文件

Android studio

  • Android studio 版本差异很大。
  • 打印log是Log.d(“tag”, 1);
  • 一个项目可有多个模块,可以导入或者删除,运行app是运行一个模块。
  • APK文件基于ZIP文件格式,并具有与JAR文件类似的结构。Apk文件可以解压,得到class文件,反编译。

参考资料

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_29334605/article/details/105854425

智能推荐

【Java程序设计】【C00799】基于(JavaWeb)Springboot的善筹网(众筹)管理系统(含论文+PPT)-程序员宅基地

文章浏览阅读388次,点赞5次,收藏3次。运行环境:推荐jdk1.8;开发工具:eclipse以及idea(推荐)、maven;操作系统:windows 10 8G内存以上(其他windows以及macOS支持,但不推荐);浏览器:Firefox(推荐)、Google Chrome(推荐)、Edge;数据库:MySQL8.0(推荐)及其他版本(支持,但容易异常尤其MySQL5.7(不含)以下版本);数据库可视化工具:Navicat Premium 15(推荐)以及其他Navicat版本是否maven项目:是。

浅析企业级SSD Multi-Stream Write技术_multistream 技术-程序员宅基地

文章浏览阅读3.1k次。Multi-stream write(多流写)技术是什么?在企业级存储中经常提到一个关键性的词语"冷热数据分区"。比如Meta data肯定是热数据。把相同生命周期的数据打上标签,告诉SSD盘,SSD盘放置数据的时候,就可以分区存放,会大大减少垃圾回收的工作量。要实现这个功能,阵列软件必须有接口和SSD控制器通讯。除了自研SSD之外,还有一个办法,采用支持Multi-stream writ_multistream 技术

免费获取百度文库的文章(厌恶积分下载)_百度文库免费获取文章-程序员宅基地

文章浏览阅读924次。大家可以下载这个软件,冰点百度文库资源下载器v1.7绿免版 http://www.33lc.com/soft/13055.html他会把SWF自动生成PDF文件。_百度文库免费获取文章

通信原理学习笔记3-3:数字通信系统概述(数字调制、IQ调制与PSK / QAM)_如何生成qam基带-程序员宅基地

文章浏览阅读4.7k次,点赞9次,收藏68次。注意,从星座图上可以看出,调制阶数增加,星座点间距变小,抗干扰能力变差,要求更高的信道质量。下面以QPSK为例介绍(Q代表4元调制,即每次输入2个比特,对应4种不同的波形),也即包含了「输入的比特数据、载波幅度相位、调制所需的IQ数据」三者的映射关系。根据星座图,显然信号受干扰后,接收端误判为相邻星座点的概率更大,因此一般结合。例如,QPSK,调制阶数为4,有4种可能的码元,即4种不同相位的余弦波。数字调制思路与模拟调制相同,就是用要传输的数字信号,来控制。上面说过,数字调制的核心就是IQ调制;_如何生成qam基带

【文件加密】文件怎么加密?4种文件加密方法总结_压缩类软件的加密功能-程序员宅基地

文章浏览阅读792次,点赞21次,收藏16次。文件怎么加密?4种文件加密方法总结_压缩类软件的加密功能

[源码和文档分享]基于C++的通讯录系统的设计与实现-程序员宅基地

文章浏览阅读59次。一 需求分析 通讯录系统可帮助使用者管理归纳通讯录名单,达到添加,删除,修改,保存等需求。 二 系统设计 2.1功能模块设计 通讯录主要功能为:添加通讯录成员,修改成员,删除成员,按需求搜索查看成员,保存为文档。 如下图所示: ​ 源码下载地..._基于c++的通讯录管理系统的设计与实现

随便推点

零基础到GPT高手:快速学习与利用ChatGPT的完全指南_chatgpt从入门到精通-程序员宅基地

文章浏览阅读572次,点赞5次,收藏5次。进入人工智能时代,令人惊叹的ChatGPT技术正在引爆全球!!!您是否想象过能够与智能语言模型对话,提升工作效率、解锁创意,甚至实现商业化变现?在本篇文章中,我将向你揭示ChatGPT的原理、学习技巧,并展示如何利用ChatGPT提升工作效率和实现商业变现。无论是初学者还是有经验的开发者,都将为你提供宝贵的信息和资源,助你成为一名ChatGPT高手。一起探索ChatGPT的魅力,让ChatGPT成为技能发展和事业成功的引擎!_chatgpt从入门到精通

vue2适配手机端_vue2 移动端适配-程序员宅基地

文章浏览阅读1.4k次。vue2适配手机端,将px自动转为rem_vue2 移动端适配

爬虫笔记.-程序员宅基地

文章浏览阅读1.2k次。浏览器分析页面中的内容发现其中引用了很多文件,包括image js文件等,所以浏览器会再次发送Request去获取这些图片 js文件、Get 和Post方法在路由选择中,默认选择的为get 指定post方式如何指定,两种均可如何指定;ip地址(http://180.97.33.107)—为每一台计算机提供一个编号,非常不容易记忆和理解。域名和ip地址之间的对应关系表—DNS服务器,完成ip地址和域名之间的映射。当所有的文件下载完成,浏览器根据html的语法结构,将网页完整的显示出来。

第二章 openblock-gui实现用户登录1(切换登录与未登录状态)_scratch-gui二次开发 登录-程序员宅基地

文章浏览阅读633次。第二章 openblock-gui实现用户登录 1前言代码编写前言接下来几章将带大家实现实现用户登录。这里要求大家必须掌握redux与react-redux,否则你将看到云里雾里,考虑到部分人可能没有学习过React、redux与react-redux,这里建议这部分人先别往下,前去bilibili学习下React相关知识:尚硅谷2021版React技术全家桶全套完整版(零基础入门到精通/男神天禹老师亲授),学完再继续。代码编写创建一个user-state的reducer,在src->redu_scratch-gui二次开发 登录

UEditor自定义请求参数_ueditor 请求携带参数-程序员宅基地

文章浏览阅读2.6k次。自定义请求参数很多情境下,编辑器与后台通信需要有登录状态,很多时候后台需要额外的参数。 UEditor 自1.4.0版本提供设置额外参数的命令serverparam命令,可动态设置自定义参数表。 在向后台发出请求时,会把参数表以GET方式加到请求里。另外,编辑器上传使用webuploader插件,在低版本的ie下,浏览器使用Flash形式的上传。 flash发送的请求不带有cookie,这..._ueditor 请求携带参数

MySql总结-程序员宅基地

文章浏览阅读576次,点赞23次,收藏5次。顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where 子句中使用最频繁的一列放在最左边。最左前缀匹配原则,非常重要的原则,MySQL 会一直向右匹配直到遇到范围查询(>、 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d 是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d 的顺序可以任意调整。

推荐文章

热门文章

相关标签