0%

图像

  1. 图片大小计算
  • 分辨率x图片位深(kb)

    1
    360px*360px*4 bytes-per-pixel * 50 images = 506 kiB * 50 images = 24.7 MiB
  1. 内存优化

    首次获取图片的宽高, 计算取样比例

    然后加载真实图片

1
2
3
4
5
6
7
8
9
10
val point = Point()
windowManager.defaultDisplay.getSize(point)
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(columnName, options)
val heightRadio = options.outHeight / point.y
val widthRadio = options.outWidth / point.x
options.inSampleSize = Math.max(heightRadio, widthRadio)
options.inJustDecodeBounds = false
val decodeFile = BitmapFactory.decodeFile(columnName, options)
Read more »

第三方音乐服务设计概要

  1. 功能

    列表

    播放

    登录/三方登录

    支付

  2. 设计总架构

    image-20210311192931748
  3. 服务端

    3.1 核心类

    • BaseMusicService 用于构建MediaBrowser体系

    • BasePlaybackManager

      1. 通过MediaSession接收客户端传输事件

      2. 管理播放队列

      3. 登录

  4. 客户端

待办:

喜马拉雅需要API请求登录, 暂未做本地缓存

分页列表缓存何时清除

​ 前言: 喜马拉雅手机版和车机版两者接入方式不同, 手机版xmly提供了SDK+API的方式, 车机版只有API的方式, 区别在客户端API接入和服务端API接入

  1. 先找喜马拉雅工作人员申请app_key ,app_secret ,如果是服务端接入的话还需要server_auth_key 一般一周内即可申请到

  2. 确定host和path, 目前喜马拉雅有三种host

    https://api.ximalaya.com/iot/openapi-smart-device-api

    https://api.ximalaya.com/iot/openapi-smart-device-pay-api

    https://api.ximalaya.com

    path根据业务区分

    GET/POST

  3. 确定公共参数和业务参数

    image-20210306100626108

    GET请求参数拼接在url后, POST请求放在body中

    文档上这么写的, 实际上部分接口POST依然拼接在url后

  4. 计算sig, 联系喜马工程师获取demo, 以testSignature方法为准

公共数据采用客户端授权access_token, 私有数据如用户信息采用服务端授权access_token

Content-Length: 内容长度(字节)

Content-Type

  • text/html: html文本,主要用于浏览器页面响应
  • application/x-www-form-urlencoded: 普通表单
  • multipart/form-data: 多用于传文件
  • application/json
  • Transfer-encoding: chunked 分段传输

前读 * 惑

Q1, 并发最佳实践

Q2, 并发是什么

​ 并发的目的: 让程序运行更快, 但不是线程越多越快, 线程的创建和CPU执行上下文切换会消耗时间

​ 使用多线程同时执行一批任务

Q3, 哪种场景下使用并发

Read more »

添加代理

增快访问速度

1.打开ipaddress.com网站

2.输入GitHub.com或者fastly.net查询IP

3.管理员打开C:\Windows\System32\drivers\etc\hosts

4.199.232.69.194 fastly.net


Q: github.io打不开

A: 按如上配置自己的域名

1
2
140.82.114.4	github.com
185.199.109.153 github.global.ssl.fastly.net

配置用户名,邮箱

git config –global user.email “email”

git config –global user.name “username”

查看邮箱

git config user.name

git config user.email

设置别名

git config –global alias.co “checkout” //使用git co 代替 checkout

git config –global alias.st status

查看当前配置

git config –global –list

克隆一个仓库

​ git clone git://

Read more »

四. \访问数据库**

cd /data/user_de/0/com.android.providers.media/databases

cd /data/data/com.android.providers.media/databases

ls

sqlite3 external.db 进入数据库

.tables //列出所有表

.mode column 显示的列会对齐

.header on //显示列名

select * from 表

** \Vim**

  • 查看

vi xx.sh +22 //跳到指定行

  • 删除

当前行 dd

全选ggvG 按d删除

  • 创建文件

touch + xx.tt

  • 查找

/xx 查找某字符, n 向下找

  • 替换

:s/from/to/ 替换当前行第一个

:s/from/to/g 替换当前行所有

  • Mk转bp

out/soong/host/linux-x86/bin

如果没有androidmk 使用m -j blueprint_tools

androidmk android.mk > android.bp

  • 查找

    find prebuilts/sdk/ -name Android.bp|xargs grep “name.*androidx”

七. \Shell**

查看cpu信息

img

adb shell

cat /proc/cpuinfo AArch64对应arm64-v8a

getprop ro.product.cpu.abi //打印架构字符串

查找文件

busybox find . -name filename

####抓取日志

adb pull /data/misc/bwlog/

####查看当前占用

Lsof | grep 路径

查看settings 属性

Settings.System.getString(context.getContentResolver(), CustomViewUtils.NAME_BW_THEME_COLOR);

settings get SYSTEM bw_theme_color

查看文件大小

busybox du -h -d 1

####设置屏幕旋转

adb shell content insert –uri content://settings/system –bind name:s:user_rotation –bind value:i:1 i后面取值(0,1,2,3)

