SanfenR的博客


  • 首页

  • 关于

  • 归档

  • 搜索
close
SanfenR的博客

Android-JNI&NDK(一)入门

发表于 2017-10-16 |

[TOC]

Android-JNI&NDK(一)入门

一. 前言

最近项目中要求要使用so库实现数据加密,来提高数据的安全性,因为好久没用过Android调用native方法,特别写这一篇回忆一下用法。

官方教程

二. 简介

NDK(Native Development Kit)

  1. Android NDK 是一套允许您使用原生代码语言(例如 C 和 C++)实现部分应用的工具集。在开发某些类型应用时,这有助于您重复使用以这些语言编写的代码库。
  2. NDK的作用
    • 从设备获取卓越性能以用于计算密集型应用,例如游戏或物理模拟。
    • 重复使用您自己或其他开发者的 C 或 C++ 库。
    • 代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。

JNI(Java Native Interface)

  1. JNI 是本地编程接口,它使得在 Java 虚拟机内部运行的 Java 代码能够与用其它语言(如 C、C++)编写的代码进行交互。
  2. JNI的作用
    • 可以让java调用c语言的代码。

三. NDK安装

在

SDKManager```下载更新ndk
1
2
3
4
5
6
7
8
9
10
11
12
![NDK下载](http://ohqvqufyf.bkt.clouddn.com/ndk_download.png)
## 四. JNI使用
### 创建项目
1. 首先创建一个AS项目。
![image](http://ohqvqufyf.bkt.clouddn.com/ndk_createproject.png)
2. 配置gradle.properties文件

    android.useDeprecatedNdk=true
1
2
3
4
![image](http://ohqvqufyf.bkt.clouddn.com/ndk_%E9%85%8D%E7%BD%AE1.png)
3. 在local.properties中加入ndk和sdk的路径:
ndk.dir=/Users/xxx/Library/Android/sdk/ndk-bundle sdk.dir=/Users/xxx/Library/Android/sdk
1
2
3
![image](http://ohqvqufyf.bkt.clouddn.com/ndk_%E9%85%8D%E7%BD%AE2.png)
4. 配置build.gradle中配置ndk的生成的so名字和支持的CPU平台(下文会介绍在如何Android.mk中去配置)。
ndk{ moduleName "hello" //生成的so文件名字,调用C程序的代码中会用到该名字 abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种平台下的so库 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
![image](http://ohqvqufyf.bkt.clouddn.com/ndk_%E9%85%8D%E7%BD%AE3.png)
项目的准备工作做完了,接下来是代码的部分。
### 使用JNI
1. 创建native方法
```java
public class MainActivity extends AppCompatActivity {
TextView mHelloText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHelloText = (TextView) findViewById(R.id.hello);
}
public native String sayHelloByJNI();
}
![image](http://ohqvqufyf.bkt.clouddn.com/ndk_%E5%88%9B%E5%BB%BAnative%E6%96%B9%E6%B3%95.png)
  1. 使用

    -jni```命令MainActivity.java生成JNI的.h文件。
    1
    2

    cd app/src/main/java

    javah -jni co.fensan.android.jnidemo.MainActivity

    1
    2
    3
    ![image](http://ohqvqufyf.bkt.clouddn.com/ndk_%E5%88%9B%E5%BB%BAh.png)
    发现在当前目录生成了``` co_fensan_android_jnidemo_MainActivity.h
  2. 创建JNI目录并将.h文件移动到jni目录
    image

然后创建.cpp文件

1
2
3
4
5
6
#include "co_fensan_android_jnidemo_MainActivity.h"
JNIEXPORT jstring JNICALL
Java_co_fensan_android_jnidemo_MainActivity_sayHelloByJNI(JNIEnv *env, jobject instance) {
return env->NewStringUTF("hello jni!");
}

image

  1. 在MainActivity中引入hello库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("hello");
}
TextView mHelloText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHelloText = (TextView) findViewById(R.id.hello);
mHelloText.setText(sayHelloByJNI());
}
public native String sayHelloByJNI();
}
![image](http://ohqvqufyf.bkt.clouddn.com/ndk_%E5%BC%95%E5%85%A5lib.png)

五. 运行结果

image

源码传送门

SanfenR的博客

解决java.lang.SecurityException

发表于 2017-10-10 |

解决java.lang.SecurityException: Invalid signature file digest for Manifest main attributes

当项目依赖其他jar包的时候,打出的jar包执行出错,抛出这个异常。

原因:因为依赖jar包中的META-INF中有多余的.SF文件与当前jar包冲突,

  • 解决方案 一

    1
    在打包前删除依赖jar包的.SF文件
  • 解决方案 二

    1
    2
    3
    在打完的jar包执行
    zip -d your.jar 'META-INF/.SF' 'META-INF/.RSA' 'META-INF/*SF'
SanfenR的博客

git命令

发表于 2017-04-12 |

1.

1
git init #初始化本地git仓库(创建新仓库)

2.

1
git config --global user.name "xxx" # 配置用户名

3.

1
git config --global user.email "xxx@xxx.com" # 配置邮件

4.

1
git config --global color.ui true # git status等命令自动着色

5.

1
git config --global color.status auto

6.

1
git config --global color.diff auto

7.

1
git config --global color.branch auto

8.

1
git config --global color.interactive auto

9.

1
git config --global --unset http.proxy # remove proxy configuration on git

10.

1
git clone git+ssh://git@192.168.53.168/VT.git # clone远程仓库

11.

1
git status # 查看当前版本状态(是否修改)

12.

1
git add xyz # 添加xyz文件至index

13.

1
git add . # 增加当前子目录下所有更改过的文件至index

14.

1
git commit -m 'xxx' # 提交

15.

1
git commit --amend -m 'xxx' # 合并上一次提交(用于反复修改)

16.

1
git commit -am 'xxx' # 将add和commit合为一步

17.

1
git rm xxx # 删除index中的文件

18.

1
git rm -r * # 递归删除

19.

1
git log # 显示提交日志

20.

1
git log -1 # 显示1行日志 -n为n行

21.

1
git log -5

22.

1
git log --stat # 显示提交日志及相关变动文件

23.

1
git log -p -m

24.

1
git show dfb02e6e4f2f7b573337763e5c0013802e392818 # 显示某个提交的详细内容

25.

1
git show dfb02 # 可只用commitid的前几位

26.

1
git show HEAD # 显示HEAD提交日志

27.

1
git show HEAD^ # 显示HEAD的父(上一个版本)的提交日志 ^^为上两个版本 ^5为上5个版本

28.

1
git tag # 显示已存在的tag

29.

1
git tag -a v2.0 -m 'xxx' # 增加v2.0的tag

30.

1
git show v2.0 # 显示v2.0的日志及详细内容

31.

1
git log v2.0 # 显示v2.0的日志

32.

1
git diff # 显示所有未添加至index的变更

33.

1
git diff --cached # 显示所有已添加index但还未commit的变更

34.

1
git diff HEAD^ # 比较与上一个版本的差异

35.

1
git diff HEAD -- ./lib # 比较与HEAD版本lib目录的差异

36.

1
git diff origin/master..master #比较远程分支master上有本地分支master上没有的

37.

1
git diff origin/master..master --stat # 只显示差异的文件,不显示具体内容

38.

1
git remote add origin git+ssh://git@192.168.53.168/VT.git # 增加远程定义(用于push/pull/fetch)

39.

1
git branch # 显示本地分支

40.

1
git branch --contains 50089 # 显示包含提交50089的分支

41.

1
git branch -a # 显示所有分支

42.

1
git branch -r # 显示所有原创分支

43.

1
git branch --merged # 显示所有已合并到当前分支的分支

44.

1
git branch --no-merged # 显示所有未合并到当前分支的分支

45.

1
git branch -m master master_copy # 本地分支改名

46.

1
git checkout -b master_copy # 从当前分支创建新分支master_copy并检出

47.

1
git checkout -b master master_copy # 上面的完整版

48.

1
git checkout features/performance # 检出已存在的features/performance分支

49.

1
git checkout --track hotfixes/BJVEP933 # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支

50.

1
git checkout v2.0 # 检出版本v2.0

51.

1
git checkout -b devel origin/develop # 从远程分支develop创建新本地分支devel并检出

52.

1
git checkout -- README # 检出head版本的README文件(可用于修改错误回退)

53.

1
git merge origin/master # 合并远程master分支至当前分支

54.

1
git cherry-pick ff44785404a8e # 合并提交ff44785404a8e的修改

55.

1
git push origin master # 将当前分支push到远程master分支

56.

1
git push origin :hotfixes/BJVEP933 # 删除远程仓库的hotfixes/BJVEP933分支

57.

1
git push --tags # 把所有tag推送到远程仓库

58.

1
git fetch # 获取所有远程分支(不更新本地分支,另需merge)

59.

1
git fetch --prune # 获取所有原创分支并清除服务器上已删掉的分支

60.

1
git pull origin master # 获取远程分支master并merge到当前分支

61.

1
git mv README README2 # 重命名文件README为README2

62.

1
git reset --hard HEAD # 将当前版本重置为HEAD(通常用于merge失败回退)

63.

1
git rebase

64.

1
git branch -d hotfixes/BJVEP933 # 删除分支hotfixes/BJVEP933(本分支修改已合并到其他分支)

65.

1
git branch -D hotfixes/BJVEP933 # 强制删除分支hotfixes/BJVEP933

66.

1
git ls-files # 列出git index包含的文件

67.

1
git show-branch # 图示当前分支历史

68.

1
git show-branch --all # 图示所有分支历史

69.

1
git whatchanged # 显示提交历史对应的文件修改

70.

1
git revert dfb02e6e4f2f7b573337763e5c0013802e392818 # 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818

71.

1
git ls-tree HEAD # 内部命令:显示某个git对象

72.

1
git rev-parse v2.0 # 内部命令:显示某个ref对于的SHA1 HASH

73.

1
git reflog # 显示所有提交,包括孤立节点

74.

1
git show HEAD@{5}

75.

1
git show master@{yesterday} # 显示master分支昨天的状态

76.

1
git log --pretty=format:'%h %s' --graph # 图示提交日志

77.

1
git show HEAD~3

78.

1
git show -s --pretty=raw 2be7fcb476

79.

1
git stash # 暂存当前修改,将所有至为HEAD状态

80.

1
git stash list # 查看所有暂存

81.

1
git stash show -p stash@{0} # 参考第一次暂存

82.

1
git stash apply stash@{0} # 应用第一次暂存

83.

1
git grep "delete from" # 文件中搜索文本“delete from”

84.

1
git grep -e '#define' --and -e SORT_DIRENT

85.

1
git gc

86.

1
git fsck

87.

1
git log --oneline --abbrev-commit --all --graph --decorate --color # 显示所有branch提交历史图

88.

1
git checkout --patch commitid filename # 合并指定commit的指定文件

SanfenR的博客

Android修改包名

发表于 2017-04-11 |

优雅的修改包名

在开发中碰到修改包名的时候,需要修改许多文件,下面演示一种比较方便的修改包名的方法。

  1. 在项目列表中取消勾选Compact Empty Middle Package

before

  1. 取消勾选显示出项目的所有目录结构

after

  1. 修改包名
    rename_package
  1. 修改gradle中的包名
    rename_gradle

修改完成。

SanfenR的博客

Homebrew常用操作

发表于 2017-04-07 |

Homebrew

  • 更新Homebrew
  • 更新包 (formula))
  • 清理旧版本
  • 锁定不想更新的包
  • 其他几个常用命令

