第一章:常用技巧

1.1 常用adb命令

查看当前Activity

Code
1
2
3
4
5
6
8.1之前
window 通过adb shell dumpsys activity | findstr “mFocus”
Linux 通过adb shell dumpsys activity | grep “mFocus”
8.1之后
window 通过adb shell dumpsys activity | findstr “mResume”
Linux 通过adb shell dumpsys activity | grep “mResume”

连接模拟器

Code
1
2
3
4
5
adb connect 127.0.0.1:21503 //逍遥浏览器

//网易木木浏览器
adb connect 127.0.0.1:7555 (Windows上)
adb connect 127.0.0.1:5555 (Mac上)

指定设备安装apk

Code
1
adb -s 设备id  install xxx.apk

第二章 基础知识

在本章节,主要包括如下内容

  • shape,layer-list

2.1 shape

基础属性

属性 说明
shape 定义形状:如 rectangle,ring,oval
corners 定义圆角
solid 定义内部填充色
stroke 定义边框
gradient 渐变色

常用shape

Code
1
2
3
4
5
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="#F2F2F2"/>
</shape>

2.2 layer-list图层

layer-list和shape不同的是,layer-list可以放多个item,每个item下可以放shape等

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="#FF4C00"/>
</shape>
</item>

<item android:left="2dp" android:right="2dp" android:top="2dp" android:bottom="2dp">
<shape
android:shape="rectangle">
<corners android:radius="6dp"/>
<stroke android:color="@color/color_white" android:width="1dp"/>
</shape>
</item>
</layer-list>

2.3 Android studio

2.3.1 阿里云仓库

替换jcenter(),修改成

Code
1
jcenter(){ url 'https://maven.aliyun.com/repository/jcenter'}

2.3.2 编译速度优化

gradle离线模式

初次打开的时候不让它连接谷歌进行更新

Code
1
2
找到studio安装目录,找到idea.properties文件,打开,加上下面一行配置,作用是在初次打开的时候不让它连接谷歌进行更新
disable.android.first.run=true

更改studio的VM大小

Code
1
2
-Xms 是JVM启动的起始堆内存,堆内存是分配给对象的内存
-Xmx 是 Java 虚拟机启动时的参数,用于限制最大堆内存

2.4版本更新记录

迁移AndroidX

记录一下迁移后遇到的一些问题

问题一:构建release包失败,错误提示如下,

Code
1
R8: Unknown option "-ignorewarning"

解决方法:
pogard文件中,将-ignorewarning 修改为-ignorewarnings

同时添加AndroidX相关混淆

Code
1
2
3
4
5
6
7
8
#/*------------------androidX混淆------------------------*/
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**

参考博客:

https://www.jianshu.com/p/81805f026d93

https://foreachi.com/android/androidx-proguard/

第三章 Android实战开发

2.1RecyclerView相关

RecyclerView嵌套滑动卡顿

Code
1
2
3
4
粗暴解决方式:
rv.setItemViewCacheSize(200);
//继续粗暴
rv.setHasFixedSize(true); rv.setNestedScrollingEnabled(false);

RecyclerView 网格布局,分割线

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

private int spanCount;
private int spacing;
private boolean includeEdge;

public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
}
}
}

2.2 自定义view

自定义长宽比ViewGroup

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//长宽比1.4
class RatioLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec))

val childWidthSize = measuredWidth
val mWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY)
val mHeightMeasureSpec = MeasureSpec.makeMeasureSpec((childWidthSize * 1.4).toInt(), MeasureSpec.EXACTLY)

super.onMeasure(mWidthMeasureSpec, mHeightMeasureSpec);
}
}

2.3 AAR

打包AAR

当我们代码写好后,如何打包AAR呢?找到右侧gradle,找到library-> Tasks -> build -> assemble

AAR包生成目录:library->build->outputs->aar下

AAR引用第三方库

如果在aar中引用了第三方库,那么需要在APP中手动引入aar,因为aar中并不能依赖传递

AAR发布

AAR可以发布到Jcenter或者国内仓库,如阿里云maven(云效)

2.4 XML转JSON

Code
1
2
https://github.com/smart-fun/XmlToJson
implementation 'com.github.smart-fun:XmlToJson:1.4.5'

