Android学习笔记4

简介: Android学习笔记4

90101cdc719da704740d0a7bac5b9f33.png90101cdc719da704740d0a7bac5b9f33.png系统广播

接收分钟到达广播


Intent.ACTION_TIME_TICK


定义一个分钟广播的接收器,并重写接收器的onReceive方法,补充收到广播之后的处理逻 辑。


重写活动页面的onStart方法,添加广播接收器的注册代码,注意要让接收器过滤分钟到达广播 Intent.ACTION_TIME_TICK


重写活动页面的onStop方法,添加广播接收器的注销代码

接收网络变更广播

步骤一,定义一个网络广播的接收器,并重写接收器的onReceive方法,补充收到广播之后的处理逻 辑。


步骤二,重写活动页面的onStart方法,添加广播接收器的注册代码,注意要让接收器过滤网络变更广播 android.net.conn.CONNECTIVITY_CHANGE。


步骤三,重写活动页面的onStop方法,添加广播接收器的注销代码

详细的网络信息

getType:获取网络类型

getTypeName:获取网络类型的名称


getSubtype:获取网络子类型。当网络类型为数据连接时,子类型为2G/3G/4G的细分类型,如 CDMA、EVDO、HSDPA、LTE等

getSubtypeName:获取网络子类型的名称。


getState:获取网络状态

定时管理器

系统闹钟定时发送广播,常见的方法:

  • set:设置一次性定时器。
  • setAndAllowWhileIdle:设置一次性定时器,即使设备处于空闲状态,也会保证执行定时器。
  • setRepeating:设置重复定时器,但系统不保证按时发送广播。
  • cancel:取消指定延迟意图的定时器。

延迟意图



定时管理器使用了PendingIntent,它与Intent之间的差异主要有下列三点:


PendingIntent代表延迟的意图,它指向的组件不会马上激活;而Intent代表实时的意图,它指 向的组件会马上激活。

PendingIntent是一类消息的组合,不但包含目标的Intent对象,还包含请求代码、请求方式等 信息。

PendingIntent对象在创建之时便已知晓将要用于活动还是广播

屏幕变更实践

竖屏与横屏切换


手机有竖屏与横屏两种模式,两种屏幕方向不但造成App界面的展示差异,而且竖屏和横 屏切换之际,甚至会打乱App的生命周期

系统配置变更的处理机制


为了避免横竖屏切换时重新加载界面的情况,Android设计了一种配置变更机制,在指定 的环境配置发生变更之时,无需重启活动页面,只需执行特定的变更行为。该机制的实现 过程分为两步:


修改AndroidManifest.xml,给activity节点增加 android:configChanges 属性

修改活动页面的Java代码,重写活动的 onConfigurationChanged 方法,补充对应的代码处理 逻辑

配置变更的豁免情况

回到桌面与切到任务列表


按下主页键会回到桌面,按下任务键会打开任务列表,这两个操作并未提供相应的按键处理方法,而是通过广播发出事件信息。

若想知晓是否回到桌面,以及是否打开任务列表,均需收听系统广播Intent.ACTION_CL OSE_SYSTEM_DIALOGS。

从收到的广播意图中获取原因reason字段,该字段值为homekey时表示回到桌面,值为r ecentapps时表示打开任务列表

应用的画中画

  • 监听回到桌面与打开任务列表的广播;
  • 收到广播之后,调用 enterPictureInPictureMode 方法进入画中画模式;
  • 重写活动页面的 onPictureInPictureModeChanged 方法,补充进入画中画模式或退出

自定义控件

视图的构建过程

简述

  • 自定义View的实现方式有以下几种


类型 定义
自定义组合控件 多个控件组合成为一个新的控件,方便多处复用
继承系统View控件 继承自TextView等系统控件,在系统控件的基础功能上进行扩展
继承View 不复用系统控件逻辑,继承View进行功能定义
继承系统ViewGroup 继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展
继承ViewViewGroup 不复用系统控件逻辑,继承ViewGroup进行功能定义

