0%

上班还是创业

上班还是创业

上班:朝九晚五还好,整天看老板颜色,听领导安排,固定资产,涨不过房价

创业:风餐露宿,未来不确定,但是有充分的自由,一朝一夕自己当老板,风险大,收益高

本周总结

1.总体计划概要

工作时长:

  • 学习:

  • 交通:

  • 陪伴家人:

  • 日常(洗漱,洗澡等)

  • 睡眠:

  • 娱乐:

2.计划本周目标及完成情况

3.完成情况原因分析

4.后续计划

本周做得完善的地方
值得改进的地方

Activity finish 后 onDestroy ()并不会立马执行

Activity finish 后 onDestroy ()并不会立马执行

只看标题就好

所以两个Activity在用到生命周期的时候,不要再onDestroy中做,控制不了

Activity官方直译深入理解(完结)

Activity官方直译深入理解(完结)


Activity

一个Activity是用户可以直观交互的东西。所有的activity都聚焦于创建一个窗口,可以通过setcontentview(view)的方式。通常情况下一个activity是全屏显示的,当然也可以设置不同的theme来显示不同的风格,例如windowIsFloating来设置浮动窗口。以下是每个activity必须回调的方法:

Oncreate 在此你可以初始化你的activity。通常情况下你会用布局文件setcontentview的方式来定义此activity。在程序中通过findViewById(int)来和布局中的控件进行交互。

Onpause 当用户离开你的activity时会回调此方法,通常情况下之前用户做的改变行为需在此提交。

在用Context.startActivity()启动之前,需要在AndroidManifest.xml通过<activity>声明。

Activity的生命周期




图片发自简书App

整个过程从Oncreate开始,ondestroy结束。用户可以见到activity时刻从onstart开始到onstop之前。我们可以在onstart里面注册BroadcastReceiver,以便收到广播后修改我们的ui, 在onstop里面取消我们的BroadcastReceiver。用户可以和activity交互的时候从onresume开始直到onpause,一个activity会经常在这两个生命周期切换,所以最好在这两个方法里做轻量级的操作。

你应该在你的子类中回调如下方法,以便在不同的时候处理不同的逻辑。

public class Activity extends ApplicationContext {
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
}

Note:如果一个activity的onpause方法不结束的话,当前的或另一个activity的onresume方法不会执行。


Configuration Changes

屏幕翻转,改变语言或者输入设备的改变,都会导致activity销毁。当然,我们可以在manifest.xml里面声明android:configChanges属性,在上述配置改变的时候交给我们自己的activity处理.将回调在这个方法里onConfigurationChanged(Configuration),如果在此方法中有些屏幕配置的改变没有处理到,那么activity还是会被销毁,如果是当前在栈顶的activity则会重建。


Starting Activities and Getting Results

通常情况下我们用startActivity(Intent)来启动一个新的activity,但有时我们想从新的activity结束的时候获取某些数据。比如微信打开相册选择某个图片发给联系人。此时就要用到startActivityForResult(Intent, int),int为请求码,将在稍后介绍。返回的结果将回调在原activity的onActivityResult(int, int, Intent)方法里。不同的int表示不同的情况,如结果返回成功与否。Android系统已帮我们做了定义(RESULT_CANCELED, RESULT_OK)。下面请看示例:见谅,实在不知道怎么在手机上调代码的格式



public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
new Intent(Intent.ACTION_PICK,
new Uri("content://contacts")),
PICK_CONTACT_REQUEST);
return true;
}
return false;
}
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Here we will just display it
// to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
}
}
}


Permissions

启动一个Activity可能被拒绝,当在manifest里面没声明对应权限的时候。任何一个权限的生命周期(6.0需要特别的权限处理,百度android6.0权限封装)将持续到整个activity结束。


Process Lifecycle

Android系统会尽可能长的保留我们的应用进程,但是当内存不足的时候会移除最老的进程。通常情况下移除进程会和用户交互的activity类型有很大相关,以下列出了四种activity的类型(重要性从高到低):

