首页
工具
隐私协议
App Privacy Policy
更多
作品
关于我们
Search
1
android5遇到INSTALL_FAILED_DEXOPT 解决办法
1,664 阅读
2
设置max_connections无效
1,484 阅读
3
FlexboxLayout+recyclerView实现自动换行
1,374 阅读
4
Nginx配置多个域名
1,257 阅读
5
Android P http网络请求失败
1,230 阅读
默认分类
mysql
android
android深入
Jetpack Compose
Android传感器
php
Yii2
windows
webrtc
登录
Search
标签搜索
android
kotlin
webrtc
kurento
mysql
adb
nginx
flutter
rsa
微信
git
Yii2
md5
加密
dart
aes
wechat
windows
小程序
dexopt
Typecho
累计撰写
80
篇文章
累计收到
3
条评论
首页
栏目
默认分类
mysql
android
android深入
Jetpack Compose
Android传感器
php
Yii2
windows
webrtc
页面
工具
隐私协议
App Privacy Policy
作品
关于我们
搜索到
75
篇与
默认分类
的结果
2024-08-13
mac配置zsh环境变量
在 macOS上zsh的环境变量通常可以在以下文件中配置:~/.zshrc ~/.zprofile ~/.zshenv这些文件的作用如下:~/.zshrc:这是 zsh 的主要配置文件。它在每次打开新的 zsh 会话时都会被读取和执行。这是设置环境变量、别名、函数等的主要位置。~/.zprofile:这个文件在 login shell 启动时被读取和执行。它通常用于设置全局环境变量和其他配置。如果你需要在 login shell 启动时设置环境变量,可以将它们放在这个文件中。~/.zshenv:这个文件在任何 zsh 子进程启动时都会被读取和执行。它通常用于设置一些全局性的环境变量,例如 PATH。通常情况下,你可以在 ~/.zshrc 文件中添加环境变量的定义。如果某个变量需要在 login shell 启动时就生效,则可以将它放在 ~/.zprofile中
2024年08月13日
14 阅读
0 评论
0 点赞
2024-04-02
docker nacos添加映射端口
停止容器docker stop 23e477f3f265生成镜像docker commit -m "new-image" 23e477f3f265 nacos-server-new删除旧的容器docker rm -f 23e477f3f265生成新的容器docker run -d --name nacos-server-new -p 8848:8848 -p 9848:9848 -p 7848:7848 --privileged=true -e JVM_XMS=256m -e MODE=standalone -v /mydata/nacos/logs/:/usr/local/nacos/logs -v /usr/local/nacos/conf/:/home/nacos/conf/ nacos-server-new
2024年04月02日
255 阅读
0 评论
0 点赞
2023-10-07
Navicat连接数据库后密码找回【亲测有效】
首先从文件—>导出连接打开在线运行工具 https://tool.lu/coderunner/,运行以下代码:<?php namespace FatSmallTools; class NavicatPassword { protected $version = 0; protected $aesKey = 'libcckeylibcckey'; protected $aesIv = 'libcciv libcciv '; protected $blowString = '3DC5CA39'; protected $blowKey = null; protected $blowIv = null; public function __construct($version = 12) { $this->version = $version; $this->blowKey = sha1('3DC5CA39', true); $this->blowIv = hex2bin('d9c7c3c8870d64bd'); } public function encrypt($string){ $result = FALSE; switch ($this->version) { case 11: $result = $this->encryptEleven($string); break; case 12: $result = $this->encryptTwelve($string); break; default: break; } return $result; } protected function encryptEleven($string){ $round = intval(floor(strlen($string) / 8)); $leftLength = strlen($string) % 8; $result = ''; $currentVector = $this->blowIv; for ($i = 0; $i < $round; $i++) { $temp = $this->encryptBlock($this->xorBytes(substr($string, 8 * $i, 8), $currentVector)); $currentVector = $this->xorBytes($currentVector, $temp); $result .= $temp; } if ($leftLength) { $currentVector = $this->encryptBlock($currentVector); $result .= $this->xorBytes(substr($string, 8 * $i, $leftLength), $currentVector); } return strtoupper(bin2hex($result)); } protected function encryptBlock($block){ return openssl_encrypt($block, 'BF-ECB', $this->blowKey, OPENSSL_RAW_DATA|OPENSSL_NO_PADDING); } protected function decryptBlock($block) { return openssl_decrypt($block, 'BF-ECB', $this->blowKey, OPENSSL_RAW_DATA|OPENSSL_NO_PADDING); } protected function xorBytes($str1, $str2) { $result = ''; for ($i = 0; $i < strlen($str1); $i++) { $result .= chr(ord($str1[$i]) ^ ord($str2[$i])); } return $result; } protected function encryptTwelve($string) { $result = openssl_encrypt($string, 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv); return strtoupper(bin2hex($result)); } public function decrypt($string) { $result = FALSE; switch ($this->version) { case 11: $result = $this->decryptEleven($string); break; case 12: $result = $this->decryptTwelve($string); break; default: break; } return $result; } protected function decryptEleven($upperString) { $string = hex2bin(strtolower($upperString)); $round = intval(floor(strlen($string) / 8)); $leftLength = strlen($string) % 8; $result = ''; $currentVector = $this->blowIv; for ($i = 0; $i < $round; $i++) { $encryptedBlock = substr($string, 8 * $i, 8); $temp = $this->xorBytes($this->decryptBlock($encryptedBlock), $currentVector); $currentVector = $this->xorBytes($currentVector, $encryptedBlock); $result .= $temp; } if ($leftLength) { $currentVector = $this->encryptBlock($currentVector); $result .= $this->xorBytes(substr($string, 8 * $i, $leftLength), $currentVector); } return $result; } protected function decryptTwelve($upperString) { $string = hex2bin(strtolower($upperString)); return openssl_decrypt($string, 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv); } } use FatSmallTools\NavicatPassword; //需要指定版本,11或12 11查不到就用12 //$navicatPassword = new NavicatPassword(12); $navicatPassword = new NavicatPassword(15); //解密 $decode = $navicatPassword->decrypt('your-navicat-password'); echo $decode."\n"; 3.将导出的加密密码替换致“your-navicat-password”,如: $decode = $navicatPassword->decrypt('999111155556666777788889999ABCDEF');
2023年10月07日
570 阅读
0 评论
1 点赞
2023-08-31
Nginx日志文件太大,如何清理
我通过以下命令找出大于1G的文件find / -type f -size +1000000k | xargs ls -lat发现nginx的access.log日志文件很大,如何清理呢,以下三种方式:以一个空文件替换该文件 ,既不需要重启,又不需要切换配置文件cat /dev/null > access.log 进入到nginx 的logs文件夹下,输入truncate清空access.log文件truncate -s 0 access.log 备注: truncate命令可以将一个文件缩小或者扩展到某个给定的大小可以利用该命令和-s选项来特别指定文件的大小删除access.log 文件 并重启nginx
2023年08月31日
923 阅读
0 评论
0 点赞
2023-08-31
是什么杀死了我的进程
dmesg -T| grep -E -i -B100 'killed process'其中-B100表示杀死发生之前的行数。在 Mac 操作系统上省略-T 。
2023年08月31日
548 阅读
0 评论
0 点赞
2023-08-16
docker常用命令
查看容器,-a查看所有docker ps进入容器docker exec -it 容器id /bin/shdocker-compose重启docker-compose -f your_compose.yaml restart 容器id查看容器日志:docker logs -f 容器找其中包含某些内容docker logs 容器名字或者 ID 2>&1 | grep xxx如果需要导出日志文件,可以# grep 的 -i 表示不区分大小写 docker inspect 容器名字或者ID | grep -i logpathinspect查询容器信息// 查询挂载信息 docker inspect 容器id | grep Mounts -A 100重启容器:docker restart 容器id
2023年08月16日
669 阅读
0 评论
0 点赞
2023-04-25
查询数据库大小
查询所有数据库的总大小查询数据库大小:use information_schema; select concat(round(sum(DATA_LENGTH/1024/1024),2),'MB') as data from TABLES;统计一下所有库数据量 每张表数据量=AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTHSELECT SUM(AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTH)/1024/1024 AS total_mb FROM information_schema.TABLES 统计每个库大小:SELECT table_schema,SUM(AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTH)/1024/1024 AS total_mb FROM information_schema.TABLES group by table_schema; 查看指定数据库的大小use information_schema; select concat(round(sum(DATA_LENGTH/1024/1024),2),'MB') as data from TABLES where table_schema='test';1.查看所有数据库各容量大小select table_schema as '数据库', sum(table_rows) as '记录数', sum(truncate(data_length/1024/1024, 2)) as '数据容量(MB)', sum(truncate(index_length/1024/1024, 2)) as '索引容量(MB)' from information_schema.tables group by table_schema order by sum(data_length) desc, sum(index_length) desc;2.查看所有数据库各表容量大小select table_schema as '数据库', table_name as '表名', table_rows as '记录数', truncate(data_length/1024/1024, 2) as '数据容量(MB)', truncate(index_length/1024/1024, 2) as '索引容量(MB)' from information_schema.tables order by data_length desc, index_length desc;3.查看指定数据库容量大小例:查看mysql库容量大小select table_schema as '数据库', sum(table_rows) as '记录数', sum(truncate(data_length/1024/1024, 2)) as '数据容量(MB)', sum(truncate(index_length/1024/1024, 2)) as '索引容量(MB)' from information_schema.tables where table_schema='mysql'; 4.查看指定数据库各表容量大小例:查看mysql库各表容量大小select table_schema as '数据库', table_name as '表名', table_rows as '记录数', truncate(data_length/1024/1024, 2) as '数据容量(MB)', truncate(index_length/1024/1024, 2) as '索引容量(MB)' from information_schema.tables where table_schema='mysql' order by data_length desc, index_length desc;
2023年04月25日
684 阅读
0 评论
0 点赞
2023-01-13
linux命令dd
dd命令的作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。可以用于测试磁盘命令、数据备份或恢复等dd 命令格式如下:dd [bs=<字节数>][cbs=<字节数>][conv=<关键字>][count=<区块数>][ibs=<字节数>][if=<文件>][obs=<字节数>][of=<文件>][seek=<区块数>][skip=<区块数>][--help][--version]dd if=path/to/input_file of=/path/to/output_file bs=block_size count=number_of_blocksif=file 输入文件名,缺省为标准输入。 从file读取,如if=/dev/zero,该设备无穷尽地提供0,(不产生读磁盘IO)of=file 输出文件名,缺省为标准输出。 向file写出,可以写文件,可以写裸设备。如of=/dev/null,"黑洞",它等价于一个只写文件. 所有写入它的内容都会永远丢失. (不产生写磁盘IO)ibs=bytes 一次读入 bytes 个字节(即一个块大小为 bytes 个字节)。obs=bytes 一次写 bytes 个字节(即一个块大小为 bytes 个字节)。bs=bytes 同时设置读写块的大小为 bytes ,可代替 ibs 和 obs。如bs=8k 每次读或写的大小,即一个块的大小为8K。cbs=bytes 一次转换 bytes 个字节,即转换缓冲区大小。skip=blocks 从输入文件开头跳过 blocks 个块后再开始复制。seek=blocks 从输出文件开头跳过 blocks 个块后再开始复制。(通常只有当输出文件是磁盘或磁带时才有效)。count=blocks 仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数。iflag=FLAGS 指定读的方式FLAGS,参见“FLAGS参数说明”oflag=FLAGS 指定写的方式FLAGS,参见“FLAGS参数说明”测试方式:使用dd指令,对磁盘进行连续写入,不使用内存缓冲区,每次写入8k的数据,总共写入20万次,产生1.6G大小的文件。测试指令:dd if=/dev/zero of=/data01/test.dbf bs=8k count=200k conv=fdatasyncdd用于复制,从if读出,写到of;if=/dev/zero(产生字符)不产生IO,因此可以用来测试纯写速度;bs是每次读或写的大小,即一个块的大小,count是读写块的数量;conv=fdatasync表示只把文件的“数据”写入磁盘会在/data01下生成一个文件test.dbf,count * bs 等于最终大小,记得删除。从源 /dev/zero 读取写入到驱动盘的时候(测试写),当从驱动盘读取时写入到/dev/null(测试读)。在整个操作过程中, DD 命令会跟踪数据传输的速度并且报告出结果。/dev/null和/dev/zero的区别/dev/null,它是空设备,也称为位桶(bit bucket)、回收站、无底洞,可以向它输出任何数据。任何写入它的输出都会被抛弃。如果不想让消息以标准输出显示或写入文件,那么可以将消息重定向到位桶。/dev/zero,是一个输入设备,可用它来初始化文件。该设备无穷尽地提供0,可以使用任何需要的数目——设备提供的要多的多。他可以用于向设备或文件写入字符串0。使用sync、fsync、fdatasync、dsyncconv=fsync,表示把文件的“数据”和“metadata”都写入磁盘(metadata包括size、访问时间st_atime & st_mtime等等),因为文件的数据和metadata通常存在硬盘的不同地方,因此fsync至少需要两次IO写操作,fsync 与fdatasync相差不大。conv=fdatasync,dd命令执行到最后会真正执行一次“同步(sync)”操作,,这样算出来的时间才是比较符合实际使用结果的。conv=fdatasync表示只把文件的“数据”写入磁盘,fsync 与fdatasync相差不大。oflag=dsync,dd在执行时每次都会进行同步写入操作。每次读取8k后就要先把这8k写入磁盘,然后再读取下面一个8k,一共重复4K次,生成一个32M文件。这是最慢的一种方式了,基本上没有用到写缓存(write cache)。也是比较准确的。conv=fdatasync与oflag=dsync的区别在于:sync函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。fsync函数只对由文件描述符filedes指定的单一文件起作用,并且等待写磁盘操作结束,然后返回。所以看到的fdatasync速度比dsync好。如果不加任何参数,dd默认的方式不包括“同步(sync)”命令(没加关于操作系统“写缓存”的参数,默认“写缓存”启作用),也就是说,dd命令完成前并没有让系统真正把文件写到磁盘上。dd先把数据写到操作系统“写缓存”,就完成了写操作。所以以上命令只是单纯地把数据读到内存缓冲当中(写缓存[write cache])。通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数,把“写缓存”中的数据刷入磁盘。
2023年01月13日
579 阅读
0 评论
0 点赞
2023-01-05
Android之ActivityThread
每一个App在启动时,都会由Zygote fork出一个进程,进程具有独立的资源空间,用于承载App上运行的各种Activity/Service等组件。当进程创建后就要真正去启动一个App了,会调用ActivityThread的main方法。 frameworks/base/core/java/android/app/ActivityThread.java ActivityThread分析:main方法public static void main(String[] args) { ...... //1.创建主线程looper,主线程初始化 Looper.prepareMainLooper(); //2.主线程Handler初始化 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); }主线程初始化frameworks/base/core/java/android/os/Looper.java//主线程Looper的初始化 public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //普通线程初始化 public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }从上面的代码中可以看出,主线程初始化时,prepare传入false,而普通线程初始化时传入true,这个参数为quitAllowed,表示线程是否可以退出,主线程无法退出。prepare函数中创建一个Looper对象,并将对象保存在ThreadLocal中。在prepare之后,又将主线程Looper赋值给了成员变量sMainLooper,这个成员变量的作用是向其他线程提供主线程Looper对象。public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }这样当我们调用Looper.getMainLooper()时可以获取到主线程的Looper对象了。主线程Handler初始化在main方法下创建了ActivityThread对象,并获取了主线程的Handler。final H mH = new H(); final Handler getHandler() { return mH; }由此可见主线程的Handler作为ActivityThread的成员变量,是在ActivityThread的main方法被执行,ActivityThread被创建时而初始化。ActivityThread.attch()frameworks/base/core/java/android/app/ActivityThread.javafinal ApplicationThread mAppThread = new ApplicationThread(); private void attach(boolean system) { ...... final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } ...... }ApplicationThread是ActivityThread内部类,继承自IApplicationThread.Stub,作为服务端接受AMS发出的请求并执行,ApplicationThread是ActivityThread与AMS连接的桥梁。attch方法实际调用了AMS的attachApplication方法,去看下AMS里的实现:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java@Override public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); } }AMS中的attachApplicationLocked方法有些复杂,这个流程可以理解为ActivityThread创建时(App启动时)需要向AMS注册自己,用于AMS管理ActivityThread中的所有四大组件的生命周期。我们看下attachApplicationLocked中的关键部分:private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { ... //1.创建Application thread.bindApplication(...); ... //2.创建Activity if (mStackSupervisor.attachApplicationLocked(app)) { ... } }thread.bindApplicationAMS通过远程调用,最终又会调用到ActivityThread中ApplicationThread内部类方法。public final void bindApplication(String processName, ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) { if (services != null) { // Setup the service cache in the ServiceManager ServiceManager.initServiceCache(services); } setCoreSettings(coreSettings); AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; data.providers = providers; data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; data.instrumentationUiAutomationConnection = instrumentationUiConnection; data.debugMode = debugMode; data.enableBinderTracking = enableBinderTracking; data.trackAllocation = trackAllocation; data.restrictedBackupMode = isRestrictedBackupMode; data.persistent = persistent; data.config = config; data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; sendMessage(H.BIND_APPLICATION, data); }在方法中将AMS传递回的数据又发送给主线程中case BIND_APPLICATION: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); AppBindData data = (AppBindData)msg.obj; handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break;在主线程中调用handleBindApplication处理:private void handleBindApplication(AppBindData data) { ...... // send up app name; do this *before* waiting for debugger //设置进程名, 也就是说进程名是在进程真正创建以后的BIND_APPLICATION过程中才取名 Process.setArgV0(data.processName); android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId()); //重置时区 TimeZone.setDefault(null); LocaleList.setDefault(data.config.getLocales()); //获取LoadedApk对象 // (public final LoadedApk getPackageInfoNoCheck()) data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); ...... try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { installContentProviders(app, data.providers); // For process that contains content providers, we want to // ensure that the JIT is enabled "at some point". mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } mInstrumentation.onCreate(data.instrumentationArgs); //调用Application.onCreate()回调方法 mInstrumentation.callApplicationOnCreate(app); } finally { StrictMode.setThreadPolicy(savedPolicy); } }handleBindApplication中调用LoadedApk类中makeApplication方法:public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { // 表示Application是个单例 if (mApplication != null) { return mApplication; } Application app = null; // 通过反射初始化Application String appClass = mApplicationInfo.className; if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; } try { java.lang.ClassLoader cl = getClassLoader(); if (!mPackageName.equals("android")) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "initializeJavaContextClassLoader"); initializeJavaContextClassLoader(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { if (!mActivityThread.mInstrumentation.onException(app, e)) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); throw new RuntimeException( "Unable to instantiate application " + appClass + ": " + e.toString(), e); } } mActivityThread.mAllApplications.add(app); mApplication = app; ...... return app; }到这里应用的Application 就创建出来了,创建Application后再调用callApplicationOnCreate,回调Application的onCreate方法。mStackSupervisor.attachApplicationLocked(app)mStackSupervisor是AMS的成员变量,是Activity堆栈管理辅助类实例frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.javaboolean attachApplicationLocked(ProcessRecord app) throws RemoteException { final String processName = app.processName; boolean didSomething = false; for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); if (!isFocusedStack(stack)) { continue; } ActivityRecord hr = stack.topRunningActivityLocked(); if (hr != null) { if (hr.app == null && app.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { if (realStartActivityLocked(hr, app, true, true)) { didSomething = true; } } catch (RemoteException e) { Slog.w(TAG, "Exception in new application when starting activity " + hr.intent.getComponent().flattenToShortString(), e); throw e; } } } } } if (!didSomething) { ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); } return didSomething; }attachApplicationLocked中获取App要启动的top Activity,然后realStartActivityLocked去启动。final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ...... app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); ...... }调用ApplicationThread的scheduleLaunchActivity方法:@Override public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) ....... sendMessage(H.LAUNCH_ACTIVITY, r); } case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);最终调用handleLaunchActivity,最终启动一个Activityprivate void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { //通过反射去创建一个Activity,然后会调用Activity的各个生命周期方法 Activity a = performLaunchActivity(r, customIntent); if (a != null) { r.createdConfig = new Configuration(mConfiguration); reportSizeConfigurations(r); Bundle oldState = r.state; // onResume handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); if (!r.activity.mFinished && r.startsNotResumed) { performPauseActivityIfNeeded(r, reason); if (r.isPreHoneycomb()) { r.state = oldState; } } } else { // If there was an error, for any reason, tell the activity manager to stop us. try { ActivityManagerNative.getDefault() .finishActivity(r.token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } }private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // 从ActivityClientRecord中获取待启动的Activity的组件信息 ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } ComponentName component = r.intent.getComponent(); if (component == null) { component = r.intent.resolveActivity( mInitialApplication.getPackageManager()); r.intent.setComponent(component); } if (r.activityInfo.targetActivity != null) { component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } Activity activity = null; try { //反射创建Activity java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { //再次调用Application的创建方法,Application是个单例。但是如果这个Activity在另一个进程中,就需要再次创建Application Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (r.overrideConfig != null) { config.updateFrom(r.overrideConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } activity.mCalled = false; // 调用Activity的onCreate方法回调,终于看到熟悉的Activity onCreate啦 if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; r.stopped = true; if (!r.activity.mFinished) { // onStart回调 activity.performStart(); r.stopped = false; } if (!r.activity.mFinished) { if (r.isPersistable()) { if (r.state != null || r.persistentState != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); } } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); } } if (!r.activity.mFinished) { activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnPostCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()"); } } } r.paused = true; mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; }
2023年01月05日
567 阅读
0 评论
1 点赞
2023-01-05
Android之WMS
WindowManagerService简称WMS,WMS是属于Android系统的其他服务,但是它的重要性和AMS在同一级别,作为“窗口管理员”,它负责窗口的创建、调度、显示以及触摸事件分发。也是一个Android程序猿必须了解的系统服务。在SyetemServer启动时会调用startOtherServices:private void startOtherServices() { WindowManagerService wm = null; //InputManagerService traceBeginAndSlog("StartInputManagerService"); inputManager = new InputManagerService(context); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); traceBeginAndSlog("StartWindowManagerService"); wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); mActivityManagerService.setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); wm.displayReady(); wm.systemReady(); }可以看到,InputManagerService作为参数传递给了WMS,而WMS又作为参数传给了AMSWMS.mainpublic static WindowManagerService main(final Context context, final InputManagerService im, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; //运行在"android.display"线程 DisplayThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { holder[0] = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore); } }, 0); return holder[0]; }WindowManagerServiceprivate WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { mContext = context; mHaveInputMethods = haveInputMethods; mAllowBootMessages = showBootMsgs; mOnlyCore = onlyCore; ...... //保存传进来的IMS mInputManager = inputManager; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplaySettings = new DisplaySettings(); mDisplaySettings.readSettingsLocked(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); // 获取Display数组,每个显示设备对应一个Display mDisplays = mDisplayManager.getDisplays(); for (Display display : mDisplays) { // 创建DisplayContent,用于支持多屏幕的功能 createDisplayContentLocked(display); } mActivityManager = ActivityManagerNative.getDefault(); //创建WindowAnimator,用于管理所有的窗口动画 mAnimator = new WindowAnimator(this); mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); LocalServices.addService(WindowManagerInternal.class, new LocalService()); //初始化窗口管理策略接口类WindowManagerPolicy initPolicy(); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); ...... }initPolicyprivate void initPolicy() { //运行在"android.ui"线程 UiThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); } }, 0); }在initPolicy中,mPolicy实际为PhoneWindowManager。final WindowManagerPolicy mPolicy = new PhoneWindowManager();"system_server"线程中会调用WMS的main方法,main方法中会创建WMS,创建WMS的过程运行在"android.display"线程中,它的优先级更高一些,因此要等创建WMS完毕后才会唤醒处于等待状态的"system_server"线程。WMS初始化时会执行initPolicy方法,initPolicy方法会调用PWM的init方法,这个init方法运行在"android.ui"线程,并且优先级更高,因此要先执行完PWM的init方法后,才会唤醒处于等待状态的"android.display"线程。
2023年01月05日
967 阅读
0 评论
0 点赞
1
2
...
8