获取签名文件MD5

获取签名文件MD5

1. 获取MD5的一般方式

1.1 有签名文件(.keystore)的情况下:

keytool -list -v -keystore XXX.keystore

1.2 有apk包的情况下:
1
keytool -printcert -jarfile XXX.apk
1.3 解压apk包,拿到META-INF/CERT.RSA文件:
1
keytool -printcert -file CERT.RSA

2. 获取MD5最有效的方式

2.1 点击运行此gradle的Task
2.2 执行gradle命令:
1
./gradlew signingReport
如果没有对应的gradle task任务 需要到AndroidStudio设置中设置。

Android Studio中Setting -> Experimental -> 取消勾选 Gradle栏目下的 Only include test tasks in the Gradle task list generated during Gradle Sync 选项 -> 重新构建项目

android EditText 限定中文个数与英文个数的解决方式

android EditText 限定中文个数与英文个数的解决方式

EditText 限定中文8个英文16个的解决方法。在EditText上控件提供的属性中有限定最大最小长度的方法。可是,对于输入时,限定中文8个英文16个时,怎么办?相当于一个中文的长度是两个英文的长度。原理就不说了。自己看一下android的源代码。以上直接上代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final int maxLen = 16;
private InputFilter filter = new InputFilter() {@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {int dindex = 0;
int count = 0;
while (count <= maxLen && dindex < dest.length()) {char c = dest.charAt(dindex++);
if (c < 128) {count = count + 1;
} else {count = count + 2;
}}if (count > maxLen) {return dest.subSequence(0, dindex - 1);
}int sindex = 0;
while (count <= maxLen && sindex < source.length()) {char c = source.charAt(sindex++);
if (c < 128) {count = count + 1;
} else {count = count + 2;
}}if (count > maxLen) {sindex--;
}return source.subSequence(0, sindex);
}};

使用例如以下:

1
editText.setFilters(new InputFilter[]{filter});
使用adb导出某个app

使用adb导出某个app

第一步查看app的包名

利用monitor监视要打开的app

1
2
3
PS D:\log> adb shell am monitor
##这时点击运行手机上要看包名的app,就可以看到启动的包名了
** Activity resuming: com.xxx.xxx

第二步 查看包存放路径

第一步可看到打开的app包名是com.xxx.xxx,接下来查看该包存放路径

1
2
PS D:\log> adb shell pm path com.xxx.xxx
package:/data/app/com.xxx.xxx-vgd98TTQzWZTy_qnmQMcIg==/base.apk

第三步 导出到电脑上

将包名 为com.xxx.xxx 的apk导出到电脑

1
PS D:\log> adb pull /data/app/com.xxx.xxx-vgd98TTQzWZTy_qnmQMcIg==/base.apk
查看 Android 手机的 CPU 架构

查看 Android 手机的 CPU 架构

查看 Android 手机的 CPU 架构
Win + R 然后输入 cmd
执行:

  1. adb shell
  2. cat /proc/cpuinfo

PD1938:/ $ cat /proc/cpuinfo
processor : 0
BogoMIPS : 52.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x41
CPU architecture: 8 (7 表示arm-v7,8表示arm-v8)
CPU variant : 0x1
CPU part : 0xd05
CPU revision : 0

processor : 1
BogoMIPS : 52.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x1
CPU part : 0xd05
CPU revision : 0

processor : 2
BogoMIPS : 52.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x1
CPU part : 0xd05
CPU revision : 0

Android 事件分发流程

Android 事件分发流程