Android中的任何一个布局、任何一个控件其实都是直接或间接继承自View的,如TextView、Button、ImageView、ListView等


每一个视图的绘制过程都必须经历三个最主要的阶段,即初始化、onMeasure()、onLayout()和onDraw()


View坐标


c911bf1c8897c704d06402650f90f342.png


View获取自身高度

由上图可算出View的高度:


width = getRight() - getLeft();

height = getBottom() - getTop();

View的源码当中提供了getWidth()和getHeight()方法用来获取View的宽度和高度,其内部方法和上文所示是相同的,我们可以直接调用来获取View得宽高。


View自身的坐标


通过如下方法可以获取View到其父控件的距离。


getTop();获取View到其父布局顶边的距离。


getLeft();获取View到其父布局左边的距离。


getBottom();获取View到其父布局顶边的距离。


getRight();获取View到其父布局左边的距离

视图构造方法

无论是我们继承系统View还是直接继承View,都需要对构造函数进行重写,构造函数有多个,至少要重写其中一个才行。

package com.kcs.customview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class CustomView extends View {
    /**
     * 构造函数会在new的时候调用
     * @param context
     */
    public CustomView(Context context) {
        super(context);
    }
    /**
     * 在xml布局文件中使用时自动调用
     * @param context
     * @param attrs
     */
    public CustomView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    /**
     * 不会自动调用,如果有默认style时,在第二个构造函数中调用
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    /**
     * 只有在API版本>21时才会用到
     * 不会自动调用,如果有默认style时,在第二个构造函数中调用
     * @param context
     * @param attrs
     * @param defStyleAttr
     * @param defStyleRes
     */
    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}


视图测量方法


onMeasure()方法用于测量视图的大小


一个界面的展示可能会涉及到很多次的measure,因为一个布局中一般都会包含多个子视图,每个视图都需要经历一次measure过程。ViewGroup中定义了一个measureChildren()方法来去测量子视图的大小


MeasureSpec是View的内部类,它封装了一个View的尺寸,在onMeasure()当中会根据这个MeasureSpec的值来确定View的宽高。


MeasureSpec的值保存在一个int值当中。一个int值有32位,前两位表示模式mode后30位表示大小size。即MeasureSpec = mode + size。


在MeasureSpec当中一共存在三种mode:UNSPECIFIED、EXACTLY 和

AT_MOST。


对于View来说,MeasureSpec的mode和Size有如下意义

模式 意义 对应
EXACTLY 精准模式,View需要一个精确值,这个值即为MeasureSpec当中的Size match_parent


AT_MOST 最大模式,View的尺寸有一个最大值,View不可以超过MeasureSpec当中的Size值 wrap_content
UNSPECIFIED 无限制,View对尺寸没有任何限制,View设置为多大就应当为多大 一般系统内部使用


    // 获取测量模式(Mode)
    int specMode = MeasureSpec.getMode(measureSpec)
    // 获取测量大小(Size)
    int specSize = MeasureSpec.getSize(measureSpec)
    // 通过Mode 和 Size 生成新的SpecMode
    int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);

视图定位方法


View中的onLayout()方法就是一个空方法,因为onLayout()过程是为了确定视图在布局中所在的位置,而这个操作应该是由布局来完成的,即父视图决定子视图的显示位置


ViewGroup中的onLayout()方法竟然是一个抽象方法,这就意味着所有ViewGroup的子类都必须重写这个方法。像LinearLayout、RelativeLayout等布局,都是重写了这个方法,然后在内部按照各自的规则对子视图进行布局的。

/**
*  这里的四个参数l、t、r、b分别代表View的左、上、右、下四个边界相对于其父View的距离。
*
*/
public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }
        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        //这里通过setFrame或setOpticalFrame方法确定View在父容器当中的位置。
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
        //调用onLayout方法。onLayout方法是一个空实现,不同的布局会有不同的实现。
        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);
        }
    }


