IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android S静态广播注册流程(广播2) -> 正文阅读

[移动开发]Android S静态广播注册流程(广播2)

1. 静态广播注册的流程

上一篇文章讲了动态广播的注册(系统存储在mReceiverResolver中),
这篇文章主要讲解静态广播的注册流程
=> 解析包中的receiver
在这里插入图片描述
=> 将receiver相关信息添加到mComponentResolver中
在这里插入图片描述

2. 在AndroidManifest静态注册广播

  1. 还是以亮屏SCREEN_ON和开机BOOT_COMPLETED静态广播为例子
//在AndroidManifest.xml中定义如下
        <receiver android:name=".MyReceiver">
            <intent-filter>
                <action android:name="android.intent.action.SCREEN_ON" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
//MyReceiver.java
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        Log.e("yunhen", "MyReceiver static intent = " + intent.getAction());
    }
}
  1. 测试发现这个不管是亮屏SCREEN_ON还是开机BOOT_COMPLETED的广播都收不到,
    BOOT_COMPLETED没有收到的日志如下(大概意思是少了一个RECEIVE_BOOT_COMPLETED权限定义):

04-26 09:00:26.506 1337 1549 W BroadcastQueue: Permission Denial: receiving Intent { act=android.intent.action.BOOT_COMPLETED flg=0x89000010 (has extras) } to co
m.example.myapplication/.MyReceiver requires android.permission.RECEIVE_BOOT_COMPLETED due to sender null (uid 1000)

那就补上RECEIVE_BOOT_COMPLETED权限:

//在AndroidManifest.xml中定义如下
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

        <receiver android:name=".MyReceiver">
            <intent-filter>
                <action android:name="android.intent.action.SCREEN_ON" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

顺利收到BOOT_COMPLETED的广播:

04-26 09:45:26.198 7894 7894 E yunhen : MyReceiver static intent = android.intent.action.BOOT_COMPLETED

  1. 不过SCREEN_ON,没有收到任何日志报错,就是没收到

  2. 我们带着这个例子的疑问,一起去代码里面找答案(下一篇广播发送才能找到答案)。
    本次先看静态广播是注册到系统的哪里去了

3. AndroidManifest静态注册接受者的安装过程

  1. 先从安装过程中的processInstallRequestsAsync(PackageManagerService.java)处理安装请求开始讲起,
    处理安装请求=>
    调用流程
    ->processInstallRequestsAsync(PackageManagerService.java) //处理安装请求
    ->installPackagesTracedLI //安装函数,加个installPackages的trace tag
    ->installPackagesLI //实际安装函数
    ->preparePackageLI //准备包的信息
    ->parsePackage(PackageParser2.java) //解析包的信息