1.foreground activity

用户正在交互的Activity,如你现在正在浏览的简书。它的进程只有在内存超出预期(由手机设备分配)才会被杀掉,通常来讲设备有责任保留它的进程。

2.visible activity

用户可见但不是foreground activity,比如此时弹出了一个对话框,一般来说不会被杀掉,除非为了保证foreground activity运行。

3.background activity

系统为前两种Activity预留内存会将此activity安全的杀掉。当用户重新返回这个activity时,oncreate方法会被回调,并会在onSaveInstanceState(Bundle)方法保存退出前的状态。

4.empty process

不再持有Activity或者是一个Service or BroadcastReceiver,系统在内存不足时有极大的可能性杀掉,所以我们在用service这种后台服务的时候,需要告诉系统我们的进程需要保留(通常做法是设置前台进程,弹出notification)。


Public method

1.addContentView(View view, ViewGroup.LayoutParams param)

添加一个可添加的布局

2.createPendingResult(int requestCode, Intent data, int flags)

创建一个PendingIntent的对象,并将处理结果返回到onActivityResult(int, int, Intent)。

3.dispatchTouchEvent(MotionEvent ev)

此方法在处理滑动冲突时极为有用,还有一个在子类中常用的方法view.getparent.requestdisallow(true)谁用谁知道。事件分发在很多大牛的文章已经有讲述了,小弟就不献丑了。

4.dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)

打印Activity的状态到指定流

5.enterPictureInPictureMode()

让此Activity进入画中画模式,如果系统允许的话。现在主流直播可能就是此方法的衍生。

6.findViewById(int id)

从XML中解析出view对象

7.finish()

结束当前Activity,有人说此方法是不正常的结束方式,但看官方并无特别说明。

8.finishActivity(int requestCode)

强制关闭通过startActivityForResult(Intent, int)启动的activity。

9.finishAndRemoveTask()

结束一个Task里所有的activity。哟,难道能在主页调这个方法?

10.getApplication()

获取Activity唯一的application。如果是组件化项目不知道这个方式可不可用?

11.getCallingActivity()

超级有用的方法,通常我们做开发的,不知道当前页面是哪个activity,我们可以继承一个baseactivity,然后再回调oncreate的时候打印出当前的类名,这样我们就可以知道哪个页面对应哪个activity了。关于这个郭大神有一篇这个文章,后面我会贴出代码。

12.getCurrentFocus()



获得当前Window聚焦的具体view

13.getFragmentManager()

返回和当前Activity绑定的fragment的fragment manager

14.getIntent()

获得启动此Activity的intent

15.getLayoutInflater()

通过此方法直接解析一个布局成View

16.getReferrer()

谁启动了我?

17.getParentActivityIntent()

18.getRequestedOrientation()

当前请求的屏幕方向,注意用词,请求的,可能没请求成功

19.getSystemService(String name)

根据名字获得不同系统级的服务

20.isDestroyed()

回调的Ondestroy执行了,表示当前activity的实例已经死了

21.isFinishing()

调用Finish()后

22.isImmersive()

是否为沉侵模式,并且不能被通知打断

23.isInMultiWindowMode()

是否为多窗口模式

24.isInPictureInPictureMode()

是否为画中画模式

25.onAttachFragment(Fragment fragment)

回调此方法在Fragment.onAttach()后

26.onAttachedToWindow()

当前Activity的主window是否关联上了windowmanager

27.onBackPressed()

用户按了返回键

28.onKeyDown(int keyCode, KeyEvent event)

当有键被按下的时候,同时所有的子view没有处理的情况会回调

29.onLowMemory()

内存不足时回调,可在此释放一些资源,降低被系统回收的概率

30.onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)

请求的所有权限回调

31.onTouchEvent(MotionEvent event)

触摸事件,当子view没做处理的时候

32.overridePendingTransition(int enterAnim, int exitAnim)

在startActivity(Intent) or finish()后执行activity的切换动画。

