精华内容
下载资源
问答
  • 1、下载Android系统的源码包(不同手机用的不一样,根据自己的情况合理下载,这不介绍) 2、解压下载下来的源码包(若是.tar.gz文件,解压方式如下) tar -xzvf 文件名 3、在解压包的alps/packages/apps下面...

     

    1、下载Android系统的源码包(不同手机用的不一样,根据自己的情况合理下载,这不做介绍)

     

    2、解压下载下来的源码包(若是.tar.gz文件,解压方式如下)

        tar -xzvf 文件名

    3、在解压包的alps/packages/apps下面新建一个文件夹 ***(项目名),将自己的项目的里面的main文件下的数据复制出来放在新建的文件夹下,并将java文件名改为src,最重要的一点是新建一个Android.mk文件,这个文件必须要有,可参照apps/下面的项目里面的.mk编写,最简单的编写是如下:

    LOCAL_PATH:= $(call my-dir)    //Android.mk文件和项目文件在同一个文件里可以这样写

    include $(CLEAR_VARS)

    LOCAL_MODULE_TAGS := optional

    LOCAL_SRC_FILES := $(call all-java-files-under, src)

    LOCAL_PACKAGE_NAME := 项目名

    include $(BUILD_PACKAGE)

     

    4、编译整个工程

    ./oneCmdMake  系统项目名  userdebug       //这里可以是userdebug、eng、user

     

    5、单一模块编译

    ./moduleMake userdebug mmm packages/apps/项目名      //注意,这里的编译选项必须一致,不然后面很麻烦

     

    如果编译成功就可以开始刷机了,编译不成功就看error是怎么提示的,如果刷机后,手机上面没有你的项目,就执行  ./moduleMake userdebug snod

    再次刷机,一般这样就可以了

     

     

    整个工程编译成功后以后就不需要在整体编译了,整体编译是一个特别耗时的操作,一般是一个小时左右,但是单一模块编译是很快的,如果以后还需要将自己的项目预置到这个系统中(已经整体编译成功的),只需直接单一模块编译,并用snod进行打包就可以了,这是我认为比较节省时间的方法,另一种方法是将自己的项目名配置到系统配置文件device.mk里面,仿照***PACKAGES+=项目名,再整体编译一次,就不需要打包了。

     

     

     

    模块编译(快速将修改过后的项目push到手机上)

                   source build/envsetup.sh    # source是用来运行shell脚本的,envsetup.sh里面有mm mmm mmmb等一系列可用的命令,但只有运行了该脚本,里面的命令才能使用
                   lunch +平台名                    #加载平台,这可名字可以在out/target/下面看,例如:正确的情况下是没有generic文件夹的

                                                   

     

                   mmm -B  项目名      #编译修改过后的项目,-B 可有可无,这里的项目名要对应所在的路径,编译模块的所有文件,需要-B选项,不加-B 则只编译发生改变的文件

                   adb root                   #获得手机的root权限

                   adb remount            #挂载手机

                   adb push  该项目生成的 .apk 和 .odex文件的位置             对应手机里面的位置            #把修改并编译后生成的push 到手机中

                   adb shell reboot             #重启手机

     对于不能remount的手机,就不能push,可以mmm单一编译后打包make -j16 snod(直接使用),再刷机,选中system下载到手机,可以省很多的时间

     

    阿里最近更新代码后, root 后无法remount需要使用的要做以下处理:
    adb root 之后,进到adb shell 里。执行命令 echo "enable 0;" > /proc/alog 然后就可以了。

     

     

    注:当不确定自己是否push成功的情况下,可以到手机的目录下面去看对应文件的修改时间,如

    ls -l Utk.apk  结果会显示文件的修改时间和权限,权限是666时,才正确,时间为你push的时间

     

    m:编译所有的模块 
    mm:编译当前目录下的模块,当前目录下要有Android.mk文件 
    mmm:编译指定路径下的模块,指定路径下要有Android.mk文件

     

     

    eng:工程版本

    user:最终用户版本

    userdebug:调试测试版本

    tests:测试版本

    一般而言,tests和userdebug两个版本是不能交付给最终用户的。

     

     

    犯过的错误及最后的修改:

    1、整体编译的时候用的是userdebug,单一模块用的是eng,编译的时候没有注意到,后面改成一样的了能生成oat文件了。
    2、自己的游戏项目里面继承是AppCompatActivity,不能被识别,可能是因为包或版本的原因,最后统一改成了Activity.
    3、用./moduleMake userdebug mmmb 改成了./moduleMake userdebug snod编译单一模块并打包就成功了

    4、注释尽量不出现中文,布局里面的text内容最好写在string.xml中,再去调用

    5、./birdCopy 6118SOP_V17_AY_HD_MV1616 userdebug
         ./mk_aliphone.sh bird_6737m_35g_m0 userdebug adb new YUNOS_CARRIER_CUSTOM=CMCC_DEEP_COOP

    这是对于阿里平台的编译,new是每次整体编译都会清掉原来编译过的数据,如果为了编译时间短,可以不要new改成remake

    提高代码的编译速度可参照http://blog.csdn.net/cscrazybing/article/details/50789482

    转载于:https://www.cnblogs.com/xhf-wonder/p/6842925.html

    展开全文
  • 然而 Windows 操作系统允许一个应用程序在运行期间修改自己的名称甚至移动自己到另一个文件夹中。利用这一点,我们可以很简单直接地程序的 OTA 自动更新。 本文将介绍示例程序运行期间改名并解释其原理。 本文...

    程序如何自己更新自己呢?你可能会想到启动一个新的程序或者脚本来更新自己。然而 Windows 操作系统允许一个应用程序在运行期间修改自己的名称甚至移动自己到另一个文件夹中。利用这一点,我们可以很简单直接地做程序的 OTA 自动更新。

    本文将介绍示例程序运行期间改名并解释其原理。


    在程序运行期间手工改名

    我们写一个简单的程序。

    简单的程序

    将它运行起来,然后删除。我们会发现无法删除它。

    无法删除程序

    但是,我们却可以很轻松地在资源管理器中对它进行改名,甚至将它从一个文件夹中移动到另一个文件夹中。

    已经成功改名

    值得注意的是,你不能跨驱动器移动此文件。

    不止是 exe 文件,dll 文件也是可以改名的

    实际上,不止是 exe 文件,在 exe 程序运行期间,即使用到了某些 dll 文件,这些 dll 文件也是可以改名的。

    当然,一个 exe 的运行不一定在启动期间就加载好了所有的 dll,所以如果你在 exe 启动之后,某个 dll 加载之前改了那个 dll 的名称,那么会出现找不到 dll 的情况,可能导致程序崩溃。

    为什么 Windows 上的可执行程序可以在运行期间改名?

    Windows 的文件系统由两个主要的表示结构:一个是目录信息,它保存有关文件的元数据(如文件名、大小、属性和时间戳);第二个是文件的数据链。

    当运行程序加载一个程序集的时候,会为此程序集创建一个内存映射文件。为了优化性能,往往只有实际用到的部分才会被加入到内存映射文件中;当需要用到程序集文件中的某块数据时,Windows 操作系统就会将需要的部分加载到内存中。但是,内存映射文件只会锁定文件的数据部分,以保证文件文件的数据不会被其他的进程修改。

    这里就是关键,内存映射文件只会锁定文件的数据部分,而不会锁住文件元数据信息。这意味着你可以随意修改这些元数据信息而不会影响程序的正常运行。这就包括你可以修改文件名,或者把程序从一个文件夹下移动到另一个文件夹去。

    但是跨驱动器移动文件,就意味着需要在原来的驱动器下删除文件,而这个操作会影响到文件的数据部分,所以此操作不被允许。

    编写一个程序在运行期间自动改名

    一般来说,需要 OTA 更新的程序是客户端程序,所以实际上真正需要此代码的是客户端应用。以下代码中我使用 .NET Core 3.0 来编写一个给自己改名的 WPF 程序。

    using System.Diagnostics;
    using System.IO;
    using System.Windows;
    
    namespace Walterlv.Windows.Updater
    {
        public partial class App : Application
        {
            protected override void OnStartup(StartupEventArgs e)
            {
                base.OnStartup(e);
                var fileName = Process.GetCurrentProcess().MainModule.FileName;
                var newFileName = Path.Combine(Path.GetDirectoryName(fileName), "OldUpdater.exe");
                File.Move(fileName, newFileName);
                // 省略的代码:将新下载下载的程序改名成 fileName。
            }
        }
    }
    

    于是,程序自己在运行后会改名。

    程序已经自己改名

    顺便的,以上代码仅适用于 .NET Framework 的桌面应用程序或者 .NET Core 3.0 的桌面应用程序。如果是 .NET Core 2.x,那么以上代码在获取到进程名称的时候可能是 dotnet.exe(已发布的 .NET Core 程序除外)。


    参考资料


    我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

    如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

    知识共享许可协议

    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

    展开全文
  • 这样做的好处是免去了各个程序自己加载各自资源时间,同时减少了系统内存使用量,代价仅仅是增加了每次系统重启时间。 但同时也带来了一些问题。例如,如果开发者想分析或调试某个应用程序,而某些分析工具...

    Android系统中的所有应用程序都是由所谓的Zygote进程(准确的说是/system/bin/app_process)“孵化”出来的。所有新创建的程序,都会继承Zygote进程内所有的资源。这样做的好处是免去了各个程序自己加载各自资源的时间,同时减少了系统总的内存使用量,代价仅仅是增加了每次系统重启的时间。

    但同时也带来了一些问题。例如,如果开发者想分析或调试某个应用程序,而某些分析工具必须要求由它自己来启动你的应用程序(例如Native程序内存检测工具valgrind), 就不能简单的通过直接调用app_process程序来完成。

    好在Google意识到了这个问题,在最近的Android系统代码中留下了这个口子,下面通过分析原代码来详述其实现原理(代码基于4.4.2_r2)。具体应用程序的启动过程就不分析了,这里重点分析和本文内容相关的部分。

    首先,来看ZygoteConnection类中的runOnce函数(代码位于frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java):

    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
        ...
        try {
            parsedArgs = new Arguments(args);
            ...
            applyInvokeWithSystemProperty(parsedArgs);
            ...
            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName);
        }
        ....
        try {
            if (pid == 0) {
                ...
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
                ...
            } else {
                ...
            }
        }
        ...
    }

    可以看到,其调用了一个叫做applyInvokeWithSystemProperty的函数,然后fork出了子进程。对于子进程来说,接下来调用了handleChileProc函数。先来看第一个函数(代码位于frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java):

    public static void applyInvokeWithSystemProperty(Arguments args) {
        if (args.invokeWith == null && args.niceName != null) {
            if (args.niceName != null) {
                String property = "wrap." + args.niceName;
                if (property.length() > 31) {
                    property = property.substring(0, 31);
                }
                args.invokeWith = SystemProperties.get(property);
                if (args.invokeWith != null && args.invokeWith.length() == 0) {
                    args.invokeWith = null;
                }
            }
        }
    }

    其中所谓的niceName就是要启动程序的包名。代码很简单,如果包名不为空的话,就查看一下系统中有没有属性名是在你的包名前加上“wrap.”的属性。如果有的话,那么就将参数列表中的invokeWith变量的值设置成那个系统属性;如果没有的话,就将其设置成空。

    通常,这个属性会被设置成你要使用的分析程序的名字。同时,由于属性名包含你要跑的程序的包名,所以只对你关心的程序起作用。

    注意,这里还有个限制条件,就是拼完之后的属性名不能超过31个字符,如果超过的话只会取前31个字符。因此,这里就有了一个隐性的限制条件,就是你程序的包名不能太长(不要超过26个字符)。

    接下来,当子进程启动之后,会继续调用handleChileProc函数,做接下来的工作(代码位于frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java):

    private void handleChildProc(Arguments parsedArgs,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
        ...
        if (parsedArgs.runtimeInit) {
            if (parsedArgs.invokeWith != null) {
                WrapperInit.execApplication(parsedArgs.invokeWith,
                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
                        pipeFd, parsedArgs.remainingArgs);
            } else {
                RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                        parsedArgs.remainingArgs);
            }
        } else {
        ...
        }
    }

    这里会根据刚才在handleChileProc函数中对参数列表中的invokeWith变量设置值的不同做不一样的处理。如果invokeWith是空的话,这也是通常的情况,就直接调用RuntimeInit.zygoteInit函数了;如果不为空的话,也就是本文要分析的情况,会调用WrapperInit.execApplication函数(代码位于frameworks\base\core\java\com\android\internal\os\WrapperInit.java):

    public static void execApplication(String invokeWith, String niceName,
                int targetSdkVersion, FileDescriptor pipeFd, String[] args) {
        StringBuilder command = new StringBuilder(invokeWith);
        command.append(" /system/bin/app_process /system/bin --application");
        if (niceName != null) {
            command.append(" '--nice-name=").append(niceName).append("'");
        }
        command.append(" com.android.internal.os.WrapperInit ");
        command.append(pipeFd != null ? pipeFd.getInt$() : 0);
        command.append(' ');
        command.append(targetSdkVersion);
        Zygote.appendQuotedShellArgs(command, args);
        Zygote.execShell(command.toString());
    }

    函数的功能很简单,就是拼一个命令行字符串,然后交给Zygote.execShell函数去执行(代码位于libcore\dalvik\src\main\java\dalvik\system\Zygote.java):

    public static void execShell(String command) {
        String[] args = { "/system/bin/sh", "-c", command };
        try {
            Libcore.os.execv(args[0], args);
        } catch (ErrnoException e) {
            throw new RuntimeException(e);
        }
    }

    这个函数实际上最后会调用系统的execv函数,用shell程序(/system/bin/sh)来执行指定的命令。而这个要执行的命令是在WrapperInit.execApplication函数中拼出来的。

    分析前面的代码,可以知道,命令格式大致如下:

    <属性值> /system/bin/app_process /system/bin --application '--nice-name=<包名>' com.android.internal.os.WrapperInit <管道号> <目标SDK版本号> <其它剩下的参数>

    这里的属性值通常被设置成你要使用的那个测试工具,或者是一个特定的测试脚本。所以,推论这样下去的结果就是让你使用的测试工具启动app_process进程,并给其传递相应的参数,从而让它接着启动你要测试的程序。

    这里特别说明一下,参数中的“--nice-name”被设置成你程序的包名,但它的作用只是将子进程的名字改成你的包名,并不是指定要app_process启动你的程序。另外,在所谓的“其它剩下的参数中”,会指定一个类名,但它也不是你所要启动程序的类名,无论你要启动的程序是什么,这个类名都被指定成“android.app.ActivityThread”。而真正决定到底启动哪个Java程序的另有玄机。

    那到底是不是这样呢?我们接着看app_process中的main函数实现(代码位于frameworks\base\cmds\app_process\app_main.cpp):

    int main(int argc, char* const argv[])
    {
        ...
        AppRuntime runtime;
        const char* argv0 = argv[0];
    
        argc--;
        argv++;
    
        int i = runtime.addVmArguments(argc, argv);
    
        bool zygote = false;
        bool startSystemServer = false;
        bool application = false;
        const char* parentDir = NULL;
        const char* niceName = NULL;
        const char* className = NULL;
        while (i < argc) {
            const char* arg = argv[i++];
            if (!parentDir) {
                parentDir = arg;
            } else if (strcmp(arg, "--zygote") == 0) {
                zygote = true;
                niceName = "zygote";
            } else if (strcmp(arg, "--start-system-server") == 0) {
                startSystemServer = true;
            } else if (strcmp(arg, "--application") == 0) {
                application = true;
            } else if (strncmp(arg, "--nice-name=", 12) == 0) {
                niceName = arg + 12;
            } else {
                className = arg;
                break;
            }
        }
    
        if (niceName && *niceName) {
            setArgv0(argv0, niceName);
            set_process_name(niceName);
        }
    
        runtime.mParentDir = parentDir;
    
        if (zygote) {
            ...
        } else if (className) {
            runtime.mClassName = className;
            runtime.mArgC = argc - i;
            runtime.mArgV = argv + i;
            runtime.start("com.android.internal.os.RuntimeInit",
                    application ? "application" : "tool");
        } else {
            ...
        }
    }
    对照前面拼的命令,可以看出,传递给app_process的参数的含义如下:

    1)“/system/bin”是所谓的父路劲(Parent Directory);

    2)“--application”表示让app_process启动一个应用程序;

    3)“--nice-name=<包名>”指定要实际启动的那个程序的包名;

    4)“com.android.internal.os.WrapperInit”字面上是指定一个类名,app_process不能直接启动一个运行Dalvik指令的程序,必须要借助一些Java层的程序,下面分析中会提到。

    由于命令没有指定--zygote,并且设置了类名是“com.android.internal.os.WrapperInit”,所以接下来会调用AppRuntime.start函数,第一个参数是“com.android.internal.os.RuntimeInit”,第二个参数是“application”,并且将AppRuntime实例的mClassName设置成了“com.android.internal.os.WrapperInit”。

    由于AppRuntime类继承自AndroidRuntime类,且没有重载其中的start函数,所以这里实际是调用的AndroidRuntime.start函数(代码位于frameworks\base\core\jni\AndroidRuntime.cpp):

    void AndroidRuntime::start(const char* className, const char* options)
    {
        ...
        JniInvocation jni_invocation;
        jni_invocation.Init(NULL);
        JNIEnv* env;
        if (startVm(&mJavaVM, &env) != 0) {
            return;
        }
        onVmCreated(env);
    
        if (startReg(env) < 0) {
            ALOGE("Unable to register all android natives\n");
            return;
        }
    
        jclass stringClass;
        jobjectArray strArray;
        jstring classNameStr;
        jstring optionsStr;
    
        stringClass = env->FindClass("java/lang/String");
        assert(stringClass != NULL);
        strArray = env->NewObjectArray(2, stringClass, NULL);
        assert(strArray != NULL);
        classNameStr = env->NewStringUTF(className);
        assert(classNameStr != NULL);
        env->SetObjectArrayElement(strArray, 0, classNameStr);
        optionsStr = env->NewStringUTF(options);
        env->SetObjectArrayElement(strArray, 1, optionsStr);
    
        char* slashClassName = toSlashClassName(className);
        jclass startClass = env->FindClass(slashClassName);
        if (startClass == NULL) {
           ...
        } else {
            jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
            if (startMeth == NULL) {
                ALOGE("JavaVM unable to find main() in '%s'\n", className);
            } else {
                env->CallStaticVoidMethod(startClass, startMeth, strArray);
            }
        }
        ...
    }

    代码很简单,启动并初始化好Dalvik虚拟机,然后找出名字为className参数指定的类(这里是“com.android.internal.os.RuntimeInit”),并调用它的静态main方法,且将传递进来的className和options参数转换成字符串数组传递进去。所以,接下来我们看RuntimeInit.main函数(代码位于frameworks\base\core\java\com\android\internal\os\RuntimeInit.java):

    public static final void main(String[] argv) {
        ...
        commonInit();
    
        nativeFinishInit();
        ...
    }

    还是继续做一些初始化的动作,那传进来的“com.android.internal.os.WrapperInit”用到了什么地方,具体的程序又是怎么被启动的呢?

    奥秘在nativeFinishInit函数中,这是一个Native的函数(代码位于frameworks\base\core\jni\AndroidRuntime.cpp):

    static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
    {
        gCurRuntime->onStarted();
    }

    这个函数会调用当前AndroidRuntime.onStarted函数,而通过前面可以看到,实际创建的是AppRuntime类的实例,且onStarted函数被申明成虚函数,所以由于多态性,实际调用的是AppRuntime.onStarted函数(代码位于frameworks\base\cmds\app_process\app_main.cpp):

    virtual void onStarted()
    {
        ...
        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        ar->callMain(mClassName, mClass, mArgC, mArgV);
        ...
    }

    看到了吗,它会接着调用mClassName变量指定类中的main函数,并且传递参数。但是,这个参数已经不是前面那个完整的命令了,而是截取了在类名(本例中是““com.android.internal.os.WrapperInit”)后面剩下命令字符串作为参数。

    那好,我们接着看WrapperInit.main函数的实现(代码位于frameworks\base\core\java\com\android\internal\os\WrapperInit.java):

    public static void main(String[] args) {
        try {
            int fdNum = Integer.parseInt(args[0], 10);
            int targetSdkVersion = Integer.parseInt(args[1], 10);
            ...
    
            ZygoteInit.preload();
    
            String[] runtimeArgs = new String[args.length - 2];
            System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
            RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs);
        } catch (ZygoteInit.MethodAndArgsCaller caller) {
            caller.run();
        }
    }

    很简单,在命令字符串中接着解析出管道号和目标SDK版本号,然后调用RuntimeInit.wrapperInit,并将SDK版本号和后面剩下的命令传递进去。

    注意,这里还特别调用了ZygoteInit.preload函数,这是因为当前的子进程已经是一个全新的app_process进程了(通过exec执行的),并没有继承Zygote进程的所有资源,所以要重新加载一下。

    接着看RuntimeInit.wrapperInit(代码位于frameworks\base\core\java\com\android\internal\os\RuntimeInit.java):

    public static void wrapperInit(int targetSdkVersion, String[] argv)
                throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
    
        applicationInit(targetSdkVersion, argv);
    }
    
    private static void applicationInit(int targetSdkVersion, String[] argv)
            throws ZygoteInit.MethodAndArgsCaller {
        ...
        final Arguments args;
        try {
            args = new Arguments(argv);
        } catch (IllegalArgumentException ex) {
            ...
        }
    
        invokeStaticMain(args.startClass, args.startArgs);
    }

    RuntimeInit.wrapperInit函数很简单,简单的直接调用RuntimeInit.applicationInit函数。

    而RuntimeInit.applicationInit函数接着解析剩下的参数,从中解析出要加载并运行的类和参数。前面曾经提到过,这个类,无论你启动什么程序,都是“android.app.ActivityThread”。所以,下面就会加载“android.app.ActivityThread”类,执行它的main函数,并将剩下的参数传递进去。

    讲到这里,估计大家还在疑惑,还有一个大问题没有解决,所有程序都加载同样的类,那我们自己的程序到底是在哪里加载进来的呢?

    这个问题说来话长,限于本篇的篇幅,不能做太详细的介绍,只能大致说一下。由于Android系统的特殊性,要启动一个正常的应用程序,需要涉及到一个ActivityManagerService系统服务,还有一个就是Zygote进程,大概经历了以下几步:

    1)ActivityManagerService收到一个Binder事件,让它启动一个指定的Activity。通常,当你在Launcher中点击一个应用程序图标之后,是由Launcher负责将这个事件发送给ActivityManagerService的。

    当然,你也可以通过am命令,向ActivityManagerService发送任何你想要的事件,例如:

    adb shell am start -n <包名>/.<Activity名>

    2)ActivityManagerService查找本地记录,发现并没有任何已运行的程序包含这个Activity,因此通过本地套接字(Local Socket)向Zygote进程发消息,请求它帮忙启动一个新的进程。

    这个请求是同步的,当一个新的子进程被创建出来后,ActivityManagerService会得到这个子进程的进程号,并将这个进程号和随后要在这个进程中启动程序的信息一一对应的记录下来,等待以后查询时用。

    而对于那个子进程来说,它启动起来之后,都会去执行“android.app.ActivityThread”类中的main函数。上面分析的部分,只是说明了从Zygote进程拿到启动新进程的本地套接字消息(ZygoteConnection.runOnce函数),到创建一个新的进程并执行“android.app.ActivityThread”类的main函数(RuntimeInit.wrapperInit)之间,为了达到让你的工具启动程序的目的,所做的一些特殊的处理。

    3)子进程在“android.app.ActivityThread”类的“领导”下,接着会试着附着(Attach)到那个应用程序上,这也是这个子进程被创建出来的真正目的。

    这里所谓的附着,其实就是子进程通过Binder通信,请求ActivityManagerService告知,对应自己这个子进程号的应用程序的信息(包括包名、组件名等)。在前一步中已经说明了,进程号和应用程序信息是一一对应的记录在ActivityManagerService中的。

    当子进程得到了应用程序信息之后,就会用类加载器将其加载进来,并执行。

    通过以上的分析,回答了我一直以来都有的一个疑问,那就是为什么不能直接通过app_process启动一个应用程序,非要绕这么个大圈子呢?答案其实很简单,你如果直接在命令行中敲入app_process命令,即使你把命令都敲对了也没用,因为你绕过了ActivityManagerService,它那并没有注册你这个进程号对应的要启动的程序的信息。而通过前面的代码分析,如果你对应用程序设置了wrapper系统属性的话,ActivityManagerService并没有被绕过,它那确实记录着你子进程号对应的应用程序信息,后面还是能够查找的到,一切的一切只是让你启动的那个子进程自己去切换环境,执行你设定的工具程序,再让它继续启动你程序。

    展开全文
  • 为什么不自己动手做一个自己想要拓展程序呢,于是我就开始了chrome拓展程序开发之旅。首先,我将介绍我开发拓展程序SortYourTabs,然后,简单介绍下如何开发一个拓展程序。 SortYourTabs已经发布到chrome应用...

    背景

    在日常工作中,我想大家应该都有一个烦扰,Chrome浏览器的Tab开得太多了,找到需要使用的Tab太难了,过多的Tab也会导致系统性能下降。为此,我上chrome应用商店寻找可以解决这个问题的拓展程序,尝试了很多拓展程序,没有找到一个满意的拓展程序。

    为什么不自己动手做一个自己想要的拓展程序呢,于是我就开始了chrome拓展程序开发之旅。首先,我将介绍我开发的拓展程序SortYourTabs,然后,简单介绍下如何开发一个拓展程序。

    SortYourTabs已经发布到chrome应用商店,欢迎安装使用。SortYourTabs项目的代码已经上传到GitHub,欢迎下载或贡献

    SortYourTabs

    简介

    SortYourTabs拓展程序的作用正如名称所述,它可以帮助您记录访问Tab的时间,并可以按照最近最少使用算法(LRU)对您的Tabs进行排序,您就可以快速找到您最近使用的Tab,您也可以快速找到最近都没有使用的Tab,然后及时把不需要的Tab关闭,避免Tab累积得越来越多,影响系统性能。

    在使用该拓展程序时,如果有一些经常使用且不能关闭的Tab,您可以将该Tab固定,固定的Tab将不会被重新排序。

    使用介绍

    1. 安装后第一次使用时,请使用快捷键Ctrl(Win)/Command(Mac)+Shift+1进行初始化,拓展程序将开始记录您访问Tab的时间。下次如果想重新初始化记住当前Tab排序时,同样可以执行该命令;

    2. 使用快捷键Ctrl(Win)/Command(Mac)+Shift+Right将Tab按照LRU从左到右排序,最右边的Tab为最近使用的,最左边的除固定Tab是最近最少使用的;

    3. 执行2中快捷键对Tab排序后,如果想恢复最初的排序,则使用快捷键Ctrl(Win)/Command(Mac)+Shift+0。

    如何开发一个拓展程序

    文件结构

    拓展程序的开发需要使用前端技术体系,如JavaScript、CSS、HTML。你可以选择任何一个你喜欢的文本编辑器进行开发,我使用的是vscode。首先,我们需要创建一个文件夹,文件夹名称为你的拓展程序的名称。然后,在该文件夹中创建一个必要的文件manifest.json,可以参考SortYourTabs。manifest.json文件中permissions表示需要申请的权限,commands表示定义的快捷键,scripts表示拓展程序的脚本,这里也是主要的逻辑代码。
    scripts中指定了运行的js脚本,主要的业务逻辑就在这个js脚本里面,因此接下来就是在这里开发你的业务逻辑。接下来你将需要了解chrome提供给开发者的API有哪些。

    API

    chrome开发者社区为我们提供了非常详细的资料,同时也详细介绍了如何开发一个拓展程序,如果有什么疑问,这里无疑是最好的资料。如果你不知道需要使用什么API,你可以通过搜索关键字查询。
    在这里插入图片描述

    Debug

    使用chrome浏览器访问 chrome://extensions 打开拓展程序安装页面,打开页面右上角开发者模式,然后从页面左上角选择加载已解压的拓展程序,选择你的开发文件夹,拓展程序就安装好了。找到你安装的拓展程序,点击背景页(如下图所示),这时就出来了一个console,拓展程序的所有输出和debug都在这里。如果需要在代码中加入断点,可以在代码中需要断点的位置加上一行代码“debugger”。
    在这里插入图片描述

    发布

    使用chrome浏览器点击打开开发者中心页面,首先你需要注册成为chrome应用开发者,并且需要支付5美元的费用。很多人可能并没有visa信用卡,这该怎么办呢,我找到了一个神奇的网站:全球付,在全球付网站上面注册一张MasterCard,最低充值金额是50美元,可以使用国内银行卡进行快捷支付。
    在这里插入图片描述
    在这里插入图片描述
    接下来你就可以开始上传你的拓展程序了,你需要将你的文件夹打包成zip格式的压缩文件,并填写拓展程序的信息。根据你拓展程序使用的权限会花费不一样的时间,如果你的拓展程序不需要申请使用比较敏感的权限,提交审核后几个小时就通过了。这个时候你上应用商店还并不能搜索到你的拓展程序,你可以点击你的拓展程序进入到拓展程序的安装页面。
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 下面我们来探讨如何利用底层Camera类来构建一个照相应用程序,并学习如何利用所提供功能。 Camera类使用 1.首先在配置文件中添加Camera权限   在开启摄像头之前,我们先获得取景器预览头像Surface。S
  • 启动应用程序来自哪里在最近的过去,大多数应用程序会在安装过程中发出警告,如果他们打算将自己添加到...然而,如今,我们注意到一些着名的应用程序在过去发出“启动”警告,现在故意不这样。例如,流行的uTorren...
  • 对于WINDOWS XP操作系统,浏览...自己用C#做的应用程序如何具有这样的功能? 具体步骤如下:1、新建一个图标文件*.ico,把项目的图标指向这个文件。2、在这个ico文件里,可以右键点空白处,新建多个不同分辩率的图...
  • 这是一个非常简单项目,显示了Next.js与TypeScript用法。 自己部署 使用部署示例: 如何使用它? 使用或执行以引导示例: npx create-next-app --example with-typescript with-typescript-app # or yarn ...
  • ------------------------异常统一处理单元 uAppFactory---------------------------应用程序工厂,本工厂生产出来的就是一个一般的应用程序 uAuthAppFactory-----------------------应用程序工厂,本工厂生产出来...
  • 这次做一个比较有意思的实验,我们知道当一个程序如果抛异常了其程序肯定会挂掉,那有木有可能在程序异常退出时能执行一段咱们自己的代码,比如说服务器在异常退出时需要做一些额外的资源清理,像这种场景就正好是...
  • 我们自己的应用程序 3.给服务发送信号的应用程序。 当然其中的 1和3 可以合并在一起,而且 2 我们可以成多应用程序。这样只要是我们自己写的都可以以SYSTEM的权限运行了,比管理员更实用。这里需要注意的是第...
  • 板子是ok6410,搭载2.6.36内核,文件系统是按照老师给文档自己做的,这两天一直在尝试自定义开机启动脚本文件或应用程序,经历多次失败终于在灵光闪间成功了,这方法可能不科学,但是可以实现要求: ...
  • 假如你是名Java开发者,正在开发和维护包含2000类并使用了很多框架的应用程序。你要如何理解这些代码呢?在典型的Java企业项目小组中,大部分能够帮你的高级工程师看起来都很忙,文档也很少。你需要尽快交付成果...
  • 有关如何在实时系统上部署项目注释,请参阅部署。 先决条件 Postgres(OSX用户可以运行brew install postgresql ) 捆绑器( gem install bundler ) Ruby 2.3.1(或此应用当前正在使用任何版本,请检查...
  • 在Windows Mobile上隐藏你的应用程序 有时候,我们需要在Windows Mobile上一些invisible的应用程序,使得用户无法察觉。...大家知道,在Windows Mobile上做自己的应用程序,会在“设置->系统-...
  • 商家都可以通过这种轻型的应用打造属于自己的移动互联网系统平台,并且效率更高,成本更低。连锁店只要开发款微信小程序系统便可以在各个店铺应用,同时还有利于对实体店铺运营效果的监控。 连锁店小程序怎么...
  • 也可以自己做一个字符设备接收用户输入,根据输入,投递 input 事件。还有一种方式就是直接往evnent 里写入数据, 都可以达到控制鼠标键盘功能。---没有键盘设备话,向哪一个event里面写?好...
  • 自己做一个学生信息管理系统,里面有一个功能是修改密码,我想在修改成功之后回到登陆界面重新登录,想用form.showdialog()可是窗体一闪就消失了,问老师说要用到线程去解决,可是我们要下学期才学,哪位大神帮...
  • 应用程序开发技巧

    2020-12-22 17:08:15
    假如你是名Java开发者,正在开发和维护包含2000类并使用了很多框架的应用程序。你要如何理解这些代码呢?在典型的Java企业项目小组中,大部分能够帮你的高级工程师看起来都很忙,文档也很少。你需要尽快交付成果...
  • 当你在写一个函数时,请想到这个函数可能会被同一个应用程序进行无数次调用。那么你想让这个函数行为是什么样呢?通常,当有错误发生时,函数不能自己肆意退出,而应该告诉调用者出现了什么样错误,这样就...
  • 前几天,我就在想一个问题,即扩展BlackBerry系统应用程序的菜单, 我打算分为几个步骤: 1)是否真的可以这样,毕竟BlackBerry是另外一个操作系统 2)即然可以加入,如何加入 3)加入之后,如何实现自己的功能 这个...
  • 建立基本web应用程序打开visual web developer 2005 express,菜单-》新建-》网站如果您打算建一个具有AjaxOnlineJudge话呢,可以建立AJAX Enabled Web Site前提是安装了Asp.net Ajax 1.0。如果您只想建立...
  • 在开发的过程中一直在考虑一个问题,看到自己的手机的内存一天天的在变小,又看到别人的应用好像能选择安装到SD卡中,可我的好像从来都没有这样过,也许自己还很菜吧,为此深表歉意,在网上找了一下相关的内容发现...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 651
精华内容 260
关键字:

如何做一个自己的应用程序系统