更新Homebrew

要获取最新的包的列表,首先得更新 Homebrew 自己。这可以用 brew update 办到。

1
brew update

更新包(formula)

要获取最新的包的列表,首先得更新 Homebrew 自己。这可以用 brew update 办到。

1
brew outdated

更新需要更新的包

1
2
brew upgrade # 更新所有的包
brew upgrade $FORMULA # 更新指定的包

清理旧版本

一般情况下,新版本安装了,旧版本就不需要了。我会用brew cleanup 清理旧版本和缓存文件。Homebrew 只会清除比当前安装的包更老的版本,所以不用担心有些包没更新但被删了。

1
2
3
brew cleanup # 清理所有包的旧版本
brew cleanup $FORMULA # 清理指定包的旧版本
brew cleanup -n # 查看可清理的旧版本包,不执行实际操作

锁定不想更新的包

如果经常更新的话,brew update 一次更新所有的包是非常方便的。但我们有时候会担心自动升级把一些不希望更新的包更新了。数据库就属于这一类,尤其是 PostgreSQL 跨 minor 版本升级都要迁移数据库的。我们更希望找个时间单独处理它。这时可用 brew pin 去锁定这个包,然后 brew update 就会略过它了。

1
2
brew pin $FORMULA # 锁定某个包
brew unpin $FORMULA # 取消锁定

