当前位置:  编程技术>移动开发
本页文章导读:
    ▪更深层次的懂得Context        更深层次的理解ContextContext在开发Android应用的过程中扮演着非常重要的角色,比如启动一个Activity需要使用context.startActivity方法,将一个xml文件转换为一个View对象也需要使用Context对象,可以.........
    ▪ 唯有一个Receiver时,不能接收到广播        只有一个Receiver时,不能接收到广播最近在做一个项目,主要是跑后台的,界面就是弹几个浮动在窗口之上的对话框与用户交互,在起初时,为了调试方便就建立了一个Activity,在Activity中启.........
    ▪ unity3d学习札记(十五)-利用Loading界面异步过渡游戏场景       unity3d学习笔记(十五)--利用Loading界面异步过渡游戏场景本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。 http://blog.csdn.net/lzhq1982/article/details/12905779 前一篇文章介绍了游.........

[1]更深层次的懂得Context
    来源: 互联网  发布时间: 2014-02-18
更深层次的理解Context

Context在开发Android应用的过程中扮演着非常重要的角色,比如启动一个Activity需要使用context.startActivity方法,将一个xml文件转换为一个View对象也需要使用Context对象,可以这么说,离开了这个类,Android开发寸步难行,对于这样一个类,我们又对他了解多少呢。我就说说我的感受吧,在刚开始学习Android开发时,感觉使用Context的地方一直就是传入一个Activity对象,久而久之感觉只要是Context的地方就传入一个Activity就行了,那么我们现在就来详细的分析一下Context和Activity的关系吧!

在开始本文之前我们先放置一个问题在这里:

我们平时在获取项目资源时使用context.getResources()的时候为什么放回的是同一个值,明明是使用不同的Activity调用getResources返回结果却是一样的。



Context本身是一个纯的abstract类,ContextWrapper是对Context的一个包装而已,它的内部包含了一个Context对象,其实对ContextWrapper的方法调用最终都是调用其中的Context对象完成的,至于ContextThremeWrapper,很明显和Theme有关,所以Activity从ContextThemmWrapper继承,而Service从ContextWrapper继承,ContextImpl是唯一一个真正实现了Context中方法的类。

 

从上面的继承关系来看,每一个Activity就是一个Context,每一个Service就是一个Context,这也就是为什么使用Context的地方可以被Activity或者Service替换了。

 

根据前面所说,由于实现了Context的只有ContextImpl类,Activity和Service本没有真正的实现,他们只是内部包含了一个真实的Context对象而已,也就是在在创建Activity或者Service的时候肯定要创建爱你一个ContextImpl对象,并赋值到Activity中的Context类型变量中。那我们就来看看Andorid源码中有哪些地方创建了ContextImpl.

据统计Android中创建ContextImpl的地方一共有7处:

在PackageInfo.makeApplication()中

在performLaunchActivity()中

在handleCreateBackupAgent()中

在handleCreateService()中

2次在hanldBinderAppplication()中

在attach()方法中



由于创建ContextImpl的基本原理类似,所以这里只会分析几个比较有代表性的地方:

1、  Application对应的Context

在应用程序启动时,都会创建一个Application对象,所以辗转调用到handleBindApplication()方法。

 private final void handleBindApplication(AppBindData data) {
        mBoundApplication = data;
        mConfiguration = new Configuration(data.config);

        ....
        data.info = getPackageInfoNoCheck(data.appInfo);

       		...
        
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;

      ....
	  }

其中data.info是LoadedApk类型的,到getPackageInfoNoCheck中看看源码

public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) {
        return getPackageInfo(ai, null, false, true);
}

里面其实调用的是getPackageInfo,继续跟进:

if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }
            LoadedApk packageInfo = ref != null ? ref.get() : null;
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                        : "Loading resource-only package ") + aInfo.packageName
                        + " (in " + (mBoundApplication != null
                                ? mBoundApplication.processName : null)
                        + ")");
                packageInfo =
                    new LoadedApk(this, aInfo, this, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }

由于includeCode传入的是true,所以首先从mPackages中获取,如果没有,则new一个出来,并放入mPackages里面去,注意,这里的mPackages是ActivityThread中的属性。

下面继续分析一下LoadedApk这个类中的makeApplication函数