####应用冷启动脚本

1
2
3
4
5
6
$ for i in `seq 1 100`
> do
> adb shell am force-stop com.android.samples.mytest
> sleep 1
> adb shell am start-activity -W -n com.android.samples.mytest/.MainActivity | grep "TotalTime" | cut -d ' ' -f 2
> done

python

使用镜像

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple (xxx)

前言:最近涉及到和QQ打交道,定义所有的好友一共只能有300条消息,如果一次性从数据库读取300条或者更多,界面会有细微的卡顿.所以考虑了下分页,第一次进来只显示20条(仿微信),当用户滑到第一条后,如果数据库有消息,则再加载20条.

##步骤-问把大象关冰箱,总共分几步?
###1.自定义absListview.scrollListerner
核心的东西是监听ListView的scrollListerner,网上扒了一个挺不错的,大家用的时候实现这个scrollListerner,完善自己的逻辑即可

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
public class MyOnScrollListener implements OnScrollListener {
private int totalItemCount;
//ListView最后的item项
private int lastItem;
//listview第一项
private int firstItem;
//用于判断当前是否在加载
private boolean isLoading;
//底部加载更多布局
private View footer;
//接口回调的实例
private OnloadDataListener listener;

//数据
private List<MsgBean> data;
Handler handler = new Handler();


public MyOnScrollListener(View footer, List<MsgBean> data) {
this.footer = footer;
this.data = data;
}
//设置接口回调的实例
public void setOnLoadDataListener(OnloadDataListener listener) {
this.listener = listener;
}
/**
* 滑动状态变化
*
* @param view
* @param scrollState 1 SCROLL_STATE_TOUCH_SCROLL是拖动 2 SCROLL_STATE_FLING是惯性滑动 0SCROLL_STATE_IDLE是停止 , 只有当在不同状态间切换的时候才会执行
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//如果数据没有加载,并且滑动状态是停止的,并且滚到了第一个item,可在此做下拉更新或者上拉更新的判断
if (!isLoading && firstItem == 0 && scrollState == SCROLL_STATE_IDLE) {
//显示加载更多
footer.setVisibility(View.VISIBLE);

//模拟一个延迟两秒的刷新功能
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (listener != null) {
//开始加载更多数据
loadMoreData();
//回调设置ListView的数据
listener.onLoadData(data);
//加载完成后操作什么
loadComplete();
}
}
}, 2000);
}
}
/**
* 当加载数据完成后,设置加载标志为false表示没有加载数据了
* 并且设置底部加载更多为隐藏
*/
private void loadComplete() {
isLoading = false;
footer.setVisibility(View.GONE);

}
/**
* 开始加载更多新数据,这里每次只更新三条数据
*/
private void loadMoreData() {
isLoading = true;
MsgBean msg = null;
for (int i = 0; i < 3; i++) {
msg = new MsgBean();
msg .setRemark("Liming"+i);
msg .setMsgID(i);
data.add(stu);
}
}
/**
* 监听可见界面的情况
*
* @param view ListView
* @param firstVisibleItem 第一个可见的 item 的索引
* @param visibleItemCount 可以显示的 item的条数
* @param totalItemCount 总共有多少个 item
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//实现下拉加载
lastItem = firstVisibleItem + visibleItemCount;
//实现上拉加载
firstItem = firstVisibleItem;
//总listView的item个数
this.totalItemCount = totalItemCount;
}
//回调接口
public interface OnloadDataListener {
void onLoadData(List<MsgBean> data);
}
}

###2.实现此接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ListPageActivity extends Activity implements MyOnScrollListener.OnloadDataListener  {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_page);

//显示到ListView上
showListView(data);
//自定义的滚动监听事件
MyOnScrollListener onScrollListener = new MyOnScrollListener(header, data);
//设置接口回调
onScrollListener.setOnLoadDataListener(this);
//设置ListView的滚动监听事件
mListView.setOnScrollListener(onScrollListener);

@Override
public void onLoadData(List<MsgBean> data) {
//加载数据完成后,展示数据到ListView
showListView(data);
}
}

showListView里面无疑是普通的更新adapter的工作
那么我们如何借助xutils的数据库进行分类呢?
###3.利用xutils数据库操作进行分页处理
首先,我们理一下思路,上面我们已经实现了上拉的回调,在此回调中把新来的数据加载到adapter即可.
//下文db是Dbmanager的实例,可参考xutils3用法

/**
 * 当前屏幕显示的消息数量
 */
private int MAX_MSG_NUMBER = 20;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private List<MsgBean> getDataFromDb() {

List<?> dbSize = db.selector(MsgBean.class).where(WhereBuilder.b("id", "=", 400)).findAll();//记得捕获null指针和DbException异常
//如果数据库比我们显示的页数小,则不偏移,否则,偏移到我们需要显示的位置
if (dbSize.size() < MAX_MSG_NUMBER) {
indexOffset = 0;
} else {
indexOffset = dbSize.size() - MAX_MSG_NUMBER;
}

List<MsgBean> datas = db.selector(MsgBean.class).where(WhereBuilder.b("id", "=", 400)).limit(MAX_MSG_NUMBER)
.offset(indexOffset).findAll();
return datas;
}

这里解释一下

db.selector(MsgBean.class).where(WhereBuilder.b("id", "=", 400)).limit(MAX_MSG_NUMBER).offset(indexOffset).findAll();是我们实现分页的关键

.limit是我们定义的分页大小
.offset偏移量,我们数据库的大小是不变的,如果不定义偏移量,那么我们定义的分页大小每次只从0取到19.假设数据库中有21条数据,那么我们需要从1取到20,而不是0到19,所以偏移1.

然后我们在loadMoreData中

MAX_MSG_NUMBER += MAX_MSG_NUMBER;
getDataFromDb();

将大小自加,即完成加载更多的功能,在onLoadData(List data)中加载数据即可.


后面贴上我对xutils数据库操作的封装,还有很多不完善之处

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
* 数据库 xutils用法
* @author 青楼爱小生
*/
public class DbUtil {
private static final String TAG = DbUtil.class.getName();
private static DbUtil dbUtil;
private DbManager db;
private DbUtil(){
db = x.getDb(MyApplication.getInstance().daoConfig);
}
public static DbUtil getInstance(){
if(dbUtil == null){
synchronized (DbUtil.class) {
if(dbUtil == null){
dbUtil = new DbUtil();
}
}
}
return dbUtil;
}
/**
* 增加数据
* @param list
* @throws DbException
*/
public void addMsgList(List<MsgBean> list) {
try {
db.saveOrUpdate(list);
} catch (DbException e) {
e.printStackTrace();
LogHelper.e(TAG, e.getMessage());
}
}
/**
* 增加一条数据
* @param node
* @throws DbException
*/
public void addMsgToDb(MsgBean node) {
try {
db.saveOrUpdate(node);
} catch (DbException e) {
e.printStackTrace();
LogHelper.e(TAG, e.getMessage());
}
}
/**
* 删除表中所有数据
* @param cls 创建的表的映射
* @throws DbException
*/
public void deleteAll(Class cls) {
try {
db.delete(cls);
} catch (DbException e) {
LogHelper.e(TAG, e.getMessage());
e.printStackTrace();
}
}
/**
* 删除第一条数据
* @param cls
*/
@SuppressWarnings("unchecked")
public void deleteFirst(Class cls){
try {
db.delete(db.findFirst(cls));
} catch (DbException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 查询表中所有数据
* @throws DbException
*/
@SuppressWarnings("unchecked")
public List<?> findAll(Class cls) {
try {
return db.findAll(cls) == null ? Collections.emptyList() : db.findAll(cls);
} catch (DbException e) {
e.printStackTrace();
LogHelper.e(TAG, e.getMessage());
return Collections.emptyList();
}
}
/**
* //添加查询条件进行查询
List<ChildInfo> all = db.selector(ChildInfo.class).where("id",">",2).and("id","<",4).findAll();
* @return 搜索指定条件的数据
*/
@SuppressWarnings("unchecked")
public List<?> findDataByWhere(Class cls,WhereBuilder format){
try {
return db.selector(cls).where(format).findAll()== null ?
Collections.emptyList() :db.selector(cls).where(format).findAll();
} catch (DbException e) {
LogHelper.e(TAG, e.getMessage());
e.printStackTrace();
return Collections.emptyList();
}

}
/**
* 添加查询条件进行查询
* @param cls 表映射
* @param str select语句
* @param format where语句
* @return List<DbModel> DbModel key为数据库列名 value为值
* eg:(Selector.from(Parent.class)
.where("id" ,"<", 54)
.and(WhereBuilder.b("age", ">", 20).or("age", " < ", 30))
.orderBy("id")
.limit(pageSize) .offset(pageSize * pageIndex));
*
*
*
*/
@SuppressWarnings("unchecked")
public Selector<?> findDataBySelector(Class cls,WhereBuilder format){
try {
return db.selector(cls).where(format);
} catch (DbException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}

}

1. 背景

​ 传统的媒体开发, 由我们自己定义接口, 服务端, 涉及到不同的业务往往需要重新定义不同的接口, 导致开发成本增加, 为此google在5.0中提供了一套多媒体开发框架, 目前Android9源码中蓝牙音乐服务/收音机服务等均已采用此架构. 主要目的为了规范媒体服务和客户端之间的通信接口, 实现解耦

2. 整体架构

媒体应用架构01

​ UI和播放器针对不同项目可有不同程度的扩展, 但针对媒体应用来说, 业务交互基本相同, 所以抽出了MediaController和MediaSession实现核心交互逻辑

2.1 核心类

  • MediaController(媒体控制器)

    负责与UI的交互, 将用户的操作通过媒体控制器转换为对会话的回调, 同样, 媒体会话改变会通知控制器的回调变化, 实现双重更新机制

  • MediaSession(媒体会话)

    负责与播放器的所有通信, 对UI隐藏播放器实现细节

    维护播放相关的信息, 比如播放状态, Id3info等

Read more »