前面一篇文章中我们分析了App返回按键的分发流程,从Native层到ViewRootImpl层到DocorView层到Activity层,以及在Activity中的dispatchKeyEvent方法中分发事件,最终调用了Activity的finish方法,即销毁Activity,所以一般情况下假如我们不重写Activity的onBackPress方法或者是onKeyDown方法,当我们按下并抬起返回按键的时候默认都是销毁当前Activity。而本文中我们主要介绍触摸事件的分发流程,从Native层到Activity层触摸事件的分发了流程和按键的分发事件都是类似的,这里我们可以根据异常堆栈信息看一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
at com.example.aaron.helloworld.MainActivity.dispatchTouchEvent(MainActivity.java:103)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2359)
at android.view.View.dispatchPointerEvent(View.java:8698)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4530)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4388)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3924)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3977)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3943)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4053)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3951)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4110)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3924)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3977)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3943)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3951)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3924)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6345)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6301)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6254)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6507)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)

这样经过一系列的方法调用之后最终调用了Activity的dispatchTouchEvent方法,而我们也是从Activiyt的dispatchTouchEvent方法开始对触摸事件的分发进行分析。

在具体查看Activity的dispatchTouchEvent方法之前我们先简单介绍一下触摸事件,触摸事件是由一个触摸按下事件、N个触摸滑动事件和一个触摸抬起事件组成的,通常的一个触摸事件中只能存在一个触摸按下和一个触摸抬起事件,但是触摸滑动事件可以有零个或者多个。好了,知道这个概念以后,下面我们就具体看一下Activity中的dispatchTouchEvent的实现逻辑。

1
2
3
4
5
6
7
8
9
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Android 基础知识总结

Android 基础知识总结

Activity生命周期

img

Fragment生命周期

img

Activity****四种启动模式

  • standard : 标准模式,每次启动Activity都会创建一个新的Activity实例,并且将其压入任务栈栈顶,而不管这个Activity是否已经存在。Activity的启动三回调(onCreate()->onStart()->onResume())都会执行。

  • singleTop : 栈顶复用模式.这种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,所以它的启动三回调就不会执行,同时Activity的onNewIntent()方法会被回调.如果Activity已经存在但是不在栈顶,那么作用与standard**模式一样.

  • singleTask: 栈内复用模式.创建这样的Activity的时候,系统会先确认它所需任务栈已经创建,否则先创建任务栈.然后放入Activity,如果栈中已经有一个Activity实例,那么这个Activity就会被调到栈顶,onNewIntent(),并且singleTask会清理在当前Activity上面的所有Activity.(clear top)

  • singleInstance : 加强版的singleTask模式,这种模式的Activity只能单独位于一个任务栈内,由于栈内复用的特性,后续请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了

Anroid开发艺术探究

Anroid开发艺术探究

1.onStart 和 onResume、onPause和onStop从描述上来看差不多,对我们来说有什么实质的不同?
onStart 和 onStop是从Activity是否可见这个角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度来回调的,除了这种区别,没有其他区别

2.假设当前Activity为A,如果这时用户打开一个新Activity B,那么 B 的onResume()和 A 的onPaus()哪个先执行呢?
启动Activity的请求会由Instrumentation来处理,然后它通过Binder向AMS发请求,AMS内部维护着一个ActivityStack并负责栈内的Activvity的状态,AMS通过
ActivityThread去同步Activity的状态从而完成生命周期方法的调用。在ActivityStack中的resumeTopActivityInnerLocked方法中,在新Acctivity启动之前,
栈顶的Activity需要先onPause()后,新Activity才能启动。最终在ActivityStackSupervisor中的realStartActivityLocked方法会调用scheduleLaunchActivity,
接着完成新Activity的onCreate、onStart、onResume的调用过程。因此,可以得到结论,是旧Activity先onPause,然后新Activity再启动。

3.Activity被销毁并重建后,我们去获取之前的存储字符串,接受的位置可以是onRestoreInstanceState 或者 onCreate 方法,两者区别是?
onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState 一定是有值的,我们不用额外的拍断是否为空;
但是onCreate不行,onCreate如果正常启动的话,其参数Bundle savedInstanceState为null, 所以必须要额外判断。

4.onSaveInstanceState和onRestoreInstanceState,在正常流程下会出发么?
系统只在Activity异常种植的时候才会调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,其他情况不会出发这个过程。