layout()方法中已经通过setOpticalFrame(l, t, r, b)或 setFrame(l, t, r, b)方法对View自身的位置进行了设置,所以onLayout(changed, l, t, r, b)方法主要是ViewGroup对子View的位置进行计算。

视图绘制方法


  1. 如果需要,绘制背景。
  2. 有过有必要,保存当前canvas。
  3. 绘制View的内容。
  4. 绘制子View。
  5. 如果有必要,绘制边缘、阴影等效果。
  6. 绘制装饰,如滚动条等等
    public void draw(Canvas canvas) {
        int saveCount;
        // 1. 如果需要,绘制背景
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }
        // 2. 有过有必要,保存当前canvas。
        final int viewFlags = mViewFlags;
        if (!verticalEdges && !horizontalEdges) {
            // 3. 绘制View的内容。
            if (!dirtyOpaque) onDraw(canvas);
            // 4. 绘制子View。
            dispatchDraw(canvas);
            drawAutofilledHighlight(canvas);
            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }
            // 6. 绘制装饰,如滚动条等等。
            onDrawForeground(canvas);
            // we're done...
            return;
        }
    }
    /**
    *  1.绘制View背景
    */
    private void drawBackground(Canvas canvas) {
        //获取背景
        final Drawable background = mBackground;
        if (background == null) {
            return;
        }
        setBackgroundBounds();
        //获取便宜值scrollX和scrollY,如果scrollX和scrollY都不等于0,则会在平移后的canvas上面绘制背景。
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;
        if ((scrollX | scrollY) == 0) {
            background.draw(canvas);
        } else {
            canvas.translate(scrollX, scrollY);
            background.draw(canvas);
            canvas.translate(-scrollX, -scrollY);
        }
    }
    /**
    * 3.绘制View的内容,该方法是一个空的实现,在各个业务当中自行处理。
    */
    protected void onDraw(Canvas canvas) {
    }
    /**
    * 4. 绘制子View。该方法在View当中是一个空的实现,在各个业务当中自行处理。
    *  在ViewGroup当中对dispatchDraw方法做了实现,主要是遍历子View,并调用子类的draw方法,一般我们不需要自己重写该方法。
    */
    protected void dispatchDraw(Canvas canvas) {
    }


改造已有的控件

自定义月份选择器

给翻页标签蓝添加新标签

不滚动的列表视图


通过持续绘制实现简单动画

Handller 的延迟机制

重新绘制视图界面

自定义饼图动画


实战项目:广告轮播

需求描述

界面设计

实现代码


通知与服务

消息通知

通知推送 Notification

通知渠道 NotificationChannel

桌面应用添加消息图标



服务Service

服务的启动和停止

服务的绑定与 解绑

推送服务到前台


多线程

分线程通过Handler 操作界面

异步任务 AsyncTask

异步服务 IntentServic


组合控件

底部标签栏

BottomNavigationView 实现底部标签栏

自定义标签栏

结合RadioGroup和ViewPager自定义底部标签栏

顶部导航栏

工具栏 Toolbar

溢出菜单OverflowMenu

标签布局TabLayout

增强型列表


循环视图RecycleView

布局管理器LayoutManager

动态更新循环视图



升级版翻页


下拉刷新布局SwipeRefreshLayout

第二代翻页视图ViewPager2

给ViewPager2集成标签布局

实战项目:电商首页

需求

界面

代码



多媒体

图片

使用相机拍摄照片

从相册中选取图片

对图片进行简单加工

图像解码器ImageDecoder



音频

使用录音机录制音频

利用MediaPlayer播放音频

利用MediaRecorder录制音频


视频

使用摄像机录制视频

从视频库中选取视频

利用视频视图(VideoView)播放视频


项目实战:评价晒单

需求

界面设计

实现代码


网络通信

HTTP 接口访问

HTTP接口访问

GET方式调用HTTP接口

POST方式调用HTTP接口


下载管理器DownloadManager

通知栏显示下载进度

主动轮询当前的下载进度