try {
            java.lang.ClassLoader cl = getClassLoader();
			//创建一个ContextImpl对象
            ContextImpl appContext = new ContextImpl();
            appContext.init(this, null, mActivityThread);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
这里创建了一个ContextImpl对象,并调用了它的init方法,现在进入init方法。
mPackageInfo = packageInfo;
mResources = mPackageInfo.getResources(mainThread);

对mPackageInof和mResources两个变量初始化

回到makeApplication中,创建了一个Application对象,并将appContext传进去,其实就是将appContext传递给ContextWrapper中的Context类型变量(Application也是继承ContextWrapper)

2、Activity中的Context

在创建一个Activity时,经过辗转调用,会执行handleLaunchActivity(),然后调用performLaunchActivity(),该方法创建ContextImpl代码如下:

r.packageInfo= getPackageInfo(aInfo.applicationInfo,

                   Context.CONTEXT_INCLUDE_CODE);

 

ContextImplappContext = new ContextImpl();

                appContext.init(r.packageInfo,r.token, this);

               appContext.setOuterContext(activity);

 

activity.attach(appContext,this, getInstrumentation(), r.token,

                        r.ident, app, r.intent,r.activityInfo, title, r.parent,

                        r.embeddedID,r.lastNonConfigurationInstance,

                       r.lastNonConfigurationChildInstances, config);

由于getPackageInfo函数之前已经分析过了,稍微有点区别,但是大致流程是差不多的,所以此处的appContext执行init之后,其中的mPackages变量和mResources变量时一样的,activity通过attach函数将该appContext赋值到ContextWrapper中的Context类型变量


3、Service中的Context

同样 在创建一个Service时,经过辗转调用会调用到scheduleCreateService方法,之后会巧用handleCreateService

LoadedApkpackageInfo = getPackageInfoNoCheck(

               data.info.applicationInfo);

 

ContextImplcontext = new ContextImpl();

            context.init(packageInfo, null,this);

 

            Application app =packageInfo.makeApplication(false, mInstrumentation);

            context.setOuterContext(service);

            service.attach(context, this,data.info.name, data.token, app,

                   ActivityManagerNative.getDefault());

其思路和上面两个基本一样,在此就不再详述。

在此总结一下:
(1)Context是一个抽象类,ContextWrapper是对Context的封装,它包含一个Context类型的变量,ContextWrapper的功能函数内部其实都是调用里面的Context类型变量完成的。Application,Service,Activity等都是直接或者间接继承自ContextWrapper,但是并没有真正的实现其中的功能,Application,Service,Activity中关于Context的功能都是通过其内部的Context类型变量完成的,而这个变量的真实对象必定是ContextImpl,所以没创建一个Application,Activity,Servcice便会创建一个ContextImpl,并且这些ContextImpl中的mPackages和mResources变量都是一样的,所以不管使用Acitivty还是Service调用getResources得到相同的结果

(2)在一个apk中,Context的数量等于Activity个数+Service个数+1.



    
[2] 唯有一个Receiver时,不能接收到广播
    来源: 互联网  发布时间: 2014-02-18
只有一个Receiver时,不能接收到广播
最近在做一个项目,主要是跑后台的,界面就是弹几个浮动在窗口之上的对话框与用户交互,在起初时,为了调试方便就建立了一个Activity,在Activity中启动后台Service。后来项目将近结束时,需要捕捉开机以及有网络的广播来开启服务,随之加入Receiver,删除Activity!心想这个是不是很完美,perfect!   但是,但是!无论如何,我的service总是起不来,连Receiver也没有收到广播,这就怪了,为啥呢? 随即写了2个小demo,一个是只有一个Receiver捕捉有网络改变的常驻广播,另外一个是在第一个的基础上增加了一个Activity。试验证明,第一种情况是收不到广播的! 第二种情况是可以收到广播的。所以android程序中,不能只有一个Receiver组件,必须还要Activity。据说这是google对android应用程序安全的考虑,防止流氓软件潜水消耗资源,正所谓禁止潜水也!

    
[3] unity3d学习札记(十五)-利用Loading界面异步过渡游戏场景
    来源: 互联网  发布时间: 2014-02-18
unity3d学习笔记(十五)--利用Loading界面异步过渡游戏场景

本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。

http://blog.csdn.net/lzhq1982/article/details/12905779


前一篇文章介绍了游戏开始场景的制作,可还没有任何交互,按理说,我的设计是点击界面然后直接到游戏场景,但看到了雨松的这篇文章--Unity3D研究院之异步加载游戏场景与异步加载游戏资源进度条后,决定尝试下用Loading界面异步加载游戏。

先看一下我的Loading界面:

像我这种小demo,从开始场景到游戏场景其实用Application.LoadLevel就可以了,但对于一个正常的游戏,新场景的资源加载往往会使你的游戏卡上一段时间,用户体验上就跟游戏死了一样,这时候Loading界面加上后台异步加载场景是你不错的。下面看看我是怎么做的。


1、首先是Loading场景的制作

这个相对简单,如上图,就是用了NGUI的Texture而已,需要注意的就是给它加了个Stretch,使屏幕分辨率自适应,不知道怎么弄的看我前一篇文章,这里不介绍NGUI怎么做的了。


2、异步加载游戏场景

最简单的Application.LoadLevel("SceneName")这种方式加载场景是同步的,如果用这种方法,新场景资源小还好,如果资源量多,那在加载资源时让你的游戏卡死,直到新场景的资源全部加载完成,这种方式对用户体验显然是不好的,最传统的做法都是开个线程,一个线程用来处理数据加载,一个线程用进度条或是动画的方式提示玩家游戏并未卡死,而是等待。但是Unity3d没有多线程的概念,不过unity也给我们提供了StartCoroutine(协同程序)和LoadLevelAsync(异步加载关卡)后台加载场景的方法。

StartCoroutine为什么叫协同程序呢,所谓协同,就是当你在StartCoroutine的函数体里处理一段代码时,利用yield语句等待执行结果,这期间不影响主程序的继续执行,可以协同工作。而LoadLevelAsync则允许你在后台加载新资源和场景,所以再利用协同,你就可以前台用loading条或动画提示玩家游戏未卡死,同时后台协同处理加载的事宜。想具体了解StartCoroutine可以看这里:MonoBehaviour.StartCoroutine 开始协同程序,想具体了解LoadLevelAsync可以看这里:Application.LoadLevelAsync 异步加载关卡。


3、利用单例类记录场景名称

有关单例脚本和单例类我前面的文章介绍过,这里就不说了,为什么要用到单例类呢,因为Loading脚本要知道下一个加载的是什么场景,而这是上一个场景告诉它的,场景切换数据就丢了,而单例类的数据会一直保留,所以要用单例类。看一下代码:

public class Global {
	public string loadName;
	
	private static Global instance;
	public static Global GetInstance()
	{
		if (instance == null)
			instance = new Global();
		
		return instance;
	}
}
这里就一个变量loadName,负责记录场景名称。


4、A场景过渡到Loading场景

我前一篇文章讲了开始游戏场景的制作,但缺切换场景的过渡,假如它是A场景,我用触摸屏幕的方法让它过渡到Loading场景,然后再通过协同程序等待加载资源完成后自动过渡到B场景。先看A场景的代码:

public class OnPress : MonoBehaviour {

	// Use this for initialization
	public UICamera nguiCamera;
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
		if (Input.GetMouseButton(0)) {
			Ray ray = nguiCamera.camera.ScreenPointToRay(Input.mousePosition);
			RaycastHit hit;
			if (Physics.Raycast(ray, out hit)) {
				Global.GetInstance().loadName = "GameScene";
				Application.LoadLevel("LoadingScene");
			}
		}
	}
}
有关触摸的代码我不解释了,重要的就两句,一句“Global.GetInstance().loadName = "GameScene";”,这是告诉Loading场景下一个要加载的场景是GameScene,然后“Application.LoadLevel("LoadingScene");”开始切换场景到LoadingScene。


