精华内容
参与话题
问答
  • APP启动流程解析

    万次阅读 多人点赞 2019-04-22 10:07:15
    启动流程简介 首先要知道的是,手机屏幕其实就是一个Activity,我们专业点将其称为Launcher,相信做过车载设备开发的朋友肯定不会陌生,Launcher是手机厂商提供的,不同的手机厂商比拼的就是Launcher的设计。当然...

    前言

    当我们点击手机屏幕上的软件图标时,就可以打开这个软件,看似很简单的过程其实包含了许多的底层交互,看了还不明白,欢迎来打我。

    一 . 启动流程简介

    首先要知道的是,手机屏幕其实就是一个Activity,我们专业点将其称为Launcher,相信做过车载设备开发的朋友肯定不会陌生,Launcher是手机厂商提供的,不同的手机厂商比拼的就是Launcher的设计。当然我们自己也可以去编写Launcher,运行在手机上使用自己的桌面风格,当然这里我们不去讲如何去编写一个Launcher,如果你感兴趣欢迎关注我。

    写过AndroidDemo的朋友肯定都知道,我们必须在AndroidManifest配置文件中配置默认启动的Activity

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
    
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    其实就是告诉Launcher该启动这个App的哪个页面。这样当系统启动的时候,PackageManger-Service就可以从配置文件中读取到该启动哪个Activity。

    其次要知道的是,Launch和其他APP,运行在不同的进程中,所以他们之间的通信是通过Binder去完成的,所以AMS是必不可少的。下面我们以启动微信为例,看看启动流程是怎样的。

     

                               

                                                 

                                                                 

                                           

                                                                              

                    

     

                                                                    

              

                                                                 

             

     

    简单概括启动微信的流程就是:

    1.Launcher通知AMS 要启动微信了,并且告诉AMS要启动的是哪个页面也就是首页是哪个页面

    2.AMS收到消息告诉Launcher知道了,并且把要启动的页面记下来

    3.Launcher进入Paused状态,告诉AMS,你去找微信吧

    上述就是Launcher和AMS的交互过程

    4.AMS检查微信是否已经启动了也就是是否在后台运行,如果是在后台运行就直接启动,如果不是,AMS会在新的进程中创建一个ActivityThread对象,并启动其中的main函数。

    5.微信启动后告诉AMS,启动好了

    6.AMS通过之前的记录找出微信的首页,告诉微信应该启动哪个页面

    7.微信按照AMS通知的页面去启动就启动成功了。

          

    上述阶段是微信和AMS的交互过程。那么接下来我们分析下具体过程

     

    二  启动流程分析

    1.点击Launcher上的微信图标时,会调用startActivitySafely方法,intent中携带微信的关键信息也就是我们在配置文件中配置的默认启动页信息,其实在微信安装的时候,Launcher已经将启动信息记录下来了,图标只是一个快捷方式的入口。

    startActivitySafely的方法最终还是会调用Activity的startActivity方法

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

    而startActivity方法最终又会回到startActivityForResult方法,这里startActivityForResult的方法中code为-1,表示startActivity并不关心是否启动成功。startActivityForResult部分方法如下所示:

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {

    startActivityForResult方法中又会调用mInstrumentation.execStartActivity方法,我们看到其中有个参数是

    mMainThread.getApplicationThread()

    关于ActivityThread曾在 深入理解Android消息机制https://blog.csdn.net/huangliniqng文章中提到过,ActivityThread是在启动APP的时候创建的,ActivityThread代表应用程序,而我们开发中常用的Application其实是ActivityThread的上下文,在开发中我们经常使用,但在Android系统中其实地位很低的。

                                                             

    Android的main函数就在ActivityThread中

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
    
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
    
        Environment.initForCurrentUser();

    再回到上面方法

    mMainThread.getApplicationThread()
    

    得到的是一个Binder对象,代表Launcher所在的App的进程,mToken实际也是一个Binder对象,代表Launcher所在的Activity通过Instrumentation传给AMS,这样AMS就知道是谁发起的请求。

    2.mInstrumentation.execStartActivity

    instrumentation在测试的时候经常用到,instrumentation的官方文档:http://developer.android.com/intl/zh-cn/reference/android/app/Instrumentation.html这里不对instrumentation进行详细介绍了,我们主要接着看mInstrumentation.execStartActivity方法

    public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread)contextThread;
        if(this.mActivityMonitors != null) {
            Object e = this.mSync;
            synchronized(this.mSync) {
                int N = this.mActivityMonitors.size();
    
                for(int i = 0; i < N; ++i) {
                    Instrumentation.ActivityMonitor am = (Instrumentation.ActivityMonitor)this.mActivityMonitors.get(i);
                    if(am.match(who, (Activity)null, intent)) {
                        ++am.mHits;
                        if(am.isBlocking()) {
                            return requestCode >= 0?am.getResult():null;
                        }
                        break;
                    }
                }
            }
        }
    
        try {
            intent.setAllowFds(false);
            intent.migrateExtraStreamToClipData();
            int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
            checkStartActivityResult(var16, intent);
        } catch (RemoteException var14) {
            ;
        }
    
        return null;
    }

    其实这个类是一个Binder通信类,相当于IPowerManager.java就是实现了IPowerManager.aidl,我们再来看看getDefault这个函数

    public static IActivityManager getDefault() {
        return (IActivityManager)gDefault.get();
    }

    getDefault方法得到一个IActivityManager,它是一个实现了IInterface的接口,里面定义了四大组件的生命周期,

    public static IActivityManager asInterface(IBinder obj) {
        if(obj == null) {
            return null;
        } else {
            IActivityManager in = (IActivityManager)obj.queryLocalInterface("android.app.IActivityManager");
            return (IActivityManager)(in != null?in:new ActivityManagerProxy(obj));
        }
    }

    最终返回一个ActivityManagerProxy对象也就是AMP,AMP就是AMS的代理对象,说到代理其实就是代理模式,关于什么是代理模式以及动态代理和静态代理的使用可以持续关注我,后面会单独写篇文章进行介绍。

                

    AMP的startActivity方法

    public int startActivity(IApplicationThread caller, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken("android.app.IActivityManager");
        data.writeStrongBinder(caller != null?caller.asBinder():null);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        data.writeString(profileFile);
        if(profileFd != null) {
            data.writeInt(1);
            profileFd.writeToParcel(data, 1);
        } else {
            data.writeInt(0);
        }

    主要就是将数据写入AMS进程,等待AMS的返回结果,这个过程是比较枯燥的,因为我们做插件化的时候只能对客户端Hook,而不能对服务端操作,所以我们只能静静的看着。(温馨提示:如果文章到这儿你已经有点头晕了,那就对了,研究源码主要就是梳理整个流程,千万不要纠结源码细节,那样会无法自拔)。

    3.AMS处理Launcher的信息

    AMS告诉Launcher我知道了,那么AMS如何告诉Launcher呢?

    Binder的通信是平等的,谁发消息谁就是客户端,接收的一方就是服务端,前面已经将Launcher所在的进程传过来了,AMS将其保存为一个ActivityRecord对象,这个对象中有一个ApplicationThreadProxy即Binder的代理对象,AMS通ApplicationTreadProxy发送消息,App通过ApplicationThread来接收这个消息。

    Launcher收到消息后,再告诉AMS,好的我知道了,那我走了,ApplicationThread调用ActivityThread的sendMessage方法给Launcher主线程发送一个消息。这个时候AMS去启动一个新的进程,并且创建ActivityThread,指定main函数入口。

    启动新进程的时候为进程创建了ActivityThread对象,这个就是UI线程,进入main函数后,创建一个Looper,也就是mainLooper,并且创建Application,所以说Application只是对开发人员来说重要而已。创建好后告诉AMS微信启动好了,AMS就记录了这个APP的登记信息,以后AMS通过这个ActivityThread向APP发送消息。

    这个时候AMS根据之前的记录告诉微信应该启动哪个Activity,微信就可以启动了。

    public void handleMessage(Message msg) {
        ActivityThread.ActivityClientRecord data;
        switch(msg.what) {
        case 100:
            Trace.traceBegin(64L, "activityStart");
            data = (ActivityThread.ActivityClientRecord)msg.obj;
            data.packageInfo = ActivityThread.this.getPackageInfoNoCheck(data.activityInfo.applicationInfo, data.compatInfo);
            ActivityThread.this.handleLaunchActivity(data, (Intent)null);
            Trace.traceEnd(64L);
    ActivityThread.ActivityClientRecord

    就是AMS传过来的Activity

    data.activityInfo.applicationInfo

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    所得到的属性我们称之为LoadedApk,可以提取到apk中的所有资源,那么APP内部是如何页面跳转的呢,比如我们从ActivityA跳转到ActivityB,我们可以将Activity看作Launcher,唯一不同的就是,在正常情况下ActivityB和ActivityA所在同一进程,所以不会去创建新的进程。

    APP的启动流程就是这样了,欢迎留言探讨,记得持续关注哦。

     

     

     

     

     

    展开全文
  • Android app启动页广告

    热门讨论 2016-12-15 10:26:22
    Android app启动也得广告,时间倒计时,支持跳过。详情见博客:http://blog.csdn.net/u010918416/article/details/52924930
  • APP启动分析 Cold start At the beginning of a cold start, the system has three tasks. These tasks are: 1、Loading and launching the app. 2、Displaying a blank starting window for the app immediately ...

    APP启动分析

    Cold start

    At the beginning of a cold start, the system has three tasks. These tasks are:

    1、Loading and launching the app.
    2、Displaying a blank starting window for the app immediately after launch
    3、.Creating the app process.

    As soon as the system creates the app process, the app process is responsible for the next stages:

    1、Creating the app object.
    2、Launching the main thread.
    3、Creating the main activity.
    4、Inflating views.
    5、Laying out the screen.
    6、Performing the initial draw.

    系统和应用之间交互简图

    在这里插入图片描述

    Application creation

    When your application launches, the blank starting window remains on the screen until the system finishes drawing the app for the first time. At that point, the system process swaps out the starting window for your app, allowing the user to start interacting with the app.
    If you’ve overloaded Application.onCreate() in your own app, the system invokes the onCreate() method on your app object. Afterwards, the app spawns the main thread, also known as the UI thread, and tasks it with creating your main activity.

    From this point, system- and app-level processes proceed in accordance with the app lifecycle stages.

    Activity creation

    After the app process creates your activity, the activity performs the following operations:

    1、Initializes values.
    2、Calls constructors.
    3、Calls the callback method, such as Activity.onCreate(), appropriate to the current lifecycle state of the activity.

    Typically, the onCreate() method has the greatest impact on load time, because it performs the work with the highest overhead: loading and inflating views, and initializing the objects needed for the activity to run.

    Hot start

    A hot start of your application is much simpler and lower-overhead than a cold start. In a hot start, all the system does is bring your activity to the foreground. If all of your application’s activities are still resident in memory, then the app can avoid having to repeat object initialization, layout inflation, and rendering.However, if some memory has been purged in response to memory trimming events, such as onTrimMemory(), then those objects will need to be recreated in response to the hot start event.
    A hot start displays the same on-screen behavior as a cold start scenario: The system process displays a blank screen until the app has finished rendering the activity.

    Warm start

    A warm start encompasses some subset of the operations that take place during a cold start; at the same time, it represents more overhead than a hot start. There are many potential states that could be considered warm starts.
    For instance:

    • The user backs out of your app, but then re-launches it. The process may have continued to run, but the app must recreate the activity from scratch via a call to onCreate().
    • The system evicts your app from memory, and then the user re-launches it. The process and the activity need to be restarted, but the task can benefit somewhat from the saved instance state bundle passed into onCreate().

    译文

    冷启动

    简单讲app冷启动可以分为两个阶段
    第一阶段

    1、加载并启动app
    2、启动后立即显示一个空白的启动窗口
    3、创建app进程

    第二阶段

    1、创建app对象
    2、启动主进程
    3、创建MainActivity
    4、渲染视图
    5、执行onLayout
    6、执行onDraw

    应用创建
    当应用程序启动时,空白的开始窗口将保留在屏幕上,直到系统第一次绘制完应用程序。此时,系统进程会为应用程序交换启动窗口,允许用户开始与应用程序交互

    之后按照app生命周期依次执行

    简单说就是当屏幕上的组件渲染结束后,用app窗口替换系统空白窗口,此时允许用户与app进行交互

    Activity创建

    1、初始化values
    2、初始化构造方法
    3、执行生命周期回调

    热启动

    热启动时,系统将activity放到前台。如果应用程序的所有activity存在内存中,则应用程序可以避免重复对象初始化、渲染、绘制操作。

    如果由于内存不足导致对象被回收,则需要在热启动时重建对象,此时与冷启动时将界面显示到手机屏幕上一样。

    温启动

    温启动时由于app的进程仍然存在,只执行冷启动第二阶段流程

    1、创建app对象
    2、启动主进程
    3、创建MainActivity
    4、渲染视图
    5、执行onLayout
    6、执行onDraw

    温启动常见场景:

    1、用户双击返回键退出应用
    2、app由于内存不足被回收

    展开全文
  • 安卓 APP 启动优化属于安卓性能优化重要部分之一,面试中问到的频率也比较高,对于移动开发者而言,做出绝佳性能的 APP 一直是我们渴求的目标。然而只有在了解 其背后的原理后,才能写出更高质量的代码,才能...

    安卓 APP 启动优化属于安卓性能优化重要部分之一,面试中问到的频率也比较高,对于移动开发者而言,做出绝佳性能的 APP 一直是我们渴求的目标。然而只有在了解 其背后的原理后,才能写出更高质量的代码,才能举一反三,轻松的应对日常开发中遇到的各种疑难杂症。

    本篇 Chat 将给大家带来如下内容:

    1. 启动原理解析;
    2. 点击 APP 图标时安卓系统做了那些处理;
    3. 启动类型,冷启动和热启动的区别;
    4. 启动可以优化的项,对启动过程的分析;
    5. 启动白屏原因分析;
    6. 启动时间的计算;
    7. 冷启动优化;
    8. 优化启动时间的一个很好的工具 Traceview 的使用。

    阅读全文: http://gitbook.cn/gitchat/activity/5b64287083a10103de853f7d

    您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

    FtooAtPSkEJwnW-9xkCLqSTRpBKX

    展开全文
  • iOS App启动广告

    千次阅读 2016-09-12 14:12:47
    App应用程序每次启动时都要展示广告, 因LaunchScreen的本质是屏幕截屏,所以苹果不允许修改LaunchScreen对应的自定义类Custom Class, 所以通过LaunchScreen在启动时实现启动广告行不通。所以只能启动完成后实现,...

    一般情况下,App在每次启动时都会有广告,那么是自己自定义UIViewController来实现还是直接使用LaunchScreen.storyboard来实现呢?


    因LaunchScreen的本质是屏幕截屏,所以苹果不允许修改LaunchScreen对应的自定义类Custom Class, 因在启动广告时还有倒数3秒钟跳过的功能,还有点击广告会打开safari浏览器的功能,不能绑定自定义的类就不能处理这些业务逻辑, 所以通过LaunchScreen在启动时实现启动广告是行不通的。所以只能通过自定义UIViewController来实现。
    此时有两种方案:
    ①:程序一启动就进入广告界面,将窗口的根控制器设置为广告控制器(采用该方式,该方式简单)
    ②:窗口的根控制器仍为TabBarController,在窗口上加一个广告界面挡住tabBar界面, 3秒钟后移除广告界面(该方式稍微复杂)

    引导页只有在应用程序第一次打开时才会出现,而广告页一般是应用程序每次启动都会打开,我们可以这样应用程序第一次启动时将rootViewController = 引导页,不是第一次启动根视图控制器设置为广告页。

    在应用程序启动完成后,进入广告界面,广告界面由UIImageView + UIView + UIButton组成,UIImageView用于存放启动图片,UIView作为容器视图以便将来添加广告图片,UIButton用于跳过广告。UIImageView在最底层,UIView容器视图将在UIImageView的正上方,完全遮挡住UIImageView,UIButton是UIView的子视图。为什么还要显示启动图片,进入广告之后,直接加载广告图片不就行了吗?原因有二:一是在请求网络图片之前广告界面是空的或者网络比较慢时也是一篇空白,不太友好,第二如果图片是启动图片,因启动时的图片,和广告的背景图片是同一张图片,这样可以给用户造成一种假象:用户会认为程序还在启动,还没结束。因为启动图片一直没有消失,其实已经消失过了,又加载了一张启动图片。

    当在Assets.scassets中看到图片正下方出现Unassigned,说明图片不能被识别,这时候就不要把图片放到Assets中了,直接放到项目中的某个文件夹下如Resource–>Image文件夹或Supporting Files中即可。Assets的目的是让图片更加安全,防止别人盗取图片。

    这里写图片描述

    最终代码

    步骤一:因界面比较简单适合使用XIB搭建广告界面

    这里写图片描述

    步骤二:应用程序启动时将窗体的根视图控制设置为广告

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // 1. 创建窗口--------------------------------------------------
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
        // 2. 设置窗口的根试图控制器--------------------------------------
        BWAdViewController *adViewController = [[BWAdViewController alloc] init];
        self.window.rootViewController = adViewController;
    
        // 3. 显示窗口--------------------------------------------------
        [self.window makeKeyAndVisible];
    
        return YES;
    }

    步骤三:加载广告图片,处理逻辑

    模型由广告的图片、点击广告图片所对应的地址,以及广告图片对应的宽度和高度
    Ad广告model

    #import <Foundation/Foundation.h>
    
    @interface BWAd : NSObject
    
    /** 广告地址 */
    @property (strong, nonatomic) NSString *w_picurl;
    
    /** 广告跳转页面 */
    @property (strong, nonatomic) NSString *ori_curl;
    
    /** 图片宽高 */
    @property (assign, nonatomic) CGFloat w;
    @property (assign, nonatomic) CGFloat h;
    
    @end
    

    BWAdViewController

    #import "BWAdViewController.h"
    #import "BWAd.h"
    #import "BWTabBarController.h"
    
    #import <AFNetworking.h>
    #import <MJExtension.h>
    #import <UIImageView+WebCache.h>
    
    #define code2 @"phcqnauGuHYkFMRquANhmgN_IauBThfqmgKsUARhIWdGULPxnz3vndtkQW08nau_I1Y1P1Rhmhwz5Hb8nBuL5HDknWRhTA_qmvqVQhGGUhI_py4MQhF1TvChmgKY5H6hmyPW5RFRHzuET1dGULnhuAN85HchUy7s5HDhIywGujY3P1n3mWb1PvDLnvF-Pyf4mHR4nyRvmWPBmhwBPjcLPyfsPHT3uWm4FMPLpHYkFh7sTA-b5yRzPj6sPvRdFhPdTWYsFMKzuykEmyfqnauGuAu95Rnsnbfknbm1QHnkwW6VPjujnBdKfWD1QHnsnbRsnHwKfYwAwiu9mLfqHbD_H70hTv6qnHn1PauVmynqnjclnj0lnj0lnj0lnj0lnj0hThYqniuVujYkFhkC5HRvnB3dFh7spyfqnW0srj64nBu9TjYsFMub5HDhTZFEujdzTLK_mgPCFMP85Rnsnbfknbm1QHnkwW6VPjujnBdKfWD1QHnsnbRsnHwKfYwAwiuBnHfdnjD4rjnvPWYkFh7sTZu-TWY1QW68nBuWUHYdnHchIAYqPHDzFhqsmyPGIZbqniuYThuYTjd1uAVxnz3vnzu9IjYzFh6qP1RsFMws5y-fpAq8uHT_nBuYmycqnau1IjYkPjRsnHb3n1mvnHDkQWD4niuVmybqniu1uy3qwD-HQDFKHakHHNn_HR7fQ7uDQ7PcHzkHiR3_RYqNQD7jfzkPiRn_wdKHQDP5HikPfRb_fNc_NbwPQDdRHzkDiNchTvwW5HnvPj0zQWndnHRvnBsdPWb4ri3kPW0kPHmhmLnqPH6LP1ndm1-WPyDvnHKBrAw9nju9PHIhmH9WmH6zrjRhTv7_5iu85HDhTvd15HDhTLTqP1RsFh4ETjYYPW0sPzuVuyYqn1mYnjc8nWbvrjTdQjRvrHb4QWDvnjDdPBuk5yRzPj6sPvRdgvPsTBu_my4bTvP9TARqnam"
    
    @interface BWAdViewController ()
    @property (weak, nonatomic) IBOutlet UIImageView *launchImageView;
    @property (weak, nonatomic) IBOutlet UIView *adContainView;
    @property (weak, nonatomic) IBOutlet UIButton *jumpButton;
    @property (weak, nonatomic) UIImageView *adImageView;
    
    @property (strong, nonatomic) BWAd *ad;
    @property (weak, nonatomic) NSTimer *timer;
    @end
    
    @implementation BWAdViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // 设置最底层的图片为启动图片,让用户认为程序还在启动的假象,进行屏幕适配
        NSString *launchImage = nil;
        if (isIphone6p) {
            launchImage = @"LaunchImage-800-Portrait-736h@3x";
        } else if (isIphone6) {
            launchImage = @"LaunchImage-800-667h";
        } else if (isIphone5) {
            launchImage = @"LaunchImage-568h";
        } else if (isIphone4) {
            launchImage = @"LaunchImage-700";
        }
        self.launchImageView.image = [UIImage imageNamed:launchImage];
    
        // 加载广告数据
        [self loadAdData];
    }
    
    - (void)loadAdData {
        AFHTTPSessionManager *manger = [AFHTTPSessionManager manager];
        NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
        parameters[@"code2"] = code2;
        [manger GET:@"http://mobads.baidu.com/cpro/ui/mads.php" parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary *  _Nullable responseObject) {
            NSDictionary *adDict = [responseObject[@"ad"] lastObject];
            _ad = [BWAd mj_objectWithKeyValues:adDict];
            CGFloat h = ScreenWidth / _ad.w * _ad.h; // 按比例拉伸
            // 懒加载并设置广告的frame
            self.adImageView.frame = CGRectMake(0, 0, ScreenWidth, h);
            [self.adImageView sd_setImageWithURL:[NSURL URLWithString:_ad.w_picurl]];
    
            // 启动定时器,定时倒数数秒跳过广告
            _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeChange) userInfo:nil repeats:YES];
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            BWLog(@"error:%@", error);
        }];
    }
    
    // 定时改变标题,数到0时进入主框架并关闭定时器
    - (void)timeChange {
        static int i = 3;
        [_jumpButton setTitle:[NSString stringWithFormat:@"(%d秒)跳转", i] forState:UIControlStateNormal];
        if (i == 0) {
            // 销毁广告界面, 进入主框架界面
            [self jumpAction:nil];
        }
        i--;
    }
    
    - (UIImageView *)adImageView {
        if (_adImageView == nil) {
            UIImageView *adImageView = [[UIImageView alloc] init];
            [self.adContainView insertSubview:adImageView atIndex:0];
    
            adImageView.userInteractionEnabled = YES;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(adDetail)];
            [adImageView addGestureRecognizer:tap];
    
            _adImageView = adImageView;
        }
    
        return _adImageView;
    
    }
    
    
    - (IBAction)jumpAction:(id)sender {
        [UIApplication sharedApplication].keyWindow.rootViewController = [[BWTabBarController alloc] init];
    
        // 销毁定时器
        [_timer invalidate];
    
    }
    
    - (void)adDetail {
        NSURL *url = [NSURL URLWithString:_ad.ori_curl];
        UIApplication *app = [UIApplication sharedApplication];
        if ([app canOpenURL:url]) {
            [app openURL:url];
        }
    }
    @end
    

    实现效果:
    这里写图片描述

    [UIApplication sharedApplication].keyWindow == [[[UIApplication sharedApplication] delegate] window];

    展开全文
  • Android 一个app启动另一个app

    千次阅读 2015-05-21 19:49:18
    [支付宝钱包],可以从支付宝直接跳转到[去啊],如果没有按照还提醒用户是否安装,有点炫酷哦,去啊的用户量一下增多了 第一个App中 // 通过包名获取要跳转的app,创建intent对象
  • 鉴于app启动速度慢的问题,接到了优化速度的任务。 无非就是异步初始化,延迟初始化,懒加载三种,我先对application中的初始化代码块做异步初始化 优化后加快了300ms,使用线程池,大概代码如下 ``` class ...
  • 学习App启动流程的时候,看到这个图。提出两个问题: 1.是否每一个启动APP都有各独立的一套 ActivityManage,PackageManager,PowerManger等等这些服务? 2.zygote进程有一个监听启动APP的socket。 当有收到...
  • AndroidApp启动优化篇

    千次阅读 2016-01-27 12:21:11
    既然是潮流,那我们也不能落下,紧跟潮流,做个这样的App启动效果,结果发现,不尽人意,每次都会先卡顿一下,然后出现黑屏或白屏的一闪而过,而当我们打开微信这样的App,却不会有这样的操蛋行为。为啥呢?百思不得...
  • 0. 前言 从Android手机打开开关,到我们可以使用其中的app时,这个启动过程到底是怎么样的? 1. 系统上电当给Android系统上电,在电源接通的瞬间,CPU内的寄存器和各引脚均会被置为初始状态,CPU复位之后,程序...
  • Android性能优化系列之App启动优化

    万次阅读 2017-02-22 00:23:29
    Android性能优化系列之布局优化Android性能优化系列之内存优化Android性能优化系列之apk瘦身应用的启动速度缓慢是我们在开发过程中经常会遇到的问题,比如启动缓慢导致的...,本篇博客就将介绍App启动优化的相关知识...
  • APP启动过程

    千次阅读 2016-08-28 00:54:16
    【原文地址 APP启动过程】 It's right time to learn Android's Framework ! 前言 一个App是怎么启动起来的?App的程序入口到底是哪里?Launcher到底是什么神奇的东西?听说还有个AMS的东西,它是做什么的?...
  • android获取app启动时间

    2016-01-04 05:09:03
    有谁知道通过java代码得到app启动的时间的么。或者用adb shell am start -W packageName/activity;这种方式怎么获取到结果。
  • 启动Activity3.1 启动进程3.2 启动主线程3.3 创建Application3.4 创建Activity参考 1. 前言 Activity是日常开发中最常用的组件,系统给我们做了很多很多的封装,让我们平时用起来特别简单,很顺畅.但是你有没有想过,...
  • Android中APP启动方式有两种状态,主要分为冷启动和热启动。 冷启动 启动APP时,应用的进程不在后台,系统会创建一个新的进程供APP使用。 冷启动在创建新的进程时,先会创建和初始化Application对象,再创建和...
  • APP启动页的正确配置方式

    千次阅读 2016-03-01 16:54:15
    APP启动页面(Splash Screen)好多都是等待3秒,好一点的还可以跳过,但是有的跳过也是假的按钮。当然像一些大厂的APP,像网易新闻等启动页面都是广告,人家要收广告费的。但是,对于一些普通的APP,有的也出现...
  • 关于App启动加载广告页面思路

    千次阅读 2016-07-01 10:51:23
    很多app(如淘宝、美团等)在启动图加载完毕后,还会显示几秒的广告,一般都有个跳过按钮可以跳过这个广告,有的app在点击广告页之后还会进入一个广告页面,点击返回进入首页。虽然说这个广告页面对用户体验来说并不是...
  • Android启动流程、app启动原理

    千次阅读 2019-03-06 20:04:04
    Android 众多基于Linux内核的系统类似, 启动系统时, bootloader启动内核和init进程. init进程分裂出更多名为"daemons(守护进程)"的底层的Linux进程, 诸如android debug deamon, USB deamon等. 这些守护...
  • 目前市面上好多机顶盒比如创维,爱奇艺,先科机顶盒,夏新机顶盒,英菲克机顶盒等等, 都设置了开机不启动第三方应用, 开机自启动设置方法: ...特别app安装在sd卡的情况下有些os是抓取不到RECEIVE_BOOT_COMPLET...
  • 前言:项目中被诟病多媒体开机第一次启动很慢,大约3s的黑屏,后续点开启动正常,其中什么原理呢?这就涉及到系统的三种启动模型 ...loding and launching the app displaying a blank window creat...
  • AMS(ActivityManagerService)的启动是在SystemServer进程中启动的,它的职责是用于和所有APP的四大组件进行通信,Activity的启动过程就是APP端与AMS端进行通信,首先理解的一点是APP端与AMS是在两个不同的进程中,...
  • android 外部html页面启动本地app及app启动问题
  • Hbuilder app开发之app启动图片

    千次阅读 2017-10-13 16:12:41
    hbuilder app开发之app启动图片: http://jingyan.baidu.com/article/19020a0a163e73529d284200.html 注意:也可以打包的时候生成下图的文件后再设置
  • Android app 启动优化

    千次阅读 2017-03-12 01:55:49
    app启动优化 白屏 黑屏 解决办法
  • APP启动时间优化

    千次阅读 2019-10-23 17:35:17
    一般而言,大家把iOS冷启动的过程定义为:从用户点击App图标开始到appDelegate didFinishLaunching方法执行完成为止。这个过程主要分为两个阶段: T1:main()函数之前,即操作系统加载App可执行文件到内存,然后...
  • 经常有人提问:如何知道我的app是被谁启动的? 首先很遗憾的告诉你,如果对方app不做任何改动的话是不可能获取到的,因为没有任何标识能区分是从哪个app跳转过来的。 如果你有能力让第三方和你对接或只需要笼统的...
  • Android - 简单的查看APP启动时间

    千次阅读 2017-05-03 10:41:10
    先看看 测量的时机,下图很清晰的说明了APP启动测量时间:具体见 : https://developer.android.com/topic/performance/launch-time.html Displayed Time reportFullyDrawn() 1. Displayed Time 启动加载代码、初始...
  • Android系统应用层App启动过程分析 Android启动App: 1、 先有Lancher启动ActivityThread,ActivityThread负责onPause、onStart等调度所有MainActivity; 2、 ActivityThread为App分配栈内存,使用:...
  • Android AnimationDrawable动画与APP启动、加载引导页面(画面) AnimationDrawable是Android的Frame动画,可以简单的认为此AnimationDrawable能够将一系列资源图片加载成“电影”一样播放。当下,在一些APP中,...
  • 提高app启动速度+冷热启动

    千次阅读 2016-07-21 10:20:13
    一、应用的启动 启动方式 通常来说,在安卓中应用的启动方式分为两种:冷启动和热启动。 1、冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是...

空空如也

1 2 3 4 5 ... 20
收藏数 36,286
精华内容 14,514
关键字:

app启动