利用POST方式上传文件


图片加载框架Glide

图片地址获取图像数据

使用Glide加载网络图片

利用Glide实现图片的三级缓存


实战项目:猜你不喜欢

需求

界面设计

代码

打造安装包

应用打包

导出APK安装包

  1. 若是直接将以下文件的apk发送到手机,是不能安装成功的

66f7857ee19b37c250fd69e9d9ed7d2b.png




d16cffa81862c314977d56ff766efeb7.png


  1. 通过签名 jks


  2. 这里需要一个商店的key,因为没有,所以创建一个新的,点击Create new



d5f7e9550c2f4c9bb72e7beff4ad1bd1.png

首先指明生成的jks文件的路径,点击这个文件夹图标,个人习惯放在D盘的Apk目录下,然后命名jks的名称为Test,然后ok

26fe3418d8e6e9e28b48216913b17aea.png



然后输入这个jks和别名的一些相关信息,上方的商店密码和别名密码可以设置为一样的,这样便于记忆,只不过安全系数就降低了。我上面设置的是123456,信息都设置好了之后,点击OK。


7233f2a9c5a567d1a2c7f3526c745127.png



  1. 若是出现以下弹窗,直接ok,无影响

  2. 勾选记住密码,点击next

  3. 选择release 版本,既是发布版本,然后点击Finnish ;debug为测试版本 ,我的apk位置默认是工程下的


5cbf5062e3d92ac79f9cc9b0e10c8c7c.png

当你看到屏幕的右下角的进度条完成了,说明你的APK已经生成了,你可以在你的app下看到多了一个release的文件夹,里面就是app-release.apk包,下面你可以复制发到手机上,然后下载,手机上是可以安装的


d1402aceb64632ff2ae96e0b3efa947b.png

  1. 然后在你刚刚选择的文件下就会看到一个jks

  2. 在工程目录下,就可以找到qpk文件

  3. 若是在第 8 步时选择debug 版本 测试版本,可能会安装成功,这是为什么呢?


那个这种方式生成的包在实际开发中起到什么作用呢?


在实际开发中,如果是自己测试,那么通过就直接用usb安装在手机上,此时就是文中第一种情况。


而如果当项目要上线或者上应用市场时,就需要用jks来生成release包上传到应用市场上。


实际开发中还有一点就是你的应用自己自己测试的并不是很到位,因此产品和测试会需要让你打包给他们做进一步测试,此时你可以给通过jks生成的debug包,也可以给release包,但是通常是debug包。

打包的细节优化

从上面的操作中,知道了一些内容,而在基本的开发中也是会用到的,而为了更好区别不同的包名,常用会给apk进行一个重命名,见名知意。


修改名称

  1. 修改app下的build.gradle
// 自定义打包名称
android.applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "apk名.apk"
    }
}


90101cdc719da704740d0a7bac5b9f33.png

  1. 每次添加后记得Sync(同步)一下,然后重新打一个包,修改了生成apk的位置,如下下图所示


    然后next 两次,使用之前是jks签名即可,最后这里我修改了apk位置

  2. 在Apk目录下查看你apk文件 release版本



8a0c6a7ce8e10cbf3dbe699e1c029c3b.png


增加构建类型

outputFileName = "apk名_${buildType.name}.apk"
• 1

借用别人的图片:


9a66eb18b5e30e27beb73e5eda69da44.png


增加版本名

outputFileName = "apk名_${buildType.name}_v${versionName}.apk"
• 1

再次借用别人的图片


de42308f0f0212049842075a37c55dba.png


增加日期时间

在android{}闭包外增加

构建年月日时分秒

def generateTime() {
    return new Date().format("yyyy_MM_dd_HH_mm_ss")
}


348c8ca58b9c6dc15b37b096c2601b71.png


最后打包来看看修改之后的效果

制作App图片

APK瘦身



规范处理

版本设置

发布模式

数据库加密


安全加固

反编译

代码混淆