PackageManagerService.java
    // Queue up an async operation since the package installation may take a little while.
    //处理安装请求
    private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
        mHandler.post(() -> {
            //...
			synchronized (mInstallLock) {
				installPackagesTracedLI(apkInstallRequests);
			}
            //...
        });
    }

    //安装函数,加个installPackages的trace tag
    private void installPackagesTracedLI(List<InstallRequest> requests) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
            installPackagesLI(requests);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

    //这个是安装应用的关键函数,实际安装函数
    private void installPackagesLI(List<InstallRequest> requests) {
            //...
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
            for (InstallRequest request : requests) {
                // TODO(b/109941548): remove this once we've pulled everything from it and into
                //                    scan, reconcile or commit.
                final PrepareResult prepareResult;
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
                    //准备包的信息,如解析这个应用的内容
                    prepareResult =
                            preparePackageLI(request.args, request.installResult);
                }
            //...
    }

    //准备包的信息
    private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
            throws PrepareFailure {
        //通过args.installFlags获得如是否instantApp、fullApp、virtualPreload等应用
        final int installFlags = args.installFlags;
        //...
        //注意默认是包含PARSE_CHATTY的tag的
        @ParseFlags final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
                | ParsingPackageUtils.PARSE_ENFORCE_CODE
                | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
        final ParsedPackage parsedPackage;
        try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {
            //开始对包进行解析parsePackage
            parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
            AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
        } catch (PackageParserException e) {
            throw new PrepareFailure("Failed parse during installPackageLI", e);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
  1. 包解析,获取相关receiver的组件放入ParsingPackageImpl的receivers中,
    我们记住receivers是存放ParsedActivity
    调用流程
    ->parsePackage(PackageParser2.java) //V2版本的解析包的信息
    ->parsePackage(ParsingPackageUtils.java) //包解析的工具类
    ->parseClusterPackage //如果是目录,解析目录的簇群
    ->parseBaseApk //解析BaseApk(最基础的apk)
    ->parseBaseApkTags //解析AndroidManifest.xml的“application”相关tag
    ->parseBaseApplication //解析application
    ->addReceiver(ParsingPackageImpl.java) //将通过parseActivityOrReceiver获取的ParsedActivity放入receivers
//PackageParser2.java
    //V2版本的包解析代码
    public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
        //...
        ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
        //...
    }

//ParsingPackageUtils.java
    //解析包信息
    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
            int flags)
            throws PackageParserException {
        if (packageFile.isDirectory()) {
            //目前大部分跑的是这里,如类似/data/app/vmdl1597983231.tmp的目录,里面包含***.apk
            return parseClusterPackage(input, packageFile, flags);
        } else {
            return parseMonolithicPackage(input, packageFile, flags);
        }
    }

    //解析包的簇群
    private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
            int flags) {
        //得到一些包的基本信息,如mBaseApkPath = "/data/app/vmdl1597983231.tmp/base.apk"
        //parseClusterPackageLite->composePackageLiteFromApks->new PackageLite(找到".apk"结尾的)
        final ParseResult<PackageLite> liteResult =
                ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
        //...
        try {
            final File baseApk = new File(lite.getBaseApkPath());
            //解析BaseApk
            final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
                    lite.getPath(), assetLoader, flags);
        //...
    }

    //解析BaseApk
    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
            String codePath, SplitAssetLoader assetLoader, int flags)
            throws PackageParserException {
        //...
		//创建解析器parser,来解析ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"
        try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
                ANDROID_MANIFEST_FILENAME)) {
            final Resources res = new Resources(assets, mDisplayMetrics, null);
            //传入解析器parser,解析BaseApk
            ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
                    parser, flags);
        //...
    }

    //传入解析器parser,解析BaseApk
    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
            String codePath, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        //...
        final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
        try {
            final boolean isCoreApp =
                    parser.getAttributeBooleanValue(null, "coreApp", false);
            final ParsingPackage pkg = mCallback.startParsingPackage(
                    pkgName, apkPath, codePath, manifestArray, isCoreApp);
            //解析apk中的各类TAG
            final ParseResult<ParsingPackage> result =
                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
        //...
    }

    //解析apk中的各类TAG
    private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        //...

            // <application> has special logic, so it's handled outside the general method
            //如果tag是TAG_APPLICATION = "application"的话
            if (TAG_APPLICATION.equals(tagName)) {
                //...
                } else {
                    foundApp = true;
                    //则进行application的内容解析
                    result = parseBaseApplication(input, pkg, res, parser, flags);
        //...
    }

    //解析"application"下的各个内容
    private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        //...
            final ParseResult result;
            String tagName = parser.getName();
            boolean isActivity = false;
            //Android四大组件解析的地方
            switch (tagName) {
                //activity和receiver的解析是一样的,它们比较类似
                case "activity":
                    isActivity = true;
                    // fall-through
                case "receiver":
                    //activity和receiver的解析是一样的,它们比较类似
					//parseActivityOrReceiver->parseActivityOrAlias中每一个"intent-filter"会对应一个ParsedIntentInfo,
                    //并放入ParsedComponent(继承关系ParsedActivity/ParsedMainComponent/ParsedComponent)的intents
                    //本例中解析出来的接收器如{ParsedActivity@38497} "Activity{8f994ed com.example.myapplication/.MyReceiver}"
                    //ParsedActivity.intents.mActions包括"android.intent.action.SCREEN_ON"、"android.intent.action.BOOT_COMPLETED"
                    ParseResult<ParsedActivity> activityResult =
                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                    res, parser, flags, sUseRoundIcon, input);

                    if (activityResult.isSuccess()) {
                        ParsedActivity activity = activityResult.getResult();
                        if (isActivity) {
                            hasActivityOrder |= (activity.getOrder() != 0);
                            pkg.addActivity(activity);
                        } else {
                            //该receiver是否有设置android:order的tag,这个用的比较少,默认order是0
							//对于一个包有多个receivers,order进行排序,按照order越大放在越前面
                            hasReceiverOrder |= (activity.getOrder() != 0);

							//将该ParsedActivity activity(receiver)放入ParsingPackageImpl的receivers
                            pkg.addReceiver(activity);
                        }
                    }

                    result = activityResult;
                    break;
                case "service":
                    //...
                case "provider":
                    //...
                case "activity-alias":
                    //...
                default:
                    result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                    break;
            }
        //...
        //按照android:order进行排序,order越大的receiver放在receivers列表的前面
        if (hasReceiverOrder) {
            pkg.sortReceivers();
        }
        //...
    }