5.比如目前任务栈S1中的情况为ABC,这个时候Activity D 以singleTask模式请求启动,其所需要的任务栈为 S2,由于S2和D的实例均不存在,所以系统会先创建
任务栈S2,然后在创建D的实例并将其入栈到S2.

6.TaskAffinity任务相关性。
这个参数标识一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。可以为每个Activity都单独制定TaskAffinity属性,
这个属性必须不能和包名相同,否则就相当于没有制定。TaskAffinity属性主要和singleTask启动模式或者allTaskReparenting属性配对使用。当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity回运行在名字和TaskAffinity相同的任务栈中。
当TaskAffinity和allowTaskReparenting结合的时候,会产生特殊的效果。当一个应用A启动了应用B的某个Activity后,如果这个Activity的allTaskReparenting属性为true的话,那么当应用B被启动后,词Activity回直接从应用A的任务栈转移到应用B的任务栈中。

7.SharedPerences是否安全?
SharedPreferences不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失,因为SharedPreferences底层是通过读/写XML文件来实现的,并发会出问题。
每个应用的SharedPreferences文件都在当前包所在的data目录下查看到,目录位于 /data/data/pacakage name/shared_prefs目录下。
SharedPreferences也属于文件的一种,但是由于系统对他的读/写有一定的缓存策略,即在内存中有一份ShareePreferences文件的缓存,因此在多进程下,系统对它的读写诗不可靠的,当面对高并发的读/写访问,有很大几率丢失文件。

8.Serializable接口的原理及serialVersionUID作用?
想让一个对象实现序列化,只需要这个类实现Serializable接口并声明一个serialVersionUID即可。实际上,甚至连这个serialVersionUID也不是必须的。如何进行对象的序列化和反序列化也很简单,只需要采用ObjectOutputStream和ObjectInputStream,readObject()writeObject()函数。
serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前累的serialVersionUID相同时才能够正常的被反序列化。
serialVersionUID的详细工作机制时这样的:序列化的时候系统把当前类的serialVersionUID写入序列化文件中,当反序列化时候系统回去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本时相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变化,比如成员变量的数量、类型可能发生了改变,这个时候时无法正常反序列化的。

9.Parcelable接口原理?
Parcelable也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化并通过Intent和Binder传递。
Parcel内部包装了可序列化的数据,可以在Binder中自由传输。
序列化的功能由writeToParcel方法来完成,最终时通过Parcel的一系列write方法来完成的;
反序列化功能由CREATOR来完成,其内部表明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程。

应用程序返回按键执行流程

应用程序返回按键执行流程

从这篇文章中我们开始分析android系统的事件分发流程,其实网上已经有了很多关于android系统的事件分发流程的文章,奈何看了很多但是印象还不是很深,所以这里总结一番。
android系统的事件分发流程分为很多部分:

  • Native层 –> ViewRootImpl层 –> DecorView层 –> Activity层 –> ViewGroup层 –> View层

所以android系统的事件分发流程是从Native层开始的,然后分发到ViewRootImpl中,然后分发到DecorView层,然后分发到ViewGroup层,最后分发到View层中。下面我们将从Native层开始分析事件的分发流程。

在Native层android系统的事件流程:

  • Android系统是从从底层驱动中获取各种原始的用户消息,包括按键、触摸屏、鼠标、滚迹球等用户事件消息。

  • 在获取用户消息之后,android系统会对最原始的消息进行预处理,包括两个方面:一方面,将消息转化成系统可以处理的消息事件;另一方面,处理一些特殊的事件,比如HOME、MENU、POWER键等处理(前面的几篇文章中我们已经分析了系统按键处理逻辑的执行流程)。

  • 将处理后的消息事件分发到各个应用进程,这个需要使用IPC机制,Android系统使用管道来进行消息的传递。

  • Android系统使用InputManager类来管理消息,而具体的功能则是通过InputReaderThread和InputDispatcherThread两个线程来实现。其中InputReaderThread线程负责消息的读取,而InputDispatcherThread则负责消息的预处理和分发到各个应用进程中。

  • Acitivty系统在SystemServer进程中启动WindowManagerService服务,然后在WindowManagerService服务中启动InputManagerService服务。