第三方加固及重前面

cationChannel

桌面应用添加消息图标


服务Service

服务的启动和停止

服务的绑定与 解绑

推送服务到前台


多线程

分线程通过Handler 操作界面

异步任务 AsyncTask

异步服务 IntentServic


组合控件

底部标签栏

BottomNavigationView 实现底部标签栏

自定义标签栏

结合RadioGroup和ViewPager自定义底部标签栏


顶部导航栏

工具栏 Toolbar

溢出菜单OverflowMenu

标签布局TabLayout


增强型列表

循环视图RecycleView

布局管理器LayoutManager

动态更新循环视图

升级版翻页


下拉刷新布局SwipeRefreshLayout

第二代翻页视图ViewPager2

给ViewPager2集成标签布局

实战项目:电商首页

需求

界面

代码


多媒体

图片

使用相机拍摄照片

从相册中选取图片

对图片进行简单加工

图像解码器ImageDecoder


音频

使用录音机录制音频

利用MediaPlayer播放音频

利用MediaRecorder录制音频

视频

使用摄像机录制视频

从视频库中选取视频

利用视频视图(VideoView)播放视频


项目实战:评价晒单

需求

界面设计

实现代码


网络通信

HTTP 接口访问

HTTP接口访问

GET方式调用HTTP接口

POST方式调用HTTP接口


下载管理器DownloadManager

通知栏显示下载进度

主动轮询当前的下载进度

利用POST方式上传文件


图片加载框架Glide

图片地址获取图像数据

使用Glide加载网络图片

利用Glide实现图片的三级缓存


实战项目:猜你不喜欢

需求

界面设计

代码



打造安装包

应用打包

导出APK安装包


若是直接将以下文件的apk发送到手机,是不能安装成功的


[外链图片转存中…(img-MBV9mwdw-1665663924504)]


[外链图片转存中…(img-VgwT5C0V-1665663924504)]


通过签名 jks


[外链图片转存中…(img-yhaZ6bKy-1665663924505)]


[外链图片转存中…(img-rgLmGK0H-1665663924505)]


这里需要一个商店的key,因为没有,所以创建一个新的,点击Create new


[外链图片转存中…(img-IpQpBT2N-1665663924506)]


首先指明生成的jks文件的路径,点击这个文件夹图标,个人习惯放在D盘的Apk目录下,然后命名jks的名称为Test,然后ok


[外链图片转存中…(img-2LpJfzWh-1665663924507)]


然后输入这个jks和别名的一些相关信息,上方的商店密码和别名密码可以设置为一样的,这样便于记忆,只不过安全系数就降低了。我上面设置的是123456,信息都设置好了之后,点击OK。


[外链图片转存中…(img-6G0kA7hl-1665663924507)]


若是出现以下弹窗,直接ok,无影响


[外链图片转存中…(img-oTZhWTAx-1665663924508)]


勾选记住密码,点击next


[外链图片转存中…(img-gGIN9osr-1665663924509)]


选择release 版本,既是发布版本,然后点击Finnish ;debug为测试版本 ,我的apk位置默认是工程下的


[外链图片转存中…(img-ipAUl8Sx-1665663924510)]


当你看到屏幕的右下角的进度条完成了,说明你的APK已经生成了,你可以在你的app下看到多了一个release的文件夹,里面就是app-release.apk包,下面你可以复制发到手机上,然后下载,手机上是可以安装的


[外链图片转存中…(img-OoyCoiKf-1665663924510)]然后在你刚刚选择的文件下就会看到一个jks


[外链图片转存中…(img-dnUu9ptS-1665663924511)]


在工程目录下,就可以找到qpk文件


[外链图片转存中…(img-hUZ0no91-1665663924511)]


若是在第 8 步时选择debug 版本 测试版本,可能会安装成功,这是为什么呢?


那个这种方式生成的包在实际开发中起到什么作用呢?


在实际开发中,如果是自己测试,那么通过就直接用usb安装在手机上,此时就是文中第一种情况。