//ParsingPackageImpl.java
	//将该receiver放入ParsingPackageImpl的receivers,
	//目前可以看到,包解析后receiver放入的地方是ParsingPackageImpl的receivers中
    public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
        this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
        addMimeGroupsFromComponent(parsedReceiver);
        return this;
    }
  1. 将之前receiver解析的ParsedActivity全部放入mComponentResolver(ComponentResolver)的mReceivers中
    mReceivers包含了所有安装应用的receiver,通过ComponentName就可以获得相应的ParsedActivity
    mReceivers.mActivities.get(new ComponentName(“com.example.myapplication”, “com.example.myapplication.MyReceive”))
    mComponentResolver是PackageManagerService的一个成员变量(也就是相当于将该receiver保存在系统的变量中)

同时将该组件的IntentFilter(intent-filter)/action添加到mComponentResolver(继承IntentResolver)的mFilters/mActionToFilter中

具体流程如下=>
(安装应用有很多阶段,前面说的是包解析,现在说的是将调和后的扫描结果保存到系统中)
->installPackagesLI(PackageManagerService.java) //安装函数
->commitPackagesLocked //将安装信息提交给系统
->commitReconciledScanResultLocked //提交已经协调好的扫描结果
->commitPackageSettings //提交到包信息里面
->mComponentResolver.addAllComponents //添加AndroidPackage pkg中的所有组件
->addReceiversLocked //Receiver组件的添加
->mReceivers.addActivity/addFilter //添加receiver到mComponentResolver的mReceivers,添加IntentFilter/action到mComponentResolver的mFilters/mActionToFilter等

//PackageManagerService.java
    //安装函数
    private void installPackagesLI(List<InstallRequest> requests) {
            //...
                    //准备包的信息,如解析这个应用的内容
                    prepareResult =
                            preparePackageLI(request.args, request.installResult);
            //...
                try {
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
                    commitRequest = new CommitRequest(reconciledPackages,
                            mUserManager.getUserIds());
                    //将安装信息提交给系统
                    commitPackagesLocked(commitRequest);
                    success = true;
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
            //...
    }

    private void commitPackagesLocked(final CommitRequest request) {
        // TODO: remove any expected failures from this method; this should only be able to fail due
        //       to unavoidable errors (I/O, etc.)
        //本例安装request.reconciledPackages.size()是1
        for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
            //...
            //提交已经协调好的扫描结果
            AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers);
            //...
    }

    private AndroidPackage commitReconciledScanResultLocked(
            @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
        //...
        // Modify state for the given package setting
        //提交到包信息里面,从上面可以知道(parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0默认是true,也就是chatty是true
        commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
                (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
        //...
    }

    private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg,
            @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,
            final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
        //...
			//通过injector获得ComponentResolver
			//mComponentResolver = injector.getComponentResolver();
			//相当于new ComponentResolver(第一次调用才会new)获取的mInstance(ComponentResolver)实例
			//通过组件解析器mComponentResolver添加pkg(PackageImpl实例)中的组件
			//PackageImpl父类是ParsingPackageImpl(存放我们之前说的receivers的地方),而且实现了AndroidPackage接口
            mComponentResolver.addAllComponents(pkg, chatty);
        //...
    }