可以看到在Native层,主要创建了两个两个线程,其中一个用于读取消息,另一个用于分发消息,消息经过分发最终会上传至App中。

在ViewRootImpl层android系统的事件流程

在Native层的事件分发线程中,经过事件的分发流程,最终会调用InputEventSender的dispatchInputEventFinished方法,可以看一下具体代码的实现:

1
2
3
private void dispatchInputEventFinished(int seq, boolean handled) {
onInputEventFinished(seq, handled);
}

在dispatchInputEventFinished方法中我们最终调用的是onInputEventFinished方法,然后我们查看onInputEventFinished方法的实现,发现其是一个空方法。。。,好吧,经过分析我们发现,Native层最终调用的并不是InputEventSender,而是调用InputEventSender的子类ImeInputEventSender,即ImeInputEventSender的onInputEventFinished方法,该类定义在源文件InputMethodManager中:

电源开关机按键事件流程

电源开关机按键事件流程

前面我们讲解了系统截屏按键处理流程,HOME按键处理流程,今天再来讲解一下电源开关机按键事件流程,当然这也是系统按键处理流程方面的最后一篇博客了。

和截屏按键、HOME按键的处理流程类似,电源按键由于也是系统级别的按键,所以对其的事件处理逻辑是和截屏按键、HOME按键类似,不在某一个App中,而是在PhoneWindowManager的dispatchUnhandledKey方法中。所以和前面两篇类似,这里我们也是从PhoneWindowManager的dispatchUnhandledKey方法开始我们今天电源开关机按键的事件流程分析。

下面首先看一下dispatchUnhandledKey方法的实现逻辑:

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
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
...
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;

// Check for fallback actions specified by the key character map.
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}

if (fallbackAction != null) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ " metaState=" + Integer.toHexString(fallbackAction.metaState));
}

final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);

if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}

if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}
...
return fallbackEvent;
}

通过前面两篇文章的分析
android源码解析(二十六)–>截屏事件流程&nbsp;&nbsp;
android源码解析(二十七)–>HOME事件流程
我们知道关于系统按键的处理逻辑被下放到了interceptFallback方法中,所以我们继续看一下interceptFallback方法的实现逻辑。

HOME事件流程

HOME事件流程

上一篇文章中我们介绍了android系统的截屏事件,由于截屏事件是一种系统全局处理事件,所以事件的处理逻辑不是在App中执行,而是在PhoneWindowManager中执行。而本文我们现在主要讲解android系统中HOME按键的事件处理,和截屏事件类似,这里的HOME按键也是系统级别的按键事件监听,所以其处理事件的逻辑也应该和截屏事件处理流程类似,从上一篇文章的分析过冲中我们不难发现,系统级别的按键处理逻辑其实都是在PhoneWindowManager中,所以HOME按键的处理逻辑也是在PhoneWindowManager的dispatchUnhandledKey方法中执行,那么我们就从dispatchUnhandleKey方法开始分析HOME按键的处理流程。

好吧我们看一下PhoneWindowManager的dispatchUnhandleKey方法的实现:

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
@Override
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
...
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;

// Check for fallback actions specified by the key character map.
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}

if (fallbackAction != null) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ " metaState=" + Integer.toHexString(fallbackAction.metaState));
}

final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);

if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}

if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}

if (DEBUG_INPUT) {
if (fallbackEvent == null) {
Slog.d(TAG, "No fallback.");
} else {
Slog.d(TAG, "Performing fallback: " + fallbackEvent);
}
}
return fallbackEvent;
}

通过查看源码,我们重点看一下dispatchUnhandledKey方法中调用的interceptFallback方法,关于HOME按键的处理逻辑也是在这个方法体中的,所以继续看一下interceptFallback方法的实现:


:D 一言句子获取中...