而如果当项目要上线或者上应用市场时,就需要用jks来生成release包上传到应用市场上。


实际开发中还有一点就是你的应用自己自己测试的并不是很到位,因此产品和测试会需要让你打包给他们做进一步测试,此时你可以给通过jks生成的debug包,也可以给release包,但是通常是debug包。

打包的细节优化

从上面的操作中,知道了一些内容,而在基本的开发中也是会用到的,而为了更好区别不同的包名,常用会给apk进行一个重命名,见名知意。



修改名称

  1. 修改app下的build.gradle
// 自定义打包名称
android.applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "apk名.apk"
    }
}


[外链图片转存中…(img-MYR0ptOG-1665663924512)]


每次添加后记得Sync(同步)一下,然后重新打一个包,修改了生成apk的位置,如下下图所示


[外链图片转存中…(img-HQ815L53-1665663924513)]


[外链图片转存中…(img-fs6PH8om-1665663924513)]


然后next 两次,使用之前是jks签名即可,最后这里我修改了apk位置


[外链图片转存中…(img-DxsmBY13-1665663924514)]


在Apk目录下查看你apk文件 release版本


[外链图片转存中…(img-1TP1pTHe-1665663924515)]

增加构建类型

outputFileName = "apk名_${buildType.name}.apk"

[外链图片转存中…(img-AchigcqM-1665663924515)]


借用别人的图片:


[外链图片转存中…(img-JG6e1xNu-1665663924516)]


增加版本名

outputFileName = "apk名_${buildType.name}_v${versionName}.apk"

[外链图片转存中…(img-z0Ojd6iE-1665663924516)]

再次借用别人的图片:

[外链图片转存中…(img-hT8SOZyo-1665663924517)]


增加日期时间

在android{}闭包外增加

构建年月日时分秒

def generateTime() {
    return new Date().format("yyyy_MM_dd_HH_mm_ss")
}


[外链图片转存中…(img-q22CqTom-1665663924518)]

最后打包来看看修改之后的效果

[外链图片转存中…(img-kIUlyoR0-1665663924518)]

制作App图片

APK瘦身



规范处理

版本设置

发布模式

数据库加密




安全加固

反编译

代码混淆

第三方加固及重前面

目录
相关文章
|
6月前
|
XML 数据库 数据安全/隐私保护
Android学习笔记3
Android学习笔记3
115 0
|
6月前
|
XML Java Android开发
Android学习笔记2
Android学习笔记2
53 0
|
6月前
|
XML Java Linux
Android学习笔记1
Android学习笔记1
32 0
|
8月前
|
存储 Java API
Android逆向 | 基础篇 - Java 学习笔记03
Android逆向 | 基础篇 - Java 学习笔记03
|
8月前
|
Java 编译器 Android开发
Android逆向 | 基础篇 - Java 学习笔记02
Android逆向 | 基础篇 - Java 学习笔记02
|
8月前
|
Java 编译器 Android开发
Android逆向 | 基础篇 - Java 学习笔记01
Android逆向 | 基础篇 - Java 学习笔记01
|
Java 开发工具 Android开发
Android中的Binder学习笔记
Android中的Binder学习笔记
Android中的Binder学习笔记
|
JSON 前端开发 Java
Java WEB 与 android | 学习笔记
快速学习 Java WEB 与 android。
Java WEB 与 android | 学习笔记
|
Web App开发 小程序 安全
mPaaS 小程序介绍+接入 mPaaS 小程序并实现启动 Android 版(二)| 学习笔记
快速学习 mPaaS 小程序介绍+接入 mPaaS 小程序并实现启动 Android 版。
660 0
mPaaS 小程序介绍+接入 mPaaS 小程序并实现启动 Android 版(二)| 学习笔记
|
移动开发 小程序 IDE
Android 小程序接入真机与调试| 学习笔记
快速学习 Android 小程序接入真机与调试。
275 0
Android 小程序接入真机与调试| 学习笔记