注意:如果xml中要解析list数据,必须设置强制解析成list,因为当list只有一条数据的时候,框架根本不知道xml里面,是解析成对象,还是list,所以必须手动指定

参考:https://github.com/smart-fun/XmlToJson#force-a-tag-to-be-a-list

第四章 APP常用开发功能

4.1版本适配

7.0 fileProvider适配

主要分为以下几步:

  • 1.创建file_paths文件,配置共享目录,注意命名不要和其他三方的冲突
  • 2.在aplication节点下添加Provider,指定file_paths
  • 3.根据版本获取不同的URI

实战:打开文件

首先配置file_paths

Code
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<!--ppt存放路径-->
<external-files-path name="resource" path="ppt/" />
</paths>
</resources>

这里的external-files-path,对应getExternalFilesDir()

name是取一个别名,只要唯一字符串即可,path是真实目录,可以看出,这里使用resource隐藏了真实目录ppt

Code
1
content://com.yijieyuanyoutongllyfeng1.app.fileProvider/resource/xxx.ppt

然后配置provider

Code
1
2
3
4
5
6
7
8
9
10
11
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="name,authorities,exported,grantUriPermissions">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"
tools:replace="name,resource"/>
</provider>

根据版本获取不同的URI

Code
1
2
3
4
5
6
7
8
fun getUriFromFile(ctx: Context,file:File):Uri{
return if (Build.VERSION.SDK_INT >= 24){
//provider authorities
FileProvider.getUriForFile(ctx, ctx.packageName + ".fileProvider", file)
}else{
Uri.fromFile(file)
}
}

使用URI打开文件

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun openFile(ctx:Context,file:File){
val intent = Intent(ACTION_GET_CONTENT)
val uri = getUriFromFile(ctx,file)
Log.d("uri:",uri.toString())
intent.setDataAndType(uri,"file/*")

intent.action = Intent.ACTION_VIEW
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
try {
ctx.startActivity(intent)
}catch (e:Exception){
e.printStackTrace()
}
}

4.2 友盟统计

添加仓库和依赖

Code
1
2
3
maven { url 'https://dl.bintray.com/umsdk/release' }//友盟
api "com.umeng.umsdk:common:9.3.0" //(必选)
api "com.umeng.umsdk:asms:1.1.3" // asms包依赖(必选)

初始化

Code
1
2
3
4
5
6
7
private fun initUmeng() {
val chanel = WalleChannelReader.getChannel(this)
UMConfigure.init(this,Constant.UMENG_KEY,chanel?:"测试",UMConfigure.DEVICE_TYPE_PHONE,null)
MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO)
//友盟日志
UMConfigure.setLogEnabled(Constant.UMENG_LOG)
}

注意:这里渠道使用的美团walle渠道包技术,关于walle,在下一节walle渠道包讲解

4.3 多渠道打包

使用美团walle + python 实现多渠道打包

美团walle

第一步:添加插件依赖

Code
1
2
3
4
5
buildscript {
dependencies {
classpath 'com.meituan.android.walle:plugin:1.1.7'
}
}

第二步:在build.gradle中应用插件,添加读取渠道号的依赖

Code
1
2
3
4
5
apply plugin: 'walle'

dependencies {
compile 'com.meituan.android.walle:library:1.1.7'
}

第三步:配置walle

Code
1
2
3
4
5
6
7
8
walle {
// 指定渠道包的输出路径
apkOutputFolder = new File("${project.buildDir}/outputs/channels");
// 定制渠道包的APK的文件名称
apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
// 渠道配置文件
channelFile = new File("${project.getProjectDir()}/channel")
}

感觉这个可要可不要,因为我们主要是打未加固的包,然后使用360加固,然后使用python打渠道包

美团walle+python多渠道打包

4.4 多module集中管理

  • 1.在项目的根目录下新建extraModule文件夹
  • 2.把所需要依赖的module都放在该目录下
  • 3.在setting.gradle中将原有的所有的moduleName改为:extraModule:moduleName,点击同步,即可

第五章 开源项目

4.1 日历

Android-CalendarView-master:实现方式 GridView