//ComponentResolver.java
    //添加AndroidPackage pkg中的所有组件
    void addAllComponents(AndroidPackage pkg, boolean chatty) {
        final ArrayList<Pair<ParsedActivity, ParsedIntentInfo>> newIntents = new ArrayList<>();
        synchronized (mLock) {
            //Activity组件的添加
            addActivitiesLocked(pkg, newIntents, chatty);
            //Receiver组件的添加
            addReceiversLocked(pkg, chatty);
            //Provider组件的添加
            addProvidersLocked(pkg, chatty);
            //Service组件的添加
            addServicesLocked(pkg, chatty);
            //状态改变通知到相关mObservers
            onChanged();
        }
        //...
    }

    //添加AndroidPackage pkg中的Receiver到mComponentResolver的mReceivers
    private void addReceiversLocked(AndroidPackage pkg, boolean chatty) {
        final int receiversSize = ArrayUtils.size(pkg.getReceivers());
        StringBuilder r = null;
        for (int i = 0; i < receiversSize; i++) {
            //逐个取出ParsingPackageImpl的receivers,如此处的MyReceiver
            ParsedActivity a = pkg.getReceivers().get(i);
			//添加mComponentResolver的mReceivers
            mReceivers.addActivity(a, "receiver", null);
			//DEBUG_PACKAGE_SCANNING这个PMS的调试开关,用于判断是否输出日志
			//可以将全部pms debug相关的开关做成动态开关
            if (DEBUG_PACKAGE_SCANNING && chatty) {
                if (r == null) {
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append(a.getName());
            }
        }
		//输出相关添加Receivers的日志
        if (DEBUG_PACKAGE_SCANNING && chatty) {
            Log.d(TAG, "  Receivers: " + (r == null ? "<NONE>" : r));
        }
    }

	//mReceivers是ReceiverIntentResolver对象,其父类是ActivityIntentResolver
	//添加ParsedActivity a到mActivities中
	protected void addActivity(ParsedActivity a, String type,
			List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
		//其mActivities以ComponentName为key,保存着ParsedActivity(此处是MyReceiver)
		mActivities.put(a.getComponentName(), a);
		//DEBUG_SHOW_INFO这个是调试信息的开关,可以打开查看流程
		if (DEBUG_SHOW_INFO) {
			Log.v(TAG, "  " + type + ":");
			Log.v(TAG, "    Class=" + a.getName());
		}
		//取得ParsedIntentInfo,这里只有1个intent-filter就对应1个ParsedIntentInfo
		//如果写2个intent-filter,就会有2个ParsedIntentInfo(继承的IntentFilter)
		final int intentsSize = a.getIntents().size();
		for (int j = 0; j < intentsSize; j++) {
			ParsedIntentInfo intent = a.getIntents().get(j);
			if (newIntents != null && "activity".equals(type)) {
				newIntents.add(Pair.create(a, intent));
			}
			if (DEBUG_SHOW_INFO) {
				Log.v(TAG, "    IntentFilter:");
				intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
			}
			//这个也是调试使用,末日是不输出的
			if (!intent.debugCheck()) {
				Log.w(TAG, "==> For Activity " + a.getName());
			}
			//Pair.create(a, intent)相当于new Pair<ParsedActivity, ParsedIntentInfo>(a, intent);
			//添加到ComponentResolver(继承IntentResolver)的mFilters/mActionToFilter中
			addFilter(Pair.create(a, intent));
		}
	}

//IntentResolver.java
    //添加IntentFilter
    public void addFilter(F f) {
        IntentFilter intentFilter = getIntentFilter(f);
		//localLOGV这些都是调试日志
        if (localLOGV) {
            Slog.v(TAG, "Adding filter: " + f);
            intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
            Slog.v(TAG, "    Building Lookup Maps:");
        }

		//将Pair<ParsedActivity, ParsedIntentInfo>添加到mFilters
        mFilters.add(f);
		//将mDataSchemes放入mSchemeToFilter
        int numS = register_intent_filter(f, intentFilter.schemesIterator(),
                mSchemeToFilter, "      Scheme: ");
        int numT = register_mime_types(f, "      Type: ");
        if (numS == 0 && numT == 0) {
            //将mActions放入mActionToFilter, key是action,value是Pair<ParsedActivity, ParsedIntentInfo>的list
            //如mActionToFilter.get("android.intent.action.BOOT_COMPLETED") = {Pair[316]@38034} 
            //107 = {Pair@38369} "Pair{Activity{7d26902 com.example.myapplication/.MyReceiver2} ParsedIntentInfo{a218213}}"
            //108 = {Pair@38370} "Pair{Activity{3985c50 com.example.myapplication/.MyReceiver} ParsedIntentInfo{9dfde49}}"
            register_intent_filter(f, intentFilter.actionsIterator(),
                    mActionToFilter, "      Action: ");
        }
        if (numT != 0) {
            register_intent_filter(f, intentFilter.actionsIterator(),
                    mTypedActionToFilter, "      TypedAction: ");
        }
    }

4. 总结

  1. AndroidManifest.xml中的receiver解析成ParsedActivity, 存放在PackageImpl的receivers(PackageImpl父类是ParsingPackageImpl而且实现了AndroidPackage接口)
  2. receivers最终会存放在mComponentResolver(PMS)的mReceivers中,mReceivers包含了所有安装应用的receiver,通过ComponentName就可以获得相应的ParsedActivity,如:
    mReceivers.mActivities.get(new ComponentName(“com.example.myapplication”, “com.example.myapplication.MyReceive”)),既可以获得MyReceiver的ParsedActivity
  3. receiver中的一个IntentFilter(intent-filter)对应一个ParsedIntentInfo,一个Pair<ParsedActivity, ParsedIntentInfo>
  4. Pair<ParsedActivity, ParsedIntentInfo>被放入mComponentResolver的mFilters
  5. mActions被放入mComponentResolver的mActionToFilter
  6. 部分静态广播注册需要权限,不是注册了就能收到。其实要接收广播还:涉及权限、进程优先级、flag标签等各类无法接收到广播的情况。
    (系统一般是skip或者根本不发送给静态广播或者mComponentResolver解析时就跳过了)
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-02 13:28:56  更:2022-05-02 13:29:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 23:42:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码