其他几个常用命令

brew info 可以查看包的相关信息,最有用的应该是包依赖和相应的命令。比如 Nginx 会提醒你怎么加 launchctl ,PostgreSQL 会告诉你如何迁移数据库。这些信息会在包安装完成后自动显示,如果忘了的话可以用这个命令很方便地查看。

1
2
brew info $FORMULA # 显示某个包的信息
brew info # 显示安装了包数量,文件数量,和总占用空间

brew deps 可以显示包的依赖关系,我常用它来查看已安装的包的依赖,然后判断哪些包是可以安全删除的。

1
brew deps --installed --tree # 查看已安装的包的依赖,树形显示
SanfenR的博客

四月

发表于 2017-04-05 |
  1. 在 Android Studio 上调试数据库 ( SQLite )

    • 以前 Eclipse 时代,调试 SQLite 都是将数据库文件导出到电脑,然后再用软件打开查看。现在我们用 Android Studio,有没有更方便的方法呢?
  2. 详解7.0带来的新工具类:DiffUtil

    • DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集-》新数据集的最小变化量。
      说到数据集,相信大家知道它是和谁相关的了,就是我的最爱,RecyclerView。
      就我使用的这几天来看,它最大的用处就是在RecyclerView刷新时,不再无脑mAdapter.notifyDataSetChanged()。