5、LoadingScene协同程序,后台加载新场景。

先看代码:

public class Loading : MonoBehaviour {
	void Start () {
		StartCoroutine(loadScene());
	}
	
	IEnumerator loadScene()
	{
		AsyncOperation async = Application.LoadLevelAsync(Global.GetInstance().loadName);
		yield return async;
	}
}

这是最简单的协同程序了,只是后台处理加载新场景而已,没有前台的事,这一篇先不讲前台的工作。代码很简单,不解释。因为Global.GetInstance().loadName = "GameScene",所以加载完就自动跳到GameScene这个场景了。


6、要切换场景,有个必要的工作没做。那就是在Unity里注册场景。

选File->Build Settings...,上面有个Scenes In Build,你要把你用到的场景都加到这个框里,程序才能正常调用,否则程序写了也无效。右下角有个Add Current的按钮,这是加载当前场景用的,你也可以直接把所有场景拖到这里。后面的数字是关卡ID,0是第一个要加载的场景。我的截图如下:



这样就可以实现最简单的Loading场景协同异步加载场景了,但本篇的协同异步其实没意义,因为我们的前台没做任何事情,下一篇让我们的前台别闲着,顺便看一下U3D如何利用XML加载游戏Tips。



    
最新技术文章:
▪Android开发之登录验证实例教程
▪Android开发之注册登录方法示例
▪Android获取手机SIM卡运营商信息的方法
▪Android实现将已发送的短信写入短信数据库的...
▪Android开发之注册登录方法示例 iis7站长之家
▪Android根据电话号码获得联系人头像实例代码
▪Android中GPS定位的用法实例
▪Android实现退出时关闭所有Activity的方法
▪Android实现文件的分割和组装
▪Android录音应用实例教程
▪Android双击返回键退出程序的实现方法
▪Android实现侦听电池状态显示、电量及充电动...
▪Android获取当前已连接的wifi信号强度的方法
▪Android实现动态显示或隐藏密码输入框的内容
▪根据USER-AGENT判断手机类型并跳转到相应的app...
▪Android Touch事件分发过程详解
▪Android中实现为TextView添加多个可点击的文本
▪Android程序设计之AIDL实例详解
▪Android显式启动与隐式启动Activity的区别介绍
▪Android按钮单击事件的四种常用写法总结
▪Android消息处理机制Looper和Handler详解
▪Android实现Back功能代码片段总结
▪Android实用的代码片段 常用代码总结
▪Android实现弹出键盘的方法
▪Android中通过view方式获取当前Activity的屏幕截...
▪Android提高之自定义Menu(TabMenu)实现方法
▪Android提高之多方向抽屉实现方法
▪Android提高之MediaPlayer播放网络音频的实现方法...
▪Android提高之MediaPlayer播放网络视频的实现方法...
▪Android提高之手游转电视游戏的模拟操控
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3