33.recreate()



重新创建一个新实例

34.releaseInstance()

通知本地App叫它释放内存

35.runOnUiThread(Runnable action)

运行在UI线程,可在此更新一些界面显示

36.setIntent(Intent newIntent)

更新回调的getintent()

37.setVisible(boolean visible)

当前主Window是否可见

Protected methods

1.onActivityResult(int requestCode, int resultCode, Intent data)

接收启动的Activity结束时返回给它的一系列参数

2.onCreate(Bundle savedInstanceState)

3.onDestroy()

4.onNewIntent(Intent intent)

当启动模式为singleTop,在调用startActivity(Intent)时回调此方法

5.onpaues()

6.onPostCreate(Bundle savedInstanceState)

在onStart() and onRestoreInstanceState(Bundle)后调用,此方法用的少,但存在基友它的道理

7.onPostResume()

在Onresume后调用

8.onUserLeaveHint()

当用户选择进入后台时回调


Activity完结,以后将开始service的直译

Android文件系统入门知识

内外存储的由来:因为历史原因,老版本的Android手机可用容量小, 加载外部存储设备以便加大容量, 如sdcard. 后来随着技术的不断提高, 手机本身的容量逐渐加大, 但是这个分法一直保留了下来.

二者区别

  • 内部存储

    不需要权限

    其它APP不可访问, 用户也不能操作

    卸载APP时会移除内部存储数据

  • 外部存储

    首先要确认可访问性, 因为外部存储可卸载

    其它APP可以访问

    需要权限

    1
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • *内部存储简单介绍**

所在路径: Android 6.0在/data/user/0/package_name/

其它版本也对应在/data/下的某个地方, 以包名作为区分, 未root的文件管理中查看不了

使用:

在继承自ContextWrapper中, 如Activity, Application

1
2
3
4
5
6
7
8
9
10
11
12
//得到文件对象
File file = getFilesDir();

String inPath = file.getAbsolutePath();

Log.d("test-file","getFileDir:"+ inPath);

//在inPath中写入hello
FileOutputStream fileOutputStream = openFileOutput("hello.txt", MODE_PRIVATE);
fileOutputStream.write("hello".getBytes());

fileOutputStream.close();

外部存储简单介绍

通过android.os包中的Environment类

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
//判断文件可用性
/* 可写 */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}

/* 可读 */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}

//得到外部存储私有对象(File),会随着APP的卸载而卸载
Environment.getExternalStorageDirectory();

//得到外部存储公共对象, 文件不会随着APP的卸载而卸载
//传入的参数代表文件类型 如Environment.DIRECTORY_MUSIC 代表音乐
//DIRECTORY_RINGTONES 代表铃音
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC));

得到File对象后, 你就可以为所欲为

File类


###共享数据

记得第一份工作面试的时候被问了一个问题:如何自己实现类似友盟分享功能?
当时回答的很含糊, 现在再来看, 就是通过Intent来定义分享action, 遍历所有的APP, 显示符合这个action的应用, 按规则启动隐式Intent

  • 简单数据
    通过Intent
  • 文件
    通过V4包中的FileProvider

最近需要打包C++文件成so库,按照网上教程同步的方式,build/intermediates下始终没有ndk文件中的so库

当前环境
windows 10
Android Studio3.5.3
NDK r21

  • 配置NDK环境变量
    我的电脑 右键–>属性–>高级–>环境变量 path中添加ndk-bundle路径
  • 新建jni文件
    src/main/java同级目录下src/main/jni
    将c++文件和头文件全部放入
  • jni目录下新建Android.mk文件
    你的so库名称对应java类中System.loadLibrary(“so库名称”)
    1
    2
    3
    4
    5
    6
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)

    LOCAL_MODULE := 你的so库名称
    LOCAL_SRC_FILES := 你的.cpp
    include $(BUILD_SHARED_LIBRARY)
  • jni目录下新建Application.mk文件
    APP_ABI := all
    打包所有支持cpu架构
  • 在jni目录下打开power shell
    输入ndk-build
    ndk-build.png
    jni同级目录下会有libs/* 各平台so库文件
  • app build.gradle中配置so库路径
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    android{
    //***
    sourceSets {
    main() {
    jniLibs.srcDirs = ['src/main/libs']
    jni.srcDirs = []
    }
    }
    }


Android 优化篇

布局优化/绘制优化
  • 原则
    避免嵌套过多,可采用约束布局
    1
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
  • 工具
    Android Studio在tools菜单栏选择layout工具查看
    Layout Inspector

内存优化
  • 原则
    避免创建过多不必要的对象,尤其是在循环中
    比如不要在onDraw中new Paint

  • 工具
    查询内存泄漏
    LeakCanary

    cpu优化
  • 原则
    避免多次执行同一个耗时方法

  • 工具
    TraceView 可找出单次执行耗时方法和多次调用的方法
    Android5.0 AS3.0之后可使用Profiler

网络优化

车机项目中使用本地较多,暂略

电量优化

昝略

apk体积优化
  • 原则
    1. 去除不用的资源
    2. 开启混淆
    3. 新型构建工具Bundle
  • 工具
    Lint
    静态代码检测工具
    Lint

双击Shift开启搜索,输入去除多余资源

最近有接到分页的需求, 想当然准备使用监听onScroll然后分段加载, 看到谷歌爸爸出品了Jetpack系列文章有Paging Lib于是点开看了下, 介绍的太复杂了有木有!!! room什么鬼, ViewModel云云, 抽丝剥茧
简单Demo入门, 记录如下

新建Activity, 我使用的Fragment+ViewModel的方式, 很方便
Fragment+ViewModel

####1.导入依赖

1
2
3
4
5
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.paging:runtime:1.0.0'
implementation 'com.android.support:recyclerview-v7:27.0.2'
//可选, 我习惯使用约束布局
implementation 'com.android.support.constraint:constraint-layout:1.1.3'

版本不同方法可能有差异, 建议初学使用我这的版本, 方便跟着后面走
首先照着后面的代码复制一遍, 大概知道有哪些东西
####2.新建实体类

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
class Concert {

public int id;
private String name;

public Concert(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "Concert{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}

@Override
public int hashCode() {
return id + name.hashCode();
}

@Override
public boolean equals( Object obj) {
if (obj instanceof Concert){
boolean b = ((Concert) obj).id == id && ((Concert) obj).name.equals(name);
return b;
}else {
return false;
}
}
}

//使用到的布局R.layout.paged_list_fragment
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="@+id/pagedlist"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="256dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

//R.layout.item_concert_list
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

####3.新建DataSource实现类

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
import android.arch.paging.PositionalDataSource;
import android.support.annotation.NonNull;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class PositionPageDataSource extends PositionalDataSource<Concert> {

@Override
public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Concert> callback) {
int requestedLoadSize = params.requestedLoadSize;
Log.d("ldo","PositionPageDataSource loadInitial "+requestedLoadSize);
List<Concert> subList = getSubList(0, requestedLoadSize);
callback.onResult(subList,0,40);//初始化加载从index == 0开始 40假设为数据总数
}

@Override
public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Concert> callback) {
Log.d("ldo","PositionPageDataSource loadRange");
callback.onResult(getSubList(params.startPosition,params.loadSize));
}

public List<Concert> getSubList(int start, int end) {
Log.d("ldo", "getSubList");
ArrayList<Concert> strings = new ArrayList<>();
for (int i = 0; i < end; i++) {
strings.add(new Concert(start, "" + i));
}
return strings;
}
}

//Factory
import android.arch.paging.DataSource;

public class DataSourceFactorty extends DataSource.Factory<Integer,Concert> {

@Override
public DataSource<Integer,Concert> create() {
return new PositionPageDataSource();
}
}

####4.新建Adapter

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
import android.arch.paging.PagedListAdapter;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;


public class ConcertAdapter extends PagedListAdapter<Concert, ConcertAdapter.ViewHolder> {

ConcertAdapter() {
super(showDiffCallBack);
}

static DiffUtil.ItemCallback<Concert> showDiffCallBack = new DiffUtil.ItemCallback<Concert>() {

@Override
public boolean areItemsTheSame(@NonNull Concert oldItem, @NonNull Concert newItem) {
return (oldItem).id == (newItem).id;
}

@Override
public boolean areContentsTheSame(@NonNull Concert oldItem, @NonNull Concert newItem) {
return oldItem.equals(newItem);

}
};

@NonNull
@Override
public ConcertAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_concert_list, null));
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Concert concert = getItem(position);
holder.v.setText(concert.getId()+"");
}

public class ViewHolder extends RecyclerView.ViewHolder {
TextView v;

public ViewHolder(@NonNull View itemView) {
super(itemView);
this.v = itemView.findViewById(R.id.textView);
}

}
}

####5.新建Fragment

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
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.paging.LivePagedListBuilder;
import android.arch.paging.PagedList;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.example.android.displayingbitmaps.R;


public class PagedListFragment extends Fragment {



private RecyclerView recyclerView;
public static final int INITIALLOADSIZE = 40;//初始加载数量
public static final int RELOADSIZE = 20;//往下滑动加载数量
private LiveData<PagedList<Concert>> data;
private ConcertAdapter adapter;


public static PagedListFragment newInstance() {

return new PagedListFragment();
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {

View inflate = inflater.inflate(R.layout.paged_list_fragment, container, false);

recyclerView = inflate.findViewById(R.id.recyclerView);
return inflate;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);


PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(INITIALLOADSIZE / 2) // 分页加载的数量
.setEnablePlaceholders(false) // 当item为null是否使用PlaceHolder展示
.setInitialLoadSizeHint(INITIALLOADSIZE) // 预加载的数量, 与分页加载的数量成倍数关系
// .setPrefetchDistance(5)
.build();
data = new LivePagedListBuilder(new DataSourceFactorty(), config).build();//可以放在ViewModel层处理
data.observe(this, new Observer<PagedList<Concert>>() {
@Override
public void onChanged(@Nullable PagedList<Concert> concerts) {
Log.d("ldo","onChanged "+concerts.size());
adapter.submitList(concerts);//调用PagedListAdapter中的方法绑定
}
});
adapter = new ConcertAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(adapter);
}

}

有一个大概认知之后简单介绍一下各个部分

  • DataSource
    数据处理核心, 最终回调到PagedList
  • LivePagedListBuilder
    关联DataSource和观察数据
  • PagedList
    可以理解为存储数据的集合容器, 不需要我们过多关注
  • PagedListAdapter
    配合recycleview使用

需要注意的是adapter.submitList(concerts)使用父类的方法设置集合, 采取自己设置list然后notifyDataChaged的方式, PositionPageDataSource里面的方法不会回调


####不同DataSource实现类的区别
目前有三种DataSource实现方式, 上面使用的是PositionalDataSource, 想必你已经看到效果了

  • PositionalDataSource
    适用于上拉加载更多的场景, 如京东商城列表浏览, 知乎等
  • ItemKeyedDataSource
    适用于下一part内容依赖当前某个key来获取, 如获得下一part的专辑列表, 需要传入当前歌手来获取
  • PageKeyedDataSource
    适用于有明显上下页的场景, 如支持上一章,下一章跳转可以使用此种方式

  • Q1:Error: INSTALL_FAILED_INVALID_APK
    关闭 File/Setting/Build/instant run

instant run .png

  • Q2:Installation failed with message Failed to commit install session 872031752 with command pm install-commit 872031752. Error: INSTALL_FAILED_INVALID_APK: /data/app/vmdl872031752.tmp/1_slice__ signatures are inconsistent
    上面那个方式未解决的话, 重新clean一次项目, 在运行

clean.png