SanfenR的博客

ActivityManager判断app运行状态

发表于 2017-02-28 |

摘要: 在后台与网络交互中,需要使用Service来和服务器保持连接,因此Service在被进程Kill掉后,主动开启Service是必要的,因此首先的检测Service是否已启动

检测Service是否已启动

1
2
3
4
5
6
7
8
9
10
11
public static boolean isServiceRunning(String serviceClassName){
final ActivityManager activityManager = (ActivityManager)Application.getContext().getSystemService(Context.ACTIVITY_SERVICE);
final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo runningServiceInfo : services) {
if (runningServiceInfo.service.getClassName().equals(serviceClassName)){
return true;
}
}
return false;
}

检测Activity是否已启动

1
2
<!-- 权限-->
<uses-permission android:name="android.permission.GET_TASKS"/>
1
2
3
4
5
6
7
8
9
10
11
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> list = am.getRunningTasks(100);
boolean isAppRunning = false;
String MY_PKG_NAME = "com.cyberblue.iitag";
for (RunningTaskInfo info : list) {
if (info.topActivity.getPackageName().equals(MY_PKG_NAME) || info.baseActivity.getPackageName().equals(MY_PKG_NAME)) {
isAppRunning = true;
Log.i(TAG,info.topActivity.getPackageName() + " info.baseActivity.getPackageName()="+info.baseActivity.getPackageName());
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private boolean isLauncherRunnig(Context context) {
boolean result = false ;
List<String> names = getAllTheLauncher();
ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE) ;
List<ActivityManager.RunningAppProcessInfo> appList = mActivityManager.getRunningAppProcesses() ;
for (RunningAppProcessInfo running : appList) {
if (running.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (int i = 0; i < names.size(); i++) {
if (names.get(i).equals(running.processName)) {
result = true ;
break;
}
}
}
}
return result ;
}

判断App是否运行

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
/**
* 判断app是否正在运行
* @param ctx
* @param packageName
* @return
*/
public boolean appIsRunning(Context ctx,String packageName)
{
ActivityManager am = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
if(runningAppProcesses!=null)
{
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
if(runningAppProcessInfo.processName.startsWith(packageName))
{
return true;
}
}
}
return false;
}
/**
* app 是否在后台运行
* @param ctx
* @param packageName
* @return
*/
public boolean appIsBackgroundRunning(Context ctx,String packageName)
{
ActivityManager am = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
if(runningAppProcesses!=null)
{
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
if(runningAppProcessInfo.processName.startsWith(packageName))
{
return runningAppProcessInfo.importance!=RunningAppProcessInfo.IMPORTANCE_FOREGROUND && runningAppProcessInfo.importance!=RunningAppProcessInfo.IMPORTANCE_VISIBLE; //排除无界面的app
}
}
}
return false;
}
SanfenR的博客

合并已经排序的2条链表(day11)

发表于 2017-02-13 |

题目

  • 合并2条链表
    1
    如 1,3,5,7 和 2,4,6,8 -> 1,2,3,4,5,6,7,8

实现

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
ListNode * mergeList(ListNode* list1, ListNode* list2){
if(!list1) return list2;
if(!list2) return list1;
ListNode* head;
ListNode* tail = new ListNode();
head = tail;
while(list1 || list2) {
if(!list2 || list1->value < list2->value) {
tail->next = list1;
tail=tail->next;
list1=list1->next;
} else {
tail->next=list2;
tail=tail->next;
list2=list2->next;
}
}
return head->next;
}
//使用递归
ListNode * mergeList2(ListNode* list1, ListNode* list2){
if(!list1) return list2;
if(!list2) return list1;
ListNode* head;
if(list1->value < list2->value){
head = list1;
head->next = mergeList2(list1->next, list2);
}
else{
head = list2;
head->next = mergeList2(list1,list2->next);
}
return head;
}

源码传送门

SanfenR的博客

打印1到最大的n位数(day10)

发表于 2017-01-20 |

题目

  • 打印1到最大的n位数。如n=4,打印1-9999。

思路

  • 如果直接使用循环会导致int或者long都不够存储,因此使用string来存储数据,这里涉及到字符串以及字符串的加法。

实现

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
void print(const int &n) {
if (n <= 0) {
return;
}
char *number = new char[n + 1];
memset(number, '0', (n + 1) * sizeof(char));
number[n] = '\0';
printMax(number, 0, n);
delete[] number;
}
void printMax(char *number, const int index, const int size) {
if (!number)
return;
if (index == size) {//嵌套截止条件
cout << number << endl;
return;
}
for (int i = 0; i < 10; i++) {
number[index] = (char) (i + '0');//index---当前调整的位数
printMax(number, index + 1, size);
}
}

源码传送门

SanfenR的博客

Android-Fragment懒加载

发表于 2017-01-11 |

ViewPager的预加载

在项目中,经常会遇到ViewPager+TabLayout实现对多个fragment的管理。但是由于ViewPager的预加载(默认的预加载为1),viewpager会调用fragment的onCreateView()进行fragmemt的初始化:

fragment

而在Android中当fragment的onCreateView()会将整个xml文件中的UI控件实例到内存,而且数据的请求初始化基本也是在onCreateView()中进行。导致App启动的时候加载的数据过多。

解决方案

解决这个问题有两种方式,一种是禁止ViewPager的预加载,重写ViewPager,但是该方法会出现左右滑动时会出现卡顿现象,带来不好的用户体验。而另外一种就是我们接下来要讲的通过Fragment的懒加载来实现。当用户切换到某个fragment时再加载。

setUserVisibleHint()方法

懒加载主要使用到fragment中的setUserVisibleHint(),这个方法是当fragment的UI显示状态变化时会被调用。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Set a hint to the system about whether this fragment's UI is currently visible
* to the user. This hint defaults to true and is persistent across fragment instance
* state save and restore.
*
* <p>An app may set this to false to indicate that the fragment's UI is
* scrolled out of visibility or is otherwise not directly visible to the user.
* This may be used by the system to prioritize operations such as fragment lifecycle updates
* or loader ordering behavior.</p>
*
* <p><strong>Note:</strong> This method may be called outside of the fragment lifecycle.
* and thus has no ordering guarantees with regard to fragment lifecycle method calls.</p>
*
* @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
* false if it is not.
*/
public void setUserVisibleHint(boolean isVisibleToUser) {
if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
&& mFragmentManager != null && isAdded()) {
mFragmentManager.performPendingDeferredStart(this);
}
mUserVisibleHint = isVisibleToUser;
mDeferStart = mState < STARTED && !isVisibleToUser;
}

实现

  1. 因为fragment的onCreateView()方法会将xml中的控件加载到内存中,所以我们定义一个空的FragmentLayout。

创建一个空的容器作为预加载界面

1
2
3
4
5
6
7
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/other_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mz.sanfen.lazyfragment.OtherFragment">
</FrameLayout>

并在onCreateView()中初始化这个布局

1
2
3
4
5
6
7
8
9
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
View inflate = inflater.inflate(R.layout.fragment_other, container, false);
frameLayout = (FrameLayout) inflate.findViewById(R.id.one_frame);
Log.e(TAG, "onCreateView: " + frameLayout );
return inflate;
}
  1. 将具体的内容的UI定义到一个子xml中,并在setUserVisibleHint()方法中进行初始化控件和数据加载,并将这个xml放入外部的容器中。

重新创建一个子布局

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/other_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</LinearLayout>

并在setUserVisibleHint()中进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
// Log.e(TAG, "setUserVisibleHint: " + isVisibleToUser );
if (isVisibleToUser) {
if (contentView == null ) {
contentView = LayoutInflater.from(getActivity()).inflate(R.layout.content_other, frameLayout, true);
textView = (TextView) contentView.findViewById(R.id.other_text);
textView.setText("load other fragment");
}
}
}

注意

ViewPager在预加载fragment的时候是先调用setUserVisibleHint(), 然后再是调用onCreateView()方法。

image

源码传送门

123
SanfenR

SanfenR

大成若缺, 大直若屈, 大巧若拙, 大智若愚。

29 日志
8 标签
GitHub
© 2017 SanfenR
由 Hexo 强力驱动
主题 - NexT.Muse

本站总访问量 次, 访客数 人次, 